Skip to content
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4deca0f
temp: initial commit
rgraber Oct 22, 2025
87ba5a4
fixup!: stuff
rgraber Oct 22, 2025
8e82e61
fixup!: stuff
rgraber Oct 23, 2025
89f236f
fixup!: stuff
rgraber Oct 23, 2025
cc9ce01
fixup!: stuff
rgraber Oct 24, 2025
9b31b20
fixup!: stuff
rgraber Oct 24, 2025
ef8655a
fixup!: stuff
rgraber Oct 28, 2025
3c35f14
fixup!: messy but functional
rgraber Oct 28, 2025
335ff04
fixup!: cleaning
rgraber Oct 28, 2025
c638c62
fixup!: accidental change
rgraber Oct 28, 2025
73da17c
fixup!: accidental change
rgraber Oct 28, 2025
397d883
fixup!: new uuid
rgraber Oct 29, 2025
3fd8f59
fixup!: format
rgraber Oct 29, 2025
746766c
fixup!: stuff
rgraber Oct 29, 2025
263227f
fixup!: changes from review
rgraber Oct 31, 2025
8569da1
fixup!: action names
rgraber Nov 4, 2025
e2a43ed
fixup: accidental change
rgraber Nov 5, 2025
334d7f7
Merge remote-tracking branch 'origin/refactor-subsequences-2025' into…
jnm Nov 6, 2025
6adf76a
Merge branch 'refactor-subsequences-2025' into beccagraber/refactor-s…
rgraber Nov 12, 2025
6588150
fixup!: auto accept manual
rgraber Nov 13, 2025
c5ac2b7
Merge branch 'refactor-subsequences-2025' into beccagraber/refactor-s…
rajpatel24 Dec 10, 2025
9b07b1e
Add support for migrating qual data
rajpatel24 Dec 11, 2025
e408360
Merge branch 'refactor-subsequences-2025' into beccagraber/refactor-s…
noliveleger Dec 11, 2025
7e1865d
fix: add nested "_data" dict for NLP actions
noliveleger Dec 11, 2025
afb8395
Update readme file
rajpatel24 Dec 12, 2025
38b4b0c
Update README
rajpatel24 Dec 12, 2025
282d483
Update readme
rajpatel24 Dec 12, 2025
1eabf39
Update readme
rajpatel24 Dec 12, 2025
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
175 changes: 164 additions & 11 deletions kobo/apps/subsequences/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,31 @@ POST to `/api/v2/assets/{uid_asset}/advanced-features/` with:
}
```

**Example: Enable Qual action**

```json
{
"question_xpath": "text_question",
"action": "qual",
"params": [
{
"type": "qualSelectOne",
"uuid": "1a8b748b-f470-4c40-bc09-ce2b1197f503",
"labels": { "_default": "Was this a first-hand account?" },
"choices": [
{ "uuid": "3c7aacdc-8971-482a-9528-68e64730fc99", "labels": { "_default": "Yes" } },
{ "uuid": "7e31c6a5-5eac-464c-970c-62c383546a94", "labels": { "_default": "No" } }
]
},
{
"type": "qualInteger",
"uuid": "1a2c8eb0-e2ec-4b3c-942a-c1a5410c081a",
"labels": { "_default": "How many characters appear in the story?" }
}
]
}
```

---

### 2.2 Add Submission Supplement
Expand Down Expand Up @@ -149,6 +174,20 @@ PATCH /api/v2/assets/<asset_uid>/data/<submission_root_uuid>/supplement/
}
```

#### Example: Qual action

```json
{
"_version": "20250820",
"text_question": {
"qual": {
"uuid": "q_uuid",
"value": "sentiment_pos"
}
}
}
```

---

### 2.3 Sequence Diagram (End-to-End Flow)
Expand Down Expand Up @@ -285,6 +324,31 @@ It describes the configuration stored on a `QuestionAdvancedFeature` when an act
}
```

**Example: enabling Qual**

```json
{
"question_xpath": "text_question",
"action": "qual",
"params": [
{
"type": "qualSelectOne",
"uuid": "1a8b748b-f470-4c40-bc09-ce2b1197f503",
"labels": { "_default": "Was this a first-hand account?" },
"choices": [
{ "uuid": "3c7aacdc-8971-482a-9528-68e64730fc99", "labels": { "_default": "Yes" } },
{ "uuid": "7e31c6a5-5eac-464c-970c-62c383546a94", "labels": { "_default": "No" } }
]
},
{
"type": "qualInteger",
"uuid": "1a2c8eb0-e2ec-4b3c-942a-c1a5410c081a",
"labels": { "_default": "How many characters appear in the story?" }
}
]
}
```

---

### 3.2 `data_schema`
Expand Down Expand Up @@ -312,6 +376,14 @@ Each action has its own expected format:
{ "language": "en", "value": null }
```

- **Qual**
```json
{
"uuid": "q_uuid",
"value": "sentiment_pos"
}
```

---

### 3.3 `external_data_schema`
Expand Down Expand Up @@ -348,28 +420,61 @@ The structure is the same for both manual and automatic actions:

- Metadata about the action itself (`_dateCreated`, `_dateModified`).
- A list of versions under `_versions`, each containing:
- The properties defined by `data_schema` (manual) or `external_data_schema` (automatic).
- A nested `_data` object with properties from either `data_schema` (manual) or `external_data_schema` (automatic).
- Audit fields (`_dateCreated`, `_dateAccepted`, `_uuid`).

**Generic Example**
**Manual Action Example**

