Skip to content

Conversation

@Gagan-Ram
Copy link
Member

@Gagan-Ram Gagan-Ram commented Nov 21, 2025

Add api app from talk component to enext.

Summary by Sourcery

Integrate the talks/submissions API from the legacy talk component into the core eventyay API and wire it into the main event routing.

New Features:

  • Expose submissions, schedules, slots, tags, questions, speakers, reviews, rooms, mail templates, access codes, and speaker information under the event API router.
  • Add permanent redirects from legacy /events/.../talks/... URLs to the new /events/.../submissions/... endpoints and provide endpoints for managing submission favourites.

Enhancements:

  • Port talk-related API views, serializers, filters, mixins, and permissions from the old pretalx-based module into the eventyay namespace, updating model imports and permission codes to the new base models.
  • Adjust OpenAPI documentation helpers and log action keys to use eventyay terminology (organizer, teams, tags, questions, answers, etc.).

Build:

  • Add drf-flex-fields and drf-spectacular as dependencies to support flexible serializers and API schema generation.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Nov 21, 2025

Reviewer's Guide

Migrates the talk API functionality into the eventyay enext app by wiring up submissions/schedule/talk-related endpoints under the main API, updating imports and models to use eventyay.* modules, aligning permission and rule names, and adding support libraries for flexible DRF serializers and schema generation.

Sequence diagram for talks to submissions redirect in the API

sequenceDiagram
    actor "Client" as Client
    participant "DjangoRouter" as DjangoRouter
    participant "talks_to_submissions_redirect" as TalksToSubmissionsRedirect
    participant "SubmissionViewSet" as SubmissionViewSet

    "Client"->>"DjangoRouter": "GET /api/events/<event>/talks/<subpath>?q=1"
    "DjangoRouter"->>"TalksToSubmissionsRedirect": "Dispatch request"
    "TalksToSubmissionsRedirect"->>"TalksToSubmissionsRedirect": "Build new path by replacing '/talks/' with '/submissions/' and preserving query string"
    "TalksToSubmissionsRedirect"-->>"Client": "301 Moved Permanently to /api/events/<event>/submissions/<subpath>?q=1"

    "Client"->>"DjangoRouter": "GET /api/events/<event>/submissions/<subpath>?q=1" "(follow redirect)"
    "DjangoRouter"->>"SubmissionViewSet": "Dispatch to appropriate SubmissionViewSet action"
    "SubmissionViewSet"-->>"Client": "200 OK with submission data"
Loading

Sequence diagram for managing submission favourites via new endpoints

sequenceDiagram
    actor "AuthenticatedUser" as AuthenticatedUser
    participant "DjangoRouter" as DjangoRouter
    participant "SubmissionFavouriteDeprecatedView" as SubmissionFavouriteDeprecatedView
    participant "favourites_view" as FavouritesView
    participant "favourite_view" as FavouriteView

    rect rgb(235, 248, 255)
        note over "AuthenticatedUser","DjangoRouter": "List favourite submissions"
        "AuthenticatedUser"->>"DjangoRouter": "GET /api/events/<event>/submissions/favourites/"
        "DjangoRouter"->>"FavouritesView": "Dispatch favourites_view"
        "FavouritesView"->>"FavouritesView": "Check 'base.list_schedule' permission and collect favourites"
        "FavouritesView"-->>"AuthenticatedUser": "200 OK with favourite submission codes"
    end

    rect rgb(235, 255, 235)
        note over "AuthenticatedUser","DjangoRouter": "Toggle favourite on a single submission"
        "AuthenticatedUser"->>"DjangoRouter": "POST /api/events/<event>/submissions/<code>/favourite/"
        "DjangoRouter"->>"FavouriteView": "Dispatch favourite_view"
        "FavouriteView"->>"FavouriteView": "Check 'base.list_schedule' permission and update favourite state"
        "FavouriteView"-->>"AuthenticatedUser": "200 OK with updated favourite status"
    end

    rect rgb(255, 245, 235)
        note over "AuthenticatedUser","DjangoRouter": "Use deprecated favourite-talk endpoint"
        "AuthenticatedUser"->>"DjangoRouter": "POST /api/events/<event>/favourite-talk/"
        "DjangoRouter"->>"SubmissionFavouriteDeprecatedView": "Dispatch deprecated view"
        "SubmissionFavouriteDeprecatedView"-->>"AuthenticatedUser": "Response using legacy handling"
    end
Loading

Updated class diagram for core talk-related API viewsets and serializers

classDiagram
    class SubmissionViewSet {
        +"queryset"
        +"serializer_class"
        +"get_legacy_queryset()"
        +"get_legacy_serializer_class()"
        +"get_legacy_serializer()"
        +"get_unversioned_serializer_class()"
        +"get_serializer()"
        +"is_orga"
    }

    class SpeakerViewSet {
        +"queryset"
        +"serializer_class"
        +"get_legacy_serializer_class()"
        +"get_legacy_queryset()"
        +"get_serializer()"
        +"is_orga"
    }

    class QuestionViewSet {
        +"queryset"
        +"serializer_class"
        +"filterset_fields"
        +"search_fields"
        +"get_queryset()"
    }

    class TalkSlotViewSet {
        +"queryset"
        +"serializer_class"
        +"filterset_fields"
        +"is_orga"
        +"get_unversioned_serializer_class()"
    }

    class RoomViewSet {
        +"queryset"
        +"serializer_class"
        +"filter_backends"
        +"pagination_class"
    }

    class SubmissionSerializer {
        +"Meta.model = Submission"
        +"Meta.expandable_fields: submission_type, tags, track, resources"
        +"Meta.extra_expandable_fields: slots, answers, speakers"
        +"get_answers(obj)"
    }

    class SpeakerSerializer {
        +"Meta.model = SpeakerProfile"
        +"Meta.expandable_fields: submissions"
        +"Meta.extra_expandable_fields: answers, submissions"
        +"get_answers(obj)"
    }

    class QuestionSerializer {
        +"Meta.model = TalkQuestion"
        +"Meta.expandable_fields: options, tracks, submission_types"
    }

    class AnswerSerializer {
        +"Meta.model = Answer"
        +"Meta.expandable_fields: question, options, person"
    }

    class AnswerCreateSerializer {
        +"question: PrimaryKeyRelatedField(queryset=TalkQuestion.objects.none())"
        +"validate(data)"
    }

    class AnswerOptionSerializer {
        +"Meta.model = AnswerOption"
        +"Meta.expandable_fields: question"
    }

    class AnswerOptionCreateSerializer {
        +"question: PrimaryKeyRelatedField(queryset=TalkQuestion.objects.none())"
        +"__init__(*args, **kwargs)"
    }

    class ScheduleSerializer {
        +"Meta.model = Schedule"
        +"Meta.extra_expandable_fields: slots"
    }

    class TalkSlotSerializer {
        +"Meta.model = TalkSlot"
        +"Meta.expandable_fields: submission, schedule, room"
    }

    class RoomSerializer {
        +"Meta.model = Room"
    }

    class TeamViewSet {
        +"get_queryset()"
        +"perform_update(serializer)"
        +"perform_destroy(instance)"
        +"delete_invite(request, invite_id)"
        +"remove_member(request)"
    }

    class TeamSerializer {
        +"Meta.model = Team"
        +"Meta.expandable_fields: limit_tracks, members, invites"
    }

    class TeamInviteSerializer {
        +"Meta.model = TeamInvite"
    }

    class User {
        +"email"
        +"is_active"
    }

    class Submission {
        +"code"
        +"state"
    }

    class SpeakerProfile {
        +"user"
        +"event"
    }

    class TalkQuestion {
        +"event"
        +"target: TalkQuestionTarget"
        +"variant: TalkQuestionVariant"
    }

    class Answer {
        +"question: TalkQuestion"
        +"submission: Submission"
        +"person: SpeakerProfile"
    }

    class AnswerOption {
        +"question: TalkQuestion"
        +"position"
    }

    class Schedule {
        +"event"
        +"version"
    }

    class TalkSlot {
        +"submission: Submission"
        +"schedule: Schedule"
        +"room: Room"
    }

    class Room {
        +"name"
        +"capacity"
    }

    class Team {
        +"organizer"
        +"members: User[*]"
    }

    class TeamInvite {
        +"team: Team"
        +"email"
    }

    SubmissionViewSet --> SubmissionSerializer : uses
    SpeakerViewSet --> SpeakerSerializer : uses
    QuestionViewSet --> QuestionSerializer : uses
    TalkSlotViewSet --> TalkSlotSerializer : uses
    RoomViewSet --> RoomSerializer : uses

    SubmissionSerializer --> Submission : serializes
    SpeakerSerializer --> SpeakerProfile : serializes
    QuestionSerializer --> TalkQuestion : serializes
    AnswerSerializer --> Answer : serializes
    AnswerOptionSerializer --> AnswerOption : serializes
    ScheduleSerializer --> Schedule : serializes
    TalkSlotSerializer --> TalkSlot : serializes
    RoomSerializer --> Room : serializes
    TeamSerializer --> Team : serializes
    TeamInviteSerializer --> TeamInvite : serializes

    Submission --> TalkSlot : has many slots
    Submission --> Answer : has many answers
    TalkQuestion --> Answer : has many answers
    TalkQuestion --> AnswerOption : has many options
    Schedule --> TalkSlot : has many slots
    Room --> TalkSlot : hosts many slots
    SpeakerProfile --> Answer : provides many answers
    Team --> TeamInvite : has many invites
    Team --> User : has many members
