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
30 changes: 30 additions & 0 deletions sponsors/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,11 @@ def get_urls(self):
self.admin_site.admin_view(self.approve_sponsorship_view),
name="sponsors_sponsorship_approve",
),
path(
"<int:pk>/enable-edit",
self.admin_site.admin_view(self.rollback_to_editing_view),
name="sponsors_sponsorship_rollback_to_edit",
),
]
return my_urls + urls

Expand Down Expand Up @@ -322,6 +327,31 @@ def get_sponsor_contacts(self, obj):

get_sponsor_contacts.short_description = "Contacts"

def rollback_to_editing_view(self, request, pk):
sponsorship = get_object_or_404(self.get_queryset(request), pk=pk)

if request.method.upper() == "POST" and request.POST.get("confirm") == "yes":
try:
sponsorship.rollback_to_editing()
sponsorship.save()
self.message_user(
request, "Sponsorship is now editable!", messages.SUCCESS
)
except SponsorshipInvalidStatusException as e:
self.message_user(request, str(e), messages.ERROR)

redirect_url = reverse(
"admin:sponsors_sponsorship_change", args=[sponsorship.pk]
)
return redirect(redirect_url)

context = {"sponsorship": sponsorship}
return render(
request,
"sponsors/admin/rollback_sponsorship_to_editing.html",
context=context,
)

def reject_sponsorship_view(self, request, pk):
sponsorship = get_object_or_404(self.get_queryset(request), pk=pk)

Expand Down
9 changes: 9 additions & 0 deletions sponsors/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,15 @@ def approve(self):
self.status = self.APPROVED
self.approved_on = timezone.now().date()

def rollback_to_editing(self):
accepts_rollback = [self.APPLIED, self.APPROVED, self.REJECTED]
if self.status not in accepts_rollback:
msg = f"Can't rollback to edit a {self.get_status_display()} sponsorship."
raise SponsorshipInvalidStatusException(msg)
self.status = self.APPLIED
self.approved_on = None
self.rejected_on = None

@property
def verified_emails(self):
emails = [self.submited_by.email]
Expand Down
29 changes: 28 additions & 1 deletion sponsors/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
from django.utils import timezone

from ..models import Sponsor, SponsorshipBenefit, Sponsorship
from ..exceptions import SponsorWithExistingApplicationException
from ..exceptions import (
SponsorWithExistingApplicationException,
SponsorshipInvalidStatusException,
)


class SponsorshipBenefitModelTests(TestCase):
Expand Down Expand Up @@ -142,6 +145,30 @@ def test_approve_sponsorship(self):
self.assertEqual(sponsorship.status, Sponsorship.APPROVED)
self.assertEqual(sponsorship.approved_on, timezone.now().date())

def test_rollback_sponsorship_to_edit(self):
sponsorship = Sponsorship.new(self.sponsor, self.benefits)
can_rollback_from = [
Sponsorship.APPLIED,
Sponsorship.APPROVED,
Sponsorship.REJECTED,
]
for status in can_rollback_from:
sponsorship.status = status
sponsorship.save()
sponsorship.refresh_from_db()

sponsorship.rollback_to_editing()

self.assertEqual(sponsorship.status, Sponsorship.APPLIED)
self.assertIsNone(sponsorship.approved_on)
self.assertIsNone(sponsorship.rejected_on)

sponsorship.status = Sponsorship.FINALIZED
sponsorship.save()
sponsorship.refresh_from_db()
with self.assertRaises(SponsorshipInvalidStatusException):
sponsorship.rollback_to_editing()

def test_raise_exception_when_trying_to_create_sponsorship_for_same_sponsor(self):
sponsorship = Sponsorship.new(self.sponsor, self.benefits)
finalized_status = [Sponsorship.REJECTED, Sponsorship.FINALIZED]
Expand Down
102 changes: 102 additions & 0 deletions sponsors/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,108 @@ def test_redirect_user_back_to_benefits_selection_if_post_without_valid_set_of_b
self.assertRedirects(r, reverse("select_sponsorship_application_benefits"))


class RollbackSponsorshipToEditingAdminViewTests(TestCase):
def setUp(self):
self.user = baker.make(
settings.AUTH_USER_MODEL, is_staff=True, is_superuser=True
)
self.client.force_login(self.user)
self.sponsorship = baker.make(
Sponsorship,
status=Sponsorship.APPROVED,
submited_by=self.user,
_fill_optional=True,
)
self.url = reverse(
"admin:sponsors_sponsorship_rollback_to_edit", args=[self.sponsorship.pk]
)

def test_display_confirmation_form_on_get(self):
response = self.client.get(self.url)
context = response.context
self.sponsorship.refresh_from_db()

