SciPost Code Repository

Skip to content
Snippets Groups Projects
Commit 43dd4e09 authored by George Katsikas's avatar George Katsikas :goat:
Browse files

add fellowship creataion using invitation response

fix htmx responsivity of nomination invitations
parent 20263390
No related branches found
No related tags found
1 merge request!58[Fellowship Nominations] Rework the fellowship nomination system and UI
...@@ -11,6 +11,7 @@ from crispy_forms.helper import FormHelper ...@@ -11,6 +11,7 @@ from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Div, Field, ButtonHolder, Submit from crispy_forms.layout import Layout, Div, Field, ButtonHolder, Submit
from crispy_bootstrap5.bootstrap5 import FloatingField from crispy_bootstrap5.bootstrap5 import FloatingField
from dal import autocomplete from dal import autocomplete
from django.utils import timezone
from ontology.models import Specialty from ontology.models import Specialty
from proceedings.models import Proceedings from proceedings.models import Proceedings
...@@ -593,20 +594,63 @@ class FellowshipInvitationResponseForm(forms.ModelForm): ...@@ -593,20 +594,63 @@ class FellowshipInvitationResponseForm(forms.ModelForm):
self.helper.layout = Layout( self.helper.layout = Layout(
Field("nomination", type="hidden"), Field("nomination", type="hidden"),
Div( Div(
Div(Field("response"), css_class="col-lg-5"), Div(
Div(Field("postpone_start_to"), css_class="col-lg-5"), Div(
css_class="row", Div(Field("response"), css_class="col-12"),
), Div(Field("postpone_start_to"), css_class="col-12"),
Div( css_class="row mb-0",
),
css_class="col-12 col-md-5",
),
Div( Div(
Field( Field(
"comments", "comments",
placeholder="Add a comment (visible to EdAdmin)", placeholder="Add a comment (visible to EdAdmin)",
rows=2, rows=4,
), ),
css_class="col-lg-10", css_class="col-12 col-md-7",
), ),
Div(ButtonHolder(Submit("submit", "Submit")), css_class="col-lg-2"), Div(ButtonHolder(Submit("submit", "Update")), css_class="col-auto"),
css_class="row mt-0", css_class="row mb-0",
), ),
) )
def clean(self):
has_contributor = hasattr(
self.cleaned_data["nomination"].profile, "contributor"
)
invitation_accepted = self.cleaned_data["response"] == (
FellowshipInvitation.RESPONSE_ACCEPTED
)
invitation_postponed = self.cleaned_data["response"] == (
FellowshipInvitation.RESPONSE_POSTPONED
)
postponed_date = self.cleaned_data["postpone_start_to"]
if (invitation_accepted or invitation_postponed) and not has_contributor:
self.add_error(
"response",
"This profile does not have a Contributor account to create a Fellowship with. Please create one before updating the invitation response to a positive answer.",
)
if postponed_date and (timezone.now().date() > postponed_date):
self.add_error(
"postpone_start_to",
"You cannot set a postponed start date in the past.",
)
if (
invitation_accepted
and (postponed_date is not None)
and (postponed_date != timezone.now().date())
):
self.add_error(
"postpone_start_to",
"If the invitation is accepted for immediate start, you cannot postpone its start date.",
)
if invitation_postponed and not postponed_date:
self.add_error(
"postpone_start_to",
"If the invitation is postponed, you must set a start date in the future.",
)
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
<div class="m-2 mt-4"> <div class="m-2 mt-4">
<form hx-post="{% url 'colleges:_hx_fellowship_invitation_update_response' invitation_id=invitation.id %}" <form hx-post="{% url 'colleges:_hx_fellowship_invitation_update_response' invitation_id=invitation.id %}"
hx-target="#invitations_tablist" hx-target="#invitation-{{ invitation.id }}-update-response">
>
{% crispy form %} {% crispy form %}
</form> </form>
</div> </div>
...@@ -4,52 +4,28 @@ ...@@ -4,52 +4,28 @@
<details id="invitation-{{ invitation.id }}-details" <details id="invitation-{{ invitation.id }}-details"
class="my-2 border border-2"> class="my-2 border border-2">
<summary class="bg-light p-2">{{ invitation }}</summary> <summary class="bg-light p-2">{{ invitation }}</summary>
<details class="m-2 mt-4 border">
<summary class="p-2 bg-light">Events for this nomination</summary>
{% include 'colleges/_nomination_events_table.html' with nomination=invitation.nomination %}
</details>
<div class="p-2">
<h4>Checklist</h4>
<ul>
{% if not invitation.nomination.profile.contributor %}
<li class="text-danger">N.B.: this nominee is not yet registered as a Contributor</li>
{% else %}
<li>
<span class="text-success">{% include 'bi/check-square-fill.html' %}</span>&nbsp;This nominee has a Contributor account
</li>
{% endif %}
{% if selected == 'notyetinvited' %}
<li>
For named or elected, but not yet invited:
<a class="btn btn-primary"
href="{% url 'colleges:fellowship_invitation_email_initial' pk=invitation.id %}">prepare and send initial email</a>
</li>
{% elif selected == 'accepted' %}
<li>
Accepted to serve as Fellow but not currently active in a College? <a href="{% url 'colleges:fellowship_create' contributor_id=invitation.nomination.profile.contributor.id %}"
target="_blank">Set up a Fellowship</a>
</li>
{% endif %}
</ul>
<hr />
<div class="row">
<div class="col-12 col-md">
<details class="m-2 mt-3 border">
<summary class="p-2 bg-light list-triangle">Events</summary>
{% include 'colleges/_nomination_events_table.html' with nomination=invitation.nomination %}
</details>
</div>
<div class="col-12 col-md">
<details open class="m-2 mt-3 border">
<summary class="p-2 bg-light list-triangle">Checklist</summary>
{% include 'colleges/_nominations_invitation_checklist.html' with invitation=invitation %}
</details>
</div>
</div>
<div class="p-2">
<h4>Update the response to this invitation:</h4> <h4>Update the response to this invitation:</h4>
<div id="invitation-{{ invitation.id }}-update-response" <div id="invitation-{{ invitation.id }}-update-response"
hx-get="{% url 'colleges:_hx_fellowship_invitation_update_response' invitation_id=invitation.id %}" hx-get="{% url 'colleges:_hx_fellowship_invitation_update_response' invitation_id=invitation.id %}"
hx-trigger="toggle from:#invitation-{{ invitation.id }}-details"></div> hx-trigger="toggle from:#invitation-{{ invitation.id }}-details"
hx-target="this"></div>
</div> </div>
</details> </details>
{% empty %} {% empty %}
......
<div class="p-2">
<ul class="mb-0">
{% if not invitation.nomination.profile.contributor %}
<li class="text-danger">N.B.: this nominee is not yet registered as a Contributor</li>
{% else %}
<li>
<span class="text-success">{% include 'bi/check-square-fill.html' %}</span>&nbsp;This nominee has a Contributor account
</li>
{% endif %}
{% if selected == 'notyetinvited' %}
<li class="text-danger">This nominee is elected, but not yet invited.</li>
<a class="btn btn-primary"
href="{% url 'colleges:fellowship_invitation_email_initial' pk=invitation.id %}">Invite nominee</a>
{% elif selected == 'accepted' %}
<li>
Accepted to serve as Fellow but not currently active in a College? <a href="{% url 'colleges:fellowship_create' contributor_id=invitation.nomination.profile.contributor.id %}"
target="_blank">Set up a Fellowship</a>
</li>
{% endif %}
</ul>
</div>
...@@ -138,8 +138,9 @@ ...@@ -138,8 +138,9 @@
</summary> </summary>
<div class="p-2"> <div class="p-2">
<div id="voting_tablist" hx-trigger="toggle from:#voting-details, click from:body target:.nomination-start-round-btn" <div id="voting_tablist" hx-trigger="toggle from:#voting-details, click from:body target:.nomination-start-round-btn" hx-get="{% url 'colleges:_hx_voting_rounds' %}?tab=
hx-get="{% url 'colleges:_hx_voting_rounds' %}?tab={% if 'edadmin' in user_roles %}ongoing{% else %}ongoing-vote_required{% endif %}" ></div> {% if 'edadmin' in user_roles %}ongoing{% else %}ongoing-vote_required{% endif %}
"></div>
</div> </div>
</details> </details>
...@@ -152,7 +153,8 @@ ...@@ -152,7 +153,8 @@
<div class="p-2 mt-2"> <div class="p-2 mt-2">
<div id="invitations_tablist" <div id="invitations_tablist"
hx-get="{% url 'colleges:_hx_nominations_invitations' %}?response=notyetinvited" hx-get="{% url 'colleges:_hx_nominations_invitations' %}?response=notyetinvited"
hx-trigger="toggle from:#invitations-details"></div> hx-trigger="toggle from:#invitations-details"
hx-target="#invitations_tablist"></div>
</div> </div>
</details> </details>
{% endif %} {% endif %}
......
...@@ -1033,12 +1033,69 @@ def _hx_fellowship_invitation_update_response(request, invitation_id): ...@@ -1033,12 +1033,69 @@ def _hx_fellowship_invitation_update_response(request, invitation_id):
description=f"Response updated to: {invitation.get_response_display()}", description=f"Response updated to: {invitation.get_response_display()}",
by=request.user.contributor, by=request.user.contributor,
) )
return redirect(
"%s?response=%s" nonexpired_fellowship = (
% ( Fellowship.objects.exclude(
reverse("colleges:_hx_nominations_invitations"), until_date__lte=timezone.now().date(),
form.cleaned_data["response"], )
.filter(
college=invitation.nomination.college,
contributor=invitation.nomination.profile.contributor,
) )
.order_by("-start_date")
.first()
)
# If the invitation is accepted or postponed, create a Fellowship
if invitation.response in [
FellowshipInvitation.RESPONSE_ACCEPTED,
FellowshipInvitation.RESPONSE_POSTPONED,
]:
# Create a new Fellowship if no object exists
if not nonexpired_fellowship:
fellowship = Fellowship.objects.create(
college=invitation.nomination.college,
contributor=invitation.nomination.profile.contributor,
start_date=timezone.now()
if invitation.response == FellowshipInvitation.RESPONSE_ACCEPTED
else invitation.postpone_start_to,
until_date=None,
)
invitation.nomination.add_event(
description=f"Fellowship created (start: {fellowship.start_date.strftime('%Y-%m-%d')})",
by=request.user.contributor,
)
else:
# Update the start date of the Fellowship if an object already exists
nonexpired_fellowship.start_date = (
timezone.now()
if invitation.response == FellowshipInvitation.RESPONSE_ACCEPTED
else invitation.postpone_start_to
)
nonexpired_fellowship.until_date = None
invitation.nomination.add_event(
description=f"Fellowship start date updated (start: {nonexpired_fellowship.start_date.strftime('%Y-%m-%d')})",
by=request.user.contributor,
)
nonexpired_fellowship.save()
# Terminate the Fellowship if the invitation is declined
elif invitation.response == FellowshipInvitation.RESPONSE_DECLINED:
if nonexpired_fellowship:
nonexpired_fellowship.until_date = (
timezone.now().date()
if nonexpired_fellowship.is_active()
else nonexpired_fellowship.start_date
)
invitation.nomination.add_event(
description=f"Fellowship ended (end: {nonexpired_fellowship.until_date.strftime('%Y-%m-%d')})",
by=request.user.contributor,
)
nonexpired_fellowship.save()
return HTMXResponse(
f"Response updated to: {invitation.get_response_display()}",
tag="success",
) )
context = { context = {
"invitation": invitation, "invitation": invitation,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment