Skip to content

feature (registration): add order extra question answers to ticket CSV import#567

Merged
smarcet merged 7 commits into
mainfrom
feature/ticket-import-extra-question-answers
Jul 6, 2026
Merged

feature (registration): add order extra question answers to ticket CSV import#567
smarcet merged 7 commits into
mainfrom
feature/ticket-import-extra-question-answers

Conversation

@JpMaxMan

@JpMaxMan JpMaxMan commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

ref: https://app.clickup.com/t/9014802374/86baraz3d

  • After the row's ticket is resolved/created, answers are upserted for the row's attendee (falls back to the ticket owner for rows keyed only by ticket number). Unknown question names, order-usage questions, questions not allowed for the attendee, unknown list values and empty values are logged-and-skipped — they never fail the row.
  • Persistence reuses the existing DTO path (SummitAttendee::hadCompletedExtraQuestions, same method SummitAttendeeFactory::populate delegates to). populate() itself is not called with a minimal payload because it has member/email side effects (clearMember() when no member passed) that are wrong for a partial update.
  • Since that path clears-and-rebuilds the full answer set, former answers not overridden by the CSV are carried into the DTO (best-effort: answers whose question was deleted or is no longer allowed are dropped by the shared path — identical to the admin PUT-attendee flow).
  • List-type questions: cells accept value name, label or raw id, |-separated for CheckBoxList (mirrors the attendee_tags convention); stored as comma-separated value ids as the persistence path and consumers (badge-print-app) expect. Multiple values on single-select types are rejected with a warning.
  • GET /summits/{id}/tickets/csv/template emits one extra_question:{name} column per Ticket/Both-usage order extra question. OpenAPI descriptions and docblocks updated.

Gating decision

Import respects the hasAllowedExtraQuestions gate from AttendeeService::updateAttendee. The persistence path clears-and-rebuilds all answers, so an attendee with zero allowed questions would have every existing answer wiped with nothing rebuilt — the gate is data protection, not permissions. Bypassing it gains nothing (no CSV answer could apply anyway) and risks destroying historical answers.

The per-question answer-change lock (canChangeAnswerValue) is also respected: the import job runs without an authenticated admin, so on summits with allow_update_attendee_extra_questions off, import fills blank answers but skips (warns on) changes to existing non-empty ones — matching what the platform enforces for the same actor context, pre-checked deterministically so a locked answer skips one column instead of rolling back the whole row.

Tests

tests/SummitOrderServiceTest.php: answer set on newly created attendee; answer updated on existing attendee; unknown question skipped without failing the row; empty value ignored (existing answer preserved); list question stores value ids; badge feature columns still cleared + re-set alongside extra-question columns.

Summary by CodeRabbit

  • New Features
    • CSV ticket template downloads and imports now support extra_question:{question name} columns for ticket-scoped extra questions, including list/checkbox-list inputs stored as value IDs.
  • Bug Fixes
    • Extra-question imports ignore unknown/empty values, skip order-scoped questions, and preserve stored answers when updates aren’t allowed.
    • Ensures badge-feature gating stays correct when badge grants and extra-question answers are in the same CSV row; clears cached allowed badge-feature results when needed.
  • Tests
    • Expanded coverage for create/update, locked answers, list normalization, missing/empty values, order-scoped skipping, and badge/extra-question same-row scenarios.

…port

Adds extra_question:{question name} column support to the ticket data
import, template endpoint and OpenAPI docs. Answers are upserted through
the existing ExtraQuestionAnswerHolder persistence path; unknown
questions, order-scoped questions, disallowed questions, empty values
and locked answers are logged and skipped without failing the row.
List type questions accept value name/label/id ('|' separated for
CheckBoxList) and store value ids.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a standardized extra_question: CSV column prefix, extends ticket CSV template/import handling for ticket-scoped extra questions, implements attendee answer parsing and persistence during import, and expands tests for the new behavior.

Changes

Extra Question CSV Import

Layer / File(s) Summary
Extra question column prefix contract
app/Services/Model/ISummitOrderService.php, app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitTicketApiController.php
Adds ExtraQuestionColumnPrefix = 'extra_question:', updates template/import descriptions, and generates ticket-scoped extra question columns in the CSV template.
Import parsing and answer persistence
app/Models/Foundation/Summit/Registration/Attendees/SummitAttendee.php, app/Services/Model/Imp/SummitOrderService.php
Adds attendee badge-feature cache eviction support, reorders row processing around badge/feature updates, and parses plus persists attendee extra-question answers while preserving prior values.
Test fixtures and import coverage
tests/SummitOrderServiceTest.php
Adds CSV import helpers, extra-question fixtures, attendee lookup helpers, and tests for new answers, updates, empty values, unknown questions, list values, badge feature interaction, and badge creation.

Estimated code review effort: 4 (Complex) | ~60 minutes

Suggested reviewers: smarcet

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant OAuth2SummitTicketApiController
  participant SummitOrderService
  participant SummitAttendee

  Client->>OAuth2SummitTicketApiController: request ticket CSV template or import
  OAuth2SummitTicketApiController->>SummitOrderService: load order extra questions
  OAuth2SummitTicketApiController->>Client: template / documented columns

  Client->>SummitOrderService: import CSV rows
  SummitOrderService->>SummitAttendee: evict allowed badge-features cache
  SummitOrderService->>SummitOrderService: resolve extra-question columns
  SummitOrderService->>SummitOrderService: upsert attendee extra-question answers
Loading
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR does not implement the linked sub-question ORM, migration, or sub-question-rule endpoints in #39. Add the ORM mappings, migrations, model updates, and the two sub-question-rule API endpoints required by issue #39.
Out of Scope Changes check ⚠️ Warning Most changes focus on CSV import/template handling and tests, which are unrelated to the linked sub-question work. Remove or justify the CSV import and cache changes, or link them to an issue that explicitly requests ticket extra-question import.
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: importing order extra question answers from ticket CSV files.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/ticket-import-extra-question-answers

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

➡️ https://OpenStackweb.github.io/summit-api/openapi/pr-567/

This page is automatically updated on each push to this PR.

The import re-reads tickets with HINT_REFRESH, so it sees DB state: the
fixture's assigned tickets share one badge entity whose FK points at only
the last of them. Use an unassigned ticket, which gets its own
DB-consistent badge from SummitTicketType::applyTo.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

➡️ https://OpenStackweb.github.io/summit-api/openapi/pr-567/

This page is automatically updated on each push to this PR.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/Services/Model/Imp/SummitOrderService.php`:
- Around line 4745-4786: The multi-value comparison in
SummitOrderService::upsertAttendeeExtraQuestionAnswers is order-sensitive, so
re-importing the same CheckBoxList selections in a different order can
incorrectly hit the “cannot change” skip. Normalize the selected ids before
comparing by applying a stable ordering to the $value_ids-derived string, and
apply the same normalization to $former_value before the $former_value != $value
check so equivalent answers are treated as unchanged.
- Around line 4806-4841: Wrap the `hadCompletedExtraQuestions()` call in
`SummitOrderService::upsertAttendeeExtraQuestionAnswers` with a
`ValidationException` catch and return early so the current row is skipped
instead of failing the whole per-row transaction. Keep the existing
debug/warning behavior, and handle the exception locally in this method so
`processTicketData()` can continue with later rows.

In `@tests/SummitOrderServiceTest.php`:
- Around line 388-421: The test helper buildTicketDataImportService sets up
$upload_strategy without the save() behavior, but processTicketData() invokes
save() on IFileUploadStrategy during this path. Add the missing save expectation
on the $upload_strategy mock in buildTicketDataImportService so
SummitOrderService can complete the ticket data import flow without Mockery
throwing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 091dda31-c9fd-417e-beac-e98f68bb7eb4

📥 Commits

Reviewing files that changed from the base of the PR and between b3f265b and 2b4196d.

📒 Files selected for processing (4)
  • app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitTicketApiController.php
  • app/Services/Model/ISummitOrderService.php
  • app/Services/Model/Imp/SummitOrderService.php
  • tests/SummitOrderServiceTest.php

Comment thread app/Services/Model/Imp/SummitOrderService.php
Comment thread app/Services/Model/Imp/SummitOrderService.php Outdated
Comment thread tests/SummitOrderServiceTest.php
…on answer persistence

Per CodeRabbit review: sort list-question value ids so the same selection
in a different order is not treated as a changed answer, and catch
ValidationException from the extra-question persistence path so one bad
payload cannot strand the remaining import rows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

➡️ https://OpenStackweb.github.io/summit-api/openapi/pr-567/

This page is automatically updated on each push to this PR.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds support for importing attendee order extra-question answers from the ticket CSV import flow, and exposes corresponding extra_question:{name} columns in the CSV template endpoint so admins can populate these answers via bulk operations.

Changes:

  • Extend ticket CSV import to upsert attendee extra-question answers from extra_question:{question name} columns (including list/checkbox parsing and guardrails).
  • Extend GET /summits/{id}/tickets/csv/template to include one extra_question:{name} column per relevant order extra question.
  • Add/expand test coverage around new create/update/skip behaviors for extra-question imports and badge-feature interaction.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
tests/SummitOrderServiceTest.php Adds helper methods + new tests validating extra-question import behaviors and badge-feature interactions.
app/Services/Model/ISummitOrderService.php Introduces a shared constant for the extra_question: CSV column prefix.
app/Services/Model/Imp/SummitOrderService.php Implements extra-question parsing/upsert during ticket CSV processing.
app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitTicketApiController.php Updates CSV template generation and OpenAPI text to document/include extra-question columns.

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

Comment thread tests/SummitOrderServiceTest.php Outdated
Comment thread app/Services/Model/Imp/SummitOrderService.php Outdated
Comment thread app/Services/Model/Imp/SummitOrderService.php Outdated
Comment thread app/Services/Model/ISummitOrderService.php
Comment thread app/Services/Model/Imp/SummitOrderService.php
Comment thread app/Services/Model/Imp/SummitOrderService.php
Comment thread app/Services/Model/Imp/SummitOrderService.php
Comment thread app/Services/Model/Imp/SummitOrderService.php

@smarcet smarcet left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@JpMaxMan please review

Per review: process badge data before extra questions and flush + evict
the badge-features result cache so same-row badge/feature grants are
visible to the question permission gates; split the upsert into
resolve/merge helpers; ctype_digit for raw value ids; targeted cache
forget in tests. Adds regression tests for the same-row feature grant
(new + existing attendee), order-scoped skip, locked-answer skip, and
badge creation for badge-less tickets (folds in #568).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

➡️ https://OpenStackweb.github.io/summit-api/openapi/pr-567/

This page is automatically updated on each push to this PR.

@JpMaxMan JpMaxMan requested a review from smarcet July 3, 2026 18:30

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/Services/Model/Imp/SummitOrderService.php`:
- Around line 4867-4909: The resolved choice ids in
SummitOrderService::resolveListQuestionValue can be added more than once when
the same token appears repeatedly or matches by multiple lookup paths, which
then breaks single-value validation and CheckBoxList comparison. Deduplicate
$value_ids before the count check and before sorting/returning, so the logic in
resolveListQuestionValue stays set-based and matches the equality handling used
by resolveExtraQuestionColumns.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fbf34d7f-bc31-401e-8c6c-5ad8f6cd67df

📥 Commits

Reviewing files that changed from the base of the PR and between 8cdce73 and da8b3d4.

📒 Files selected for processing (3)
  • app/Models/Foundation/Summit/Registration/Attendees/SummitAttendee.php
  • app/Services/Model/Imp/SummitOrderService.php
  • tests/SummitOrderServiceTest.php

Comment thread app/Services/Model/Imp/SummitOrderService.php
…ault badge type

SummitTicketType::getBadgeType falls back to the summit default badge
type, so on the main fixture summit applyTo always builds a badge and a
badge-less ticket cannot be constructed. Build the scenario on the second
fixture summit, which has no badge types — the real case the badge
creation fix covers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
JpMaxMan added a commit that referenced this pull request Jul 3, 2026
…ator for export/import round-trip

Adopts badge_feature:{name} alongside extra_question:{name} uniformly
across the import template, the import (bare feature names still
accepted for legacy CSVs) and the ticket CSV export; import matches
questions by label since export emits labels; multi-value answer cells
now export with the import's '|' separator (per-id label resolution, so
comma-bearing labels stay intact). Closes the export->import round-trip
gap raised in #567 review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

➡️ https://OpenStackweb.github.io/summit-api/openapi/pr-567/

This page is automatically updated on each push to this PR.

SummitAttendeeTicketEmail's constructor requires the summit support
email; the second fixture summit never sets one, so the ticket
reassignment path threw during the badge-less import test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
JpMaxMan added a commit that referenced this pull request Jul 3, 2026
…ator for export/import round-trip

Adopts badge_feature:{name} alongside extra_question:{name} uniformly
across the import template, the import (bare feature names still
accepted for legacy CSVs) and the ticket CSV export; import matches
questions by label since export emits labels; multi-value answer cells
now export with the import's '|' separator (per-id label resolution, so
comma-bearing labels stay intact). Closes the export->import round-trip
gap raised in #567 review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