self.assertTemplateUsed(
response, "sponsors/admin/rollback_sponsorship_to_editing.html"
)
self.assertEqual(context["sponsorship"], self.sponsorship)
self.assertNotEqual(
self.sponsorship.status, Sponsorship.APPLIED
) # did not update

def test_rollback_sponsorship_to_applied_on_post(self):
data = {"confirm": "yes"}
response = self.client.post(self.url, data=data)
self.sponsorship.refresh_from_db()

expected_url = reverse(
"admin:sponsors_sponsorship_change", args=[self.sponsorship.pk]
)
self.assertRedirects(response, expected_url, fetch_redirect_response=True)
self.assertEqual(self.sponsorship.status, Sponsorship.APPLIED)
msg = list(get_messages(response.wsgi_request))[0]
assertMessage(msg, "Sponsorship is now editable!", messages.SUCCESS)

def test_do_not_rollback_if_invalid_post(self):
response = self.client.post(self.url, data={})
self.sponsorship.refresh_from_db()
self.assertTemplateUsed(
response, "sponsors/admin/rollback_sponsorship_to_editing.html"
)
self.assertNotEqual(
self.sponsorship.status, Sponsorship.APPLIED
) # did not update

response = self.client.post(self.url, data={"confirm": "invalid"})
self.sponsorship.refresh_from_db()
self.assertTemplateUsed(
response, "sponsors/admin/rollback_sponsorship_to_editing.html"
)
self.assertNotEqual(self.sponsorship.status, Sponsorship.APPLIED)

def test_404_if_sponsorship_does_not_exist(self):
self.sponsorship.delete()
response = self.client.get(self.url)
self.assertEqual(response.status_code, 404)

def test_login_required(self):
login_url = reverse("admin:login")
redirect_url = f"{login_url}?next={self.url}"
self.client.logout()

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

self.assertRedirects(r, redirect_url)

def test_staff_required(self):
login_url = reverse("admin:login")
redirect_url = f"{login_url}?next={self.url}"
self.user.is_staff = False
self.user.save()
self.client.force_login(self.user)

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

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

def test_message_user_if_rejecting_invalid_sponsorship(self):
self.sponsorship.status = Sponsorship.FINALIZED
self.sponsorship.save()
data = {"confirm": "yes"}
response = self.client.post(self.url, data=data)
self.sponsorship.refresh_from_db()

expected_url = reverse(
"admin:sponsors_sponsorship_change", args=[self.sponsorship.pk]
)
self.assertRedirects(response, expected_url, fetch_redirect_response=True)
self.assertEqual(self.sponsorship.status, Sponsorship.FINALIZED)
msg = list(get_messages(response.wsgi_request))[0]
assertMessage(
msg, "Can't rollback to edit a Finalized sponsorship.", messages.ERROR
)


class RejectedSponsorshipAdminViewTests(TestCase):
def setUp(self):
self.user = baker.make(
Expand Down
35 changes: 35 additions & 0 deletions templates/sponsors/admin/rollback_sponsorship_to_editing.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{% extends 'admin/base_site.html' %}
{% load i18n admin_static sponsors %}

{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}">{% endblock %}

{% block title %}Rollback {{ sponsorship }} to editing| python.org{% endblock %}

{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a> &gt
<a href="{% url 'admin:app_list' app_label='sponsors' %}">{% trans 'Sponsors' %}</a> &gt
<a href="{% url 'admin:sponsors_sponsorship_changelist' %}">{% trans 'Sponsorship' %}</a> &gt
<a href="{% url 'admin:sponsors_sponsorship_change' sponsorship.pk %}">{{ sponsorship }}</a> &gt
{% trans 'Rollback to editing' %}
</div>
{% endblock %}

{% block content %}
<h1>Rollback to Editing</h1>
<p>Please review the sponsorship application and click in the Rollback button if you want to proceed.</p>
<div id="content-main">
<form action="" method="post" id="new_psf_board_meeting_form">
{% csrf_token %}

<pre>{% full_sponsorship sponsorship display_fee=True %}</pre>

<input name="confirm" value="yes" style="display:none">

<div class="submit-row">
<input type="submit" value="Rollback" class="default">
</div>

</form>
<div>
</div>{% endblock %}
6 changes: 6 additions & 0 deletions templates/sponsors/admin/sponsorship_change_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
</li>
{% endif %}

{% if sp.status != sp.FINALIZED and sp.status != sp.APPLIED %}
<li>
<a href="{% url 'admin:sponsors_sponsorship_rollback_to_edit' sp.pk %}">Rollback to Edit</a>
</li>
{% endif %}

{% endwith %}

{{ block.super }}
Expand Down