Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement form to request an invitation letter #4279

Merged
merged 9 commits into from
Dec 29, 2024
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
4 changes: 2 additions & 2 deletions backend/api/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
from .association_membership.mutation import AssociationMembershipMutation
from .cms.schema import CMSQuery
from .sponsors.schema import SponsorsMutation
from .visa.queries import VisaQuery
from .visa.mutations import VisaMutation
from .visa.query import VisaQuery
from .visa.mutation import VisaMutation


@strawberry.type
Expand Down
10 changes: 10 additions & 0 deletions backend/api/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,13 @@ def paginate_list(
@strawberry.type
class NotFound:
message: str = "Not found"


@strawberry.type
class NoAdmissionTicket:
message: str = "User does not have admission ticket"


@strawberry.type
class FormNotAvailable:
message: str = "Form is not available"
155 changes: 155 additions & 0 deletions backend/api/users/tests/test_invitation_letter_request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
from api.visa.types import (
InvitationLetterRequestStatus as InvitationLetterRequestStatusAPI,
)
from visa.models import InvitationLetterRequestStatus as InvitationLetterRequestStatusDB
from users.tests.factories import UserFactory
from visa.models import InvitationLetterRequestOnBehalfOf
from visa.tests.factories import InvitationLetterRequestFactory
from conferences.tests.factories import ConferenceFactory
import pytest

pytestmark = pytest.mark.django_db


def _query_invitation_letter_request(client, conference):
return client.query(
"""query($conference: String!) {
me {
invitationLetterRequest(conference: $conference) {
id
status
}
}
}""",
variables={
"conference": conference.code,
},
)


def test_get_user_invitation_letter_request_with_none_present(graphql_client, user):
graphql_client.force_login(user)

conference = ConferenceFactory()
response = _query_invitation_letter_request(graphql_client, conference)

me = response["data"]["me"]
assert me["invitationLetterRequest"] is None


def test_get_user_invitation_letter_request(graphql_client, user):
graphql_client.force_login(user)

conference = ConferenceFactory()
invitation_letter_request = InvitationLetterRequestFactory(
requester=user,
conference=conference,
)

response = _query_invitation_letter_request(graphql_client, conference)

me = response["data"]["me"]
assert me["invitationLetterRequest"]["id"] == str(invitation_letter_request.id)
assert me["invitationLetterRequest"]["status"] == invitation_letter_request.status


@pytest.mark.parametrize(
"actual_status,exposed_status",
[
(
InvitationLetterRequestStatusDB.PENDING,
InvitationLetterRequestStatusAPI.PENDING,
),
(
InvitationLetterRequestStatusDB.PROCESSING,
InvitationLetterRequestStatusAPI.PENDING,
),
(
InvitationLetterRequestStatusDB.FAILED_TO_GENERATE,
InvitationLetterRequestStatusAPI.PENDING,
),
(
InvitationLetterRequestStatusDB.PROCESSED,
InvitationLetterRequestStatusAPI.PENDING,
),
(InvitationLetterRequestStatusDB.SENT, InvitationLetterRequestStatusAPI.SENT),
(
InvitationLetterRequestStatusDB.REJECTED,
InvitationLetterRequestStatusAPI.REJECTED,
),
],
)
def test_user_invitation_letter_request_has_user_friendly_status(
graphql_client, user, actual_status, exposed_status
):
graphql_client.force_login(user)

conference = ConferenceFactory()
invitation_letter_request = InvitationLetterRequestFactory(
requester=user, conference=conference, status=actual_status
)

response = _query_invitation_letter_request(graphql_client, conference)

me = response["data"]["me"]
assert me["invitationLetterRequest"]["id"] == str(invitation_letter_request.id)
assert me["invitationLetterRequest"]["status"] == exposed_status.name


def test_on_behalf_of_others_invitation_letter_request_are_excluded(
graphql_client, user
):
graphql_client.force_login(user)

conference = ConferenceFactory()

InvitationLetterRequestFactory(
requester=user,
conference=conference,
on_behalf_of=InvitationLetterRequestOnBehalfOf.OTHER,
)

InvitationLetterRequestFactory(
requester=user,
conference=conference,
on_behalf_of=InvitationLetterRequestOnBehalfOf.OTHER,
)

response = _query_invitation_letter_request(graphql_client, conference)

me = response["data"]["me"]
assert me["invitationLetterRequest"] is None


def test_other_users_invitation_letter_requests_are_excluded(graphql_client, user):
graphql_client.force_login(user)

conference = ConferenceFactory()

InvitationLetterRequestFactory(
requester=UserFactory(),
conference=conference,
on_behalf_of=InvitationLetterRequestOnBehalfOf.SELF,
)

response = _query_invitation_letter_request(graphql_client, conference)

me = response["data"]["me"]
assert me["invitationLetterRequest"] is None


def test_other_conferences_invitation_letter_request_are_excluded(graphql_client, user):
graphql_client.force_login(user)

conference = ConferenceFactory()

InvitationLetterRequestFactory(
requester=user,
conference=ConferenceFactory(),
on_behalf_of=InvitationLetterRequestOnBehalfOf.SELF,
)

response = _query_invitation_letter_request(graphql_client, conference)

me = response["data"]["me"]
assert me["invitationLetterRequest"] is None
46 changes: 46 additions & 0 deletions backend/api/users/tests/test_me.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,49 @@ def test_is_python_italia_member_with_expired_membership(graphql_client, user, s

me = response["data"]["me"]
assert me["isPythonItaliaMember"] is False


@pytest.mark.parametrize("has_ticket", [True, False])
def test_has_admission_ticket(mock_has_ticket, has_ticket, graphql_client, user):
graphql_client.force_login(user)

conference = ConferenceFactory()
mock_has_ticket(conference, has_ticket=has_ticket, user=user)

response = graphql_client.query(
"""query($conference: String!) {
me {
hasAdmissionTicket(conference: $conference)
}
}""",
variables={
"conference": conference.code,
},
)

me = response["data"]["me"]
assert me["hasAdmissionTicket"] == has_ticket


@pytest.mark.parametrize("has_ticket", [True, False])
def test_has_admission_ticket_with_non_existent_conference(
mock_has_ticket, has_ticket, graphql_client, user
):
graphql_client.force_login(user)

conference = ConferenceFactory()
mock_has_ticket(conference, has_ticket=has_ticket, user=user)

response = graphql_client.query(
"""query($conference: String!) {
me {
hasAdmissionTicket(conference: $conference)
}
}""",
variables={
"conference": "invalid",
},
)

me = response["data"]["me"]
assert me["hasAdmissionTicket"] is False
35 changes: 35 additions & 0 deletions backend/api/users/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@

from django.urls import reverse
from api.billing.types import BillingAddress
from api.visa.types import InvitationLetterRequest
from visa.models import (
InvitationLetterRequest as InvitationLetterRequestModel,
InvitationLetterRequestOnBehalfOf,
)
from pretix import user_has_admission_ticket
from pycon.signing import sign_path
import strawberry
from strawberry.types import Info
Expand Down Expand Up @@ -139,6 +145,19 @@ def tickets(
attendee_tickets = get_user_tickets(conference, self.email, language)
return [ticket for ticket in attendee_tickets]

@strawberry.field
def has_admission_ticket(self, conference: str) -> bool:
conference = Conference.objects.filter(code=conference).first()

if not conference:
return False

return user_has_admission_ticket(
email=self.email,
event_organizer=conference.pretix_organizer_id,
event_slug=conference.pretix_event_id,
)

@strawberry.field
def submissions(self, info: Info, conference: str) -> list[Submission]:
return SubmissionModel.objects.filter(
Expand All @@ -162,6 +181,22 @@ def billing_addresses(self, conference: str) -> list[BillingAddress]:
.all()
]

@strawberry.field
def invitation_letter_request(
self, conference: str
) -> InvitationLetterRequest | None:
invitation_letter_request = (
InvitationLetterRequestModel.objects.for_conference_code(conference)
.of_user(self.id)
.filter(on_behalf_of=InvitationLetterRequestOnBehalfOf.SELF)
.first()
)
return (
InvitationLetterRequest.from_model(invitation_letter_request)
if invitation_letter_request
else None
)

@classmethod
def from_django_model(cls, user):
return cls(
Expand Down
13 changes: 13 additions & 0 deletions backend/api/visa/mutation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from api.visa.mutations.update_invitation_letter_document import (
update_invitation_letter_document,
)
from api.visa.mutations.request_invitation_letter import request_invitation_letter
from strawberry.tools import create_type

VisaMutation = create_type(
"VisaMutation",
(
update_invitation_letter_document,
request_invitation_letter,
),
)
Empty file.
Loading
Loading