Loading

File-Level Changes

Change Details Files
Expose submissions, schedule, review, speaker, room, and related endpoints from the main eventyay API, including redirects and favourites handling.
  • Add a permanent redirect from /events//talks/... to /events//submissions/... preserving subpath and query string
  • Register new event-scoped viewsets for submissions, schedules, slots, tags, submission types, tracks, mail templates, access codes, speakers, reviews, rooms, questions/answers, and speaker-information on the event router
  • Add separate function-based endpoints for submission favourites, and mount the event router URL patterns under /events/slug:event/
  • Expose a deprecated favourite-talk endpoint mapped to SubmissionFavouriteDeprecatedView
app/eventyay/api/urls.py
Port talk/question-related serializers and views from pretalx into the eventyay namespace and switch them to the new TalkQuestion/TalkQuestionVariant/QuestionTarget models and rules.
  • Move question serializers to eventyay.api.serializers.question and update expandable_fields references to eventyay.* serializer paths
  • Switch serializer model references and querysets from Question/QuestionVariant/QuestionTarget to TalkQuestion/TalkQuestionVariant/TalkQuestionTarget
  • Update Answer and AnswerOption creation/validation logic to use TalkQuestion* enums and ensure target-specific validation remains intact
  • Move Question/Answer viewsets to eventyay.api.views.question, updating imports to eventyay.base.models.question and eventyay.talk_rules.submission.questions_for_user, and adjust querysets/filters accordingly
app/eventyay/api/serializers/question.py
app/eventyay/api/views/question.py
Port legacy serializers and team/speaker/submission/schedule/room/review/access-code/speaker-information views/serializers to the eventyay.* modules and base models, aligning permissions and log/event terminology.
  • Update legacy serializers to import models from eventyay.base.* (auth, profile, availability, room, schedule, slot, submission, tag, question, resource, review) and set LegacyQuestionSerializer to use TalkQuestion
  • Move team, speaker, submission, schedule, room, review, access_code, mail, speaker_information viewsets and serializers into the eventyay.api.* package, updating imports to eventyay.api.* helpers and eventyay.base.* models
  • Change organiser/organiser-related attributes and descriptions to organizer where applicable in docs and schema descriptions
  • Adjust log action names from pretalx.* to eventyay.* in team-related actions and update organiser/organizer attribute access for permission checks and logging
  • Update DRF filters (schedule, review) to use eventyay.base models and keep scopes_disabled context
