Skip to content

Conversation

@rgraber
Copy link
Contributor

@rgraber rgraber commented Nov 17, 2025

🗒️ Checklist

  1. run linter locally
  2. update developer docs (API, README, inline, etc.), if any
  3. for user-facing doc changes create a Zulip thread at #Support Docs Updates, if any
  4. draft PR with a title <type>(<scope>)<!>: <title> DEV-1234
  5. assign yourself, tag PR: at least Front end and/or Back end or workflow
  6. fill in the template below and delete template comments
  7. review thyself: read the diff and repro the preview as written
  8. open PR & confirm that CI passes & request reviewers, if needed
  9. delete this section before merging

📣 Summary

Move advanced features API to its own endpoints rather than going through the asset detail API.

📖 Description

Previously we enabled/modified advanced features on an asset by PATCHing the /api/v2/assets/ endpoint with a params dictionary specifying the desired actions. This PR moves that functionality to a new endpoint, /api/v2/assets//advanced-features and updates it so we only configure one action per question per request, which is done via POST. Actions are also given unique IDs so they can be changed via PATCHes to /api/v2/assets/<asset_uid>/advanced-features/<action_uid>.

💭 Notes

This is incompatible with the front end and will cause an infinite refresh if you try to open a question for advanced editing. All testing should be done with curl.

Contains a significant refactor of advanced actions. Instead of a a large JSON in Asset.advanced_features, actions are broken out into individual QuestionAdvancedAction objects, where each object holds the configuration for one action for one question, in-keeping with the new question-level action parameters (some of which were previously asset-level).

Among other things, this required significant changes to migrate_advanced_features. That code and some other bits were moved into models.py to avoid circular imports.

After some consultation with the front end, the advanced_features field in the asset detail endpoint will have a link to the advanced features API endpoint, the same way we do for hooks and other nested objects.

Older assets are migrated on the fly to use the new QuestionAdvancedAction objects as needed. Attempting to POST to an unmigrated asset will result in an error since it would be confusing if a user tried to POST what they thought was a new action but got a Uniqueness error because the action already existed inside advanced_features and we migrated it before trying to create a new one.

The migration assumes we only want to configure actions for questions in Asset.known_cols, ie questions for which we know there exists at least one action response, like a transcription, translation, or QA answer. This does open us up to some risk if somehow the known_cols of an asset got out of sync, but it is preferable to having to parse the content dict of an Asset, which is very slow.

Another option was to use long_running_migrations, but ensuring correctness between migrating an old advanced_features dict and new actions possibly being created was very touchy, and still could have resulted in very slow requests while we locked the advanced features on an asset to do the migration. Similarly, we could have used a Django migration, but it would have been slow and have similar risks of conflict unless we actually disabled advanced features for a while.

This PR does not deal with how exactly the parameters for QualActions are migrated. That is being done in a separate PR.

👀 Preview steps

  1. On main: have an account and a project with an audio question
  2. On main: Add a few submissions with audio
  3. On main: Add an English transcription to one of the submissions
  4. Switch branches to the PR branch. Remember to restart the workers.
  5. Navigate to /api/v2/assets/<asset-uid>/advanced-features
  6. 🟢 [on PR] Result should include a transcript action for the audio question
  7. Enable Spanish translation: curl -X POST -H 'Authorization: Token <your token>' kf.kobo.local/api/v2/assets/<asset-uid>/advanced-features/ --json '{"action": "manual_translation", "question_xpath": <audio question xpath>, "params": [{"language": "es"}]}'
  8. Reload /api/v2/assets/<asset-uid>/advanced-features
  9. 🟢 Result should include a translation action
  10. Create a new asset (on the PR branch) with an audio question
  11. Add a submission
  12. Try to add a transcript: curl -X PATCH -H 'Authorization: Token <your token>' kf.kobo.local/api/v2/assets/<asset-uid>/data/<submission-uuid>/supplement/ --json '{"_version":"20250820", "<audio question xpath>": {"manual_transcription": {"language":"en", "value": "hello"}}}'
    13.🟢 Request should fail with 'Invalid question name' (because actions are not enabled on this question)
  13. Enable English transcription: curl -X POST -H 'Authorization: Token <your token>' kf.kobo.local/api/v2/assets/<asset-uid>/advanced-features/ --json '{"action": "manual_transcription", "question_xpath": <audio question xpath>, "params": [{"language": "en"}]}'
  14. Add a transcript: curl -X PATCH -H 'Authorization: Token <your token>' kf.kobo.local/api/v2/assets/<asset-uid>/data/<submission-uuid>/supplement/ --json '{"_version":"20250820", "<audio question xpath>": {"manual_transcription": {"language":"es", "value": "hello"}}}'
  15. Go the to data table
  16. 🟢 The new transcription should be shown in the columns

@rgraber rgraber changed the base branch from main to refactor-subsequences-2025 November 17, 2025 13:49
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