➡️ https://OpenStackweb.github.io/summit-api/openapi/pr-567/

This page is automatically updated on each push to this PR.

Comment thread app/Services/Model/Imp/SummitOrderService.php

@smarcet smarcet left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@JpMaxMan please review
#567 (comment)

…import

Duplicated CSV tokens (drag-fill artifacts, or the same choice spelled
as name/label/raw id) resolve to the same value id: they falsely
tripped the single-value guard on radio/combo questions and persisted
duplicated id strings for CheckBoxList, which also resisted later
correction under the answer-change lock. Adds both regression tests
from review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
JpMaxMan added a commit that referenced this pull request Jul 6, 2026
…ator for export/import round-trip

Adopts badge_feature:{name} alongside extra_question:{name} uniformly
across the import template, the import (bare feature names still
accepted for legacy CSVs) and the ticket CSV export; import matches
questions by label since export emits labels; multi-value answer cells
now export with the import's '|' separator (per-id label resolution, so
comma-bearing labels stay intact). Closes the export->import round-trip
gap raised in #567 review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

➡️ https://OpenStackweb.github.io/summit-api/openapi/pr-567/

This page is automatically updated on each push to this PR.

@smarcet smarcet self-requested a review July 6, 2026 18:15

@smarcet smarcet left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

@smarcet smarcet merged commit c1d5eef into main Jul 6, 2026
20 checks passed
smarcet pushed a commit that referenced this pull request Jul 7, 2026
…V import (#567)

* feat(registration): add order extra question answers to ticket CSV import

Adds extra_question:{question name} column support to the ticket data
import, template endpoint and OpenAPI docs. Answers are upserted through
the existing ExtraQuestionAnswerHolder persistence path; unknown
questions, order-scoped questions, disallowed questions, empty values
and locked answers are logged and skipped without failing the row.
List type questions accept value name/label/id ('|' separated for
CheckBoxList) and store value ids.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(registration): fix badge-features import test fixture assumptions

The import re-reads tickets with HINT_REFRESH, so it sees DB state: the
fixture's assigned tickets share one badge entity whose FK points at only
the last of them. Use an unassigned ticket, which gets its own
DB-consistent badge from SummitTicketType::applyTo.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(registration): normalize list answer ordering + guard import row on answer persistence

Per CodeRabbit review: sort list-question value ids so the same selection
in a different order is not treated as a changed answer, and catch
ValidationException from the extra-question persistence path so one bad
payload cannot strand the remaining import rows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(registration): review fixes for extra-question CSV import

Per review: process badge data before extra questions and flush + evict
the badge-features result cache so same-row badge/feature grants are
visible to the question permission gates; split the upsert into
resolve/merge helpers; ctype_digit for raw value ids; targeted cache
forget in tests. Adds regression tests for the same-row feature grant
(new + existing attendee), order-scoped skip, locked-answer skip, and
badge creation for badge-less tickets (folds in #568).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(registration): badge-less ticket requires a summit without a default badge type

SummitTicketType::getBadgeType falls back to the summit default badge
type, so on the main fixture summit applyTo always builds a badge and a
badge-less ticket cannot be constructed. Build the scenario on the second
fixture summit, which has no badge types — the real case the badge
creation fix covers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(registration): set support email on summit2 fixture path

SummitAttendeeTicketEmail's constructor requires the summit support
email; the second fixture summit never sets one, so the ticket
reassignment path threw during the badge-less import test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(registration): dedupe resolved list-question value ids on ticket import

Duplicated CSV tokens (drag-fill artifacts, or the same choice spelled
as name/label/raw id) resolve to the same value id: they falsely
tripped the single-value guard on radio/combo questions and persisted
duplicated id strings for CheckBoxList, which also resisted later
correction under the answer-change lock. Adds both regression tests
from review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
smarcet pushed a commit that referenced this pull request Jul 7, 2026
…ator for export/import round-trip

Adopts badge_feature:{name} alongside extra_question:{name} uniformly
across the import template, the import (bare feature names still
accepted for legacy CSVs) and the ticket CSV export; import matches
questions by label since export emits labels; multi-value answer cells
now export with the import's '|' separator (per-id label resolution, so
comma-bearing labels stay intact). Closes the export->import round-trip
gap raised in #567 review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

3 participants