app/eventyay/api/serializers/legacy.py
app/eventyay/api/views/team.py
app/eventyay/api/documentation.py
app/eventyay/api/serializers/submission.py
app/eventyay/api/views/submission.py
app/eventyay/api/serializers/review.py
app/eventyay/api/serializers/speaker.py
app/eventyay/api/views/speaker.py
app/eventyay/api/filters/schedule.py
app/eventyay/api/serializers/schedule.py
app/eventyay/api/views/room.py
app/eventyay/api/filters/review.py
app/eventyay/api/serializers/access_code.py
app/eventyay/api/serializers/speaker_information.py
app/eventyay/api/serializers/mail.py
app/eventyay/api/serializers/room.py
app/eventyay/api/views/access_code.py
app/eventyay/api/views/mail.py
app/eventyay/api/views/review.py
app/eventyay/api/views/speaker_information.py
Align API permissions, auth, and mixins with the eventyay base implementation and permission codename scheme.
  • Update ApiPermission helper to fall back to request.organizer instead of request.organiser and to use eventyay.talk_rules.person.is_only_reviewer
  • Change all permission codenames from submission., schedule., person.* to base.* equivalents (e.g., base.orga_list_submission, base.list_schedule, base.orga_list_speakerprofile, base.orga_view_schedule) across submission and speaker viewsets and schedule-related views
  • Switch DRF TokenAuthentication to use eventyay.base.models.auth_token.UserApiToken
  • Wire mixins to use eventyay.api.versions.get_api_version_from_request and get_serializer_by_version
app/eventyay/api/permissions.py
app/eventyay/common/auth.py
app/eventyay/api/mixins.py
app/eventyay/api/views/submission.py
app/eventyay/api/views/speaker.py
app/eventyay/api/views/schedule.py
Integrate DRF extensions (flex-fields and spectacular) and event serializers with the core app.
  • Add drf-flex-fields and drf-spectacular as runtime dependencies in app/pyproject.toml
  • Introduce event API documentation helpers and OpenAPI metadata wired to drf-spectacular, including updated descriptions for organizer-oriented resources
  • Update Event serializer to load plugins via eventyay.base.plugins.get_all_plugins instead of pretix.base.plugins
  • Minor formatting fixes in event and rooms views to ensure correct file termination
app/pyproject.toml
app/eventyay/api/documentation.py
app/eventyay/api/serializers/event.py
app/eventyay/api/views/event.py
app/eventyay/api/views/rooms.py

Possibly linked issues

  • #ENext Migrations: PR migrates the talk API (serializers, views, URLs) to use unified base models, supporting the ENext migration.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • In talks_to_submissions_redirect, consider building the target URL via reverse/resolve rather than string replace on request.path to avoid edge cases (e.g., unexpected extra 'talks' segments or future URL changes) and to let Django handle URL construction.
  • There are many hard-coded permission codename strings (e.g., 'base.orga_list_submission', 'base.list_schedule'); centralising these in a shared constants module or helper would make future permission refactors less error-prone and help keep backend and rules in sync.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In talks_to_submissions_redirect, consider building the target URL via reverse/resolve rather than string replace on request.path to avoid edge cases (e.g., unexpected extra 'talks' segments or future URL changes) and to let Django handle URL construction.
- There are many hard-coded permission codename strings (e.g., 'base.orga_list_submission', 'base.list_schedule'); centralising these in a shared constants module or helper would make future permission refactors less error-prone and help keep backend and rules in sync.

## Individual Comments

### Comment 1
<location> `app/eventyay/api/urls.py:47-49` </location>
<code_context>
def talks_to_submissions_redirect(request, event, subpath):
    """
    Redirects requests from /events/.../talks/... to /events/.../submissions/...
    preserving the subpath and query parameters.
    """
    new_path = request.path.replace("/talks/", "/submissions/", 1)

    query_string = request.META.get('QUERY_STRING', '')
    if query_string:
        new_path += '?' + query_string

    return HttpResponsePermanentRedirect(new_path)

