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
2 changes: 2 additions & 0 deletions .github/workflows/frontend-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,7 @@ jobs:
run: pnpm install
- name: Codegen
run: pnpm run codegen
env:
API_URL_SERVER: https://pastaporto-admin.pycon.it
- name: Run Biome
run: biome ci .
8 changes: 5 additions & 3 deletions backend/api/files_upload/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ def _check_proposal_material(self, user, input: "UploadFileInput") -> bool:
conference_code = input.data.conference_code

try:
proposal = Submission.objects.for_conference_code(
conference_code
).get_by_hashid(proposal_id)
proposal = (
Submission.objects.for_conference_code(conference_code)
.filter(status=Submission.STATUS.accepted)
.get_by_hashid(proposal_id)
)
except (Submission.DoesNotExist, IndexError):
return False

Expand Down
29 changes: 24 additions & 5 deletions backend/api/files_upload/tests/mutations/test_upload_file.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest
from files_upload.models import File
from submissions.tests.factories import SubmissionFactory
from submissions.tests.factories import AcceptedSubmissionFactory, SubmissionFactory
from conferences.tests.factories import ConferenceFactory
from django.test import override_settings

Expand Down Expand Up @@ -64,7 +64,7 @@ def test_upload_participant_avatar_to_invalid_conf_fails(graphql_client, user):


def test_upload_proposal_material_file(graphql_client, user):
proposal = SubmissionFactory(speaker=user)
proposal = AcceptedSubmissionFactory(speaker=user)
graphql_client.force_login(user)

response = _upload_file(
Expand All @@ -88,7 +88,26 @@ def test_upload_proposal_material_file(graphql_client, user):


def test_cannot_upload_proposal_material_file_if_not_speaker(graphql_client, user):
proposal = SubmissionFactory()
proposal = AcceptedSubmissionFactory()
graphql_client.force_login(user)

response = _upload_file(
graphql_client,
{
"proposalMaterial": {
"filename": "test.txt",
"proposalId": proposal.hashid,
"conferenceCode": proposal.conference.code,
}
},
)

assert not response["data"]
assert response["errors"][0]["message"] == "You cannot upload files of this type"


def test_cannot_upload_proposal_material_file_if_not_accepted(graphql_client, user):
proposal = SubmissionFactory(status="proposed")
graphql_client.force_login(user)

response = _upload_file(
Expand Down Expand Up @@ -129,7 +148,7 @@ def test_cannot_upload_proposal_material_file_with_invalid_proposal_id(
def test_cannot_upload_proposal_material_file_with_invalid_proposal_id_for_conference(
graphql_client, user
):
proposal = SubmissionFactory()
proposal = AcceptedSubmissionFactory()
graphql_client.force_login(user)

response = _upload_file(
Expand All @@ -151,7 +170,7 @@ def test_cannot_upload_proposal_material_file_with_invalid_proposal_id_for_confe
"file_type", [File.Type.PARTICIPANT_AVATAR, File.Type.PROPOSAL_MATERIAL]
)
def test_superusers_can_upload_anything(graphql_client, admin_superuser, file_type):
proposal = SubmissionFactory()
proposal = AcceptedSubmissionFactory()
graphql_client.force_login(admin_superuser)

req_input = {}
Expand Down
76 changes: 73 additions & 3 deletions backend/api/submissions/mutations.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from urllib.parse import urljoin
from django.conf import settings
from conferences.frontend import trigger_frontend_revalidate
from grants.tasks import get_name
from notifications.models import EmailTemplate, EmailTemplateIdentifier
from strawberry.scalars import JSON
Expand All @@ -20,15 +21,22 @@
from i18n.strings import LazyI18nString
from languages.models import Language
from participants.models import Participant
from submissions.models import Submission as SubmissionModel
from submissions.models import ProposalMaterial, Submission as SubmissionModel
from submissions.tasks import notify_new_cfp_submission

from .types import Submission
from .types import Submission, SubmissionMaterialInput

FACEBOOK_LINK_MATCH = re.compile(r"^http(s)?:\/\/(www\.)?facebook\.com\/")
LINKEDIN_LINK_MATCH = re.compile(r"^http(s)?:\/\/(www\.)?linkedin\.com\/")


@strawberry.type
class ProposalMaterialErrors:
file_id: list[str] = strawberry.field(default_factory=list)
url: list[str] = strawberry.field(default_factory=list)
id: list[str] = strawberry.field(default_factory=list)


@strawberry.type
class SendSubmissionErrors(BaseErrorType):
@strawberry.type
Expand All @@ -46,6 +54,7 @@ class _SendSubmissionErrors:
audience_level: list[str] = strawberry.field(default_factory=list)
tags: list[str] = strawberry.field(default_factory=list)
short_social_summary: list[str] = strawberry.field(default_factory=list)
materials: list[ProposalMaterialErrors] = strawberry.field(default_factory=list)

speaker_bio: list[str] = strawberry.field(default_factory=list)
speaker_photo: list[str] = strawberry.field(default_factory=list)
Expand Down Expand Up @@ -247,6 +256,22 @@ class UpdateSubmissionInput(BaseSubmissionInput):

topic: Optional[ID] = strawberry.field(default=None)
tags: list[ID] = strawberry.field(default_factory=list)
materials: list[SubmissionMaterialInput] = strawberry.field(default_factory=list)

def validate(self, conference: Conference, submission: SubmissionModel):
errors = super().validate(conference)

if self.materials:
if len(self.materials) > 3:
errors.add_error(
"non_field_errors", "You can only add up to 3 materials"
)
else:
for index, material in enumerate(self.materials):
with errors.with_prefix("materials", index):
material.validate(errors, submission)

return errors


SendSubmissionOutput = Annotated[
Expand Down Expand Up @@ -276,7 +301,7 @@ def update_submission(

conference = instance.conference

errors = input.validate(conference=conference)
errors = input.validate(conference=conference, submission=instance)

if errors.has_errors:
return errors
Expand All @@ -294,13 +319,56 @@ def update_submission(
instance.speaker_level = input.speaker_level
instance.previous_talk_video = input.previous_talk_video
instance.short_social_summary = input.short_social_summary

languages = Language.objects.filter(code__in=input.languages).all()
instance.languages.set(languages)

instance.tags.set(input.tags)

instance.save()

materials_to_create = []
materials_to_update = []

existing_materials = {
existing_material.id: existing_material
for existing_material in instance.materials.all()
}
for material in input.materials:
existing_material = (
existing_materials.get(int(material.id)) if material.id else None
)

if existing_material:
existing_material.name = material.name
existing_material.url = material.url
existing_material.file_id = material.file_id
materials_to_update.append(existing_material)
else:
materials_to_create.append(
ProposalMaterial(
proposal=instance,
name=material.name,
url=material.url,
file_id=material.file_id,
)
)

if to_delete := [
m.id for m in existing_materials.values() if m not in materials_to_update
]:
ProposalMaterial.objects.filter(
proposal=instance,
id__in=to_delete,
).delete()

if materials_to_create:
ProposalMaterial.objects.bulk_create(materials_to_create)
if materials_to_update:
ProposalMaterial.objects.bulk_update(
materials_to_update, fields=["name", "url", "file_id"]
)

Participant.objects.update_or_create(
user_id=request.user.id,
conference=conference,
Expand All @@ -319,6 +387,8 @@ def update_submission(
},
)

trigger_frontend_revalidate(conference, instance)

instance.__strawberry_definition__ = Submission.__strawberry_definition__
return instance

Expand Down
Loading
Loading