```json
{
"_dateCreated": "2025-08-21T20:55:42Z",
"_dateModified": "2025-08-21T20:57:28Z",
"_versions": [
{
"_data": {
"language": "en",
"value": "My manual transcript"
},
"_dateCreated": "2025-08-21T20:57:28Z",
"_dateAccepted": "2025-08-21T20:57:28Z",
"_uuid": "4dcf9c9f-e503-4e5c-81f5-74250b295001"
},
{
"_data": {
"language": "en",
"value": "My previous manual transcript"
},
"_dateCreated": "2025-08-21T20:55:42Z",
"_dateAccepted": "2025-08-21T20:55:42Z",
"_uuid": "850e6359-50e8-4252-9895-e9669a27b1ea"
}
]
}
```

**Automatic Action Example**

```json
{
"_dateCreated": "2025-08-21T20:55:42Z",
"_dateModified": "2025-08-21T20:57:28Z",
"_versions": [
{
"language": "en",
"value": "My automatic result",
"status": "complete",
"_data": {
"language": "en",
"value": "My automatic result",
"status": "complete"
},
"_dateCreated": "2025-08-21T20:57:28Z",
"_dateAccepted": "2025-08-21T20:57:28Z",
"_uuid": "4dcf9c9f-e503-4e5c-81f5-74250b295001"
},
{
"language": "en",
"value": "Previous revision",
"status": "complete",
"_data": {
"language": "en",
"value": "My previous automatic result",
"status": "complete"
},
"_dateCreated": "2025-08-21T20:55:42Z",
"_dateAccepted": "2025-08-21T20:55:42Z",
"_uuid": "850e6359-50e8-4252-9895-e9669a27b1ea"
Expand All @@ -378,6 +483,52 @@ The structure is the same for both manual and automatic actions:
}
```

**Qual Action Example**

```json
{
"q1_uuid_here": {
"_dateCreated": "2025-08-21T20:55:42Z",
"_dateModified": "2025-08-21T20:57:28Z",
"_versions": [
{
"_data": {
"uuid": "q1_uuid_here",
"value": "sentiment_pos"
},
"_dateCreated": "2025-08-21T20:57:28Z",
"_dateAccepted": "2025-08-21T20:57:28Z",
"_uuid": "4dcf9c9f-e503-4e5c-81f5-74250b295001"
},
{
"_data": {
"uuid": "q1_uuid_here",
"value": "sentiment_neg"
},
"_dateCreated": "2025-08-21T20:55:42Z",
"_dateAccepted": "2025-08-21T20:55:42Z",
"_uuid": "850e6359-50e8-4252-9895-e9669a27b1ea"
}
]
},
"q2_uuid_here": {
"_dateCreated": "2025-08-21T20:55:42Z",
"_dateModified": "2025-08-21T20:57:28Z",
"_versions": [
{
"_data": {
"uuid": "q2_uuid_here",
"value": 8
},
"_dateCreated": "2025-08-21T20:57:28Z",
"_dateAccepted": "2025-08-21T20:57:28Z",
"_uuid": "91ab5f30-0f73-4e2e-b91f-8ad2f67a4729"
}
]
}
}
```

> For manual actions, the inner version objects correspond to `data_schema`.
>
> For automatic actions, they correspond to `external_data_schema`.
Expand All @@ -398,9 +549,11 @@ In this case, a `_dependency` property is added to the persisted JSON.
"_dateModified": "2025-09-01T12:17:28Z",
"_versions": [
{
"language": "fr",
"value": "Mon audio a été traduit automatiquement",
"status": "complete",
"_data": {
"language": "fr",
"value": "Mon audio a été traduit automatiquement",
"status": "complete"
},
"_dateCreated": "2025-09-01T12:17:28Z",
"_uuid": "91ab5f30-0f73-4e2e-b91f-8ad2f67a4729",
"_dependency": {
Expand Down
14 changes: 10 additions & 4 deletions kobo/apps/subsequences/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ def __repr__(self):
@staticmethod
def revise_data(asset: 'kpi.Asset', submission: dict, incoming_data: dict) -> dict:

from .utils.versioning import migrate_submission_supplementals

if not asset.advanced_features_set.exists():
raise InvalidAction

Expand All @@ -46,8 +48,10 @@ def revise_data(asset: 'kpi.Asset', submission: dict, incoming_data: dict) -> di
raise NotImplementedError

if schema_version != SCHEMA_VERSIONS[0]:
# TODO: migrate from old per-submission schema
raise NotImplementedError
migrated_data = migrate_submission_supplementals(incoming_data)
if migrated_data is None:
raise InvalidAction
incoming_data = migrated_data

submission_uuid = remove_uuid_prefix(submission[SUBMISSION_UUID_FIELD]) # constant?
supplemental_data = SubmissionExtras.objects.get_or_create(
Expand Down Expand Up @@ -153,8 +157,10 @@ def retrieve_data(
raise NotImplementedError

if schema_version != SCHEMA_VERSIONS[0]:
# TODO: migrate from old per-submission schema
raise NotImplementedError
migrated_data = migrate_submission_supplementals(supplemental_data)
if migrated_data is None:
raise InvalidAction
supplemental_data = migrated_data

retrieved_supplemental_data = {}
data_for_output = {}
Expand Down
2 changes: 1 addition & 1 deletion kobo/apps/subsequences/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@


def validate_submission_supplement(asset: 'kpi.models.Asset', supplement: dict):
jsonschema.validate(get_submission_supplement_schema(asset), supplement)
jsonschema.validate(supplement, get_submission_supplement_schema(asset))


def get_submission_supplement_schema(asset: 'kpi.models.Asset') -> dict:
Expand Down
Loading
Loading