</code_context>

<issue_to_address>
**suggestion (code-quality):** We've found these issues:

- Use named expression to simplify assignment and conditional ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))
- Use f-string instead of string concatenation ([`use-fstring-for-concatenation`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-fstring-for-concatenation/))

```suggestion
    if query_string := request.META.get('QUERY_STRING', ''):
        new_path += f'?{query_string}'
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +47 to +49
query_string = request.META.get('QUERY_STRING', '')
if query_string:
new_path += '?' + query_string
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (code-quality): We've found these issues:

Suggested change
query_string = request.META.get('QUERY_STRING', '')
if query_string:
new_path += '?' + query_string
if query_string := request.META.get('QUERY_STRING', ''):
new_path += f'?{query_string}'

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR migrates the talks/submissions API from the legacy talk component into the main eventyay (enext) application. The migration includes porting API views, serializers, filters, permissions, and routing from the old pretalx-based module structure to the eventyay namespace.

Key changes:

  • Add drf-flex-fields and drf-spectacular as dependencies for flexible serializers and API schema generation
  • Update all import paths from pretalx.* to eventyay.*
  • Rename models and permission codes (e.g., organiserorganizer, QuestionTalkQuestion)
  • Wire submission-related endpoints into the main event router with permanent redirects from legacy /talks/ URLs to /submissions/

Reviewed changes

Copilot reviewed 32 out of 38 changed files in this pull request and generated no comments.

Show a summary per file
File Description
app/pyproject.toml Added drf-flex-fields and drf-spectacular dependencies
app/uv.lock Lock file updates for new dependencies including inflection and uritemplate
app/eventyay/common/auth.py Updated UserApiToken import path to eventyay namespace
app/eventyay/api/views/team.py Updated imports and changed organiser → organizer terminology
app/eventyay/api/views/submission.py Migrated submission view imports and updated permission codes
app/eventyay/api/views/speaker*.py Updated speaker and speaker_information view imports
app/eventyay/api/views/schedule.py Updated schedule and slot view imports
app/eventyay/api/views/room*.py Updated room view imports
app/eventyay/api/views/review.py Updated review view imports
app/eventyay/api/views/question.py Updated question view imports and Question → TalkQuestion renaming
app/eventyay/api/views/mail.py Updated mail template view imports
app/eventyay/api/views/access_code.py Updated access code view imports
app/eventyay/api/urls.py Added submission-related endpoints and legacy redirect logic
app/eventyay/api/templates/rest_framework/api.html New template for API browsable interface
app/eventyay/api/serializers/*.py Updated all serializer imports and expandable field paths
app/eventyay/api/permissions.py Updated permission object getter to use organizer
app/eventyay/api/pagination.py New pagination classes supporting legacy API fallback
app/eventyay/api/mixins.py Updated version utility imports
app/eventyay/api/filters/*.py Updated filter model imports
app/eventyay/api/exceptions.py New API exception handler with logging
app/eventyay/api/documentation.py Updated documentation with organizer terminology
talk/src/pretalx/api/views/upload.py Removed legacy upload view file
Comments suppressed due to low confidence (3)

app/eventyay/api/serializers/team.py:9

  • The Team model is imported twice from different modules (line 6 from eventyay.base.models.event and line 7 from eventyay.base.models.organizer). This creates ambiguity about which Team model is being used. Remove the duplicate import and keep only the correct one.
    app/eventyay/api/documentation.py:81
  • Corrected 'Organisers' to 'organizers' but inconsistently capitalized - should be 'Organizers' to match the sentence structure.
    app/eventyay/api/serializers/speaker.py:11
  • The import of ModelSerializer was removed but this appears to be a cleanup of an unused import. Verify that no code in this file actually requires ModelSerializer.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants