Skip to content

Commit 9c62bfe

Browse files
committed
feat(LAB-4125): handle both workflow in the export
1 parent 247007a commit 9c62bfe

File tree

2 files changed

+504
-25
lines changed

2 files changed

+504
-25
lines changed

src/kili/domain_api/exports.py

Lines changed: 121 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from kili.domain.asset.asset import AssetStatus, StatusInStep
99
from kili.domain.issue import IssueStatus, IssueType
1010
from kili.domain.label import LabelType
11+
from kili.domain.project import ProjectId
1112
from kili.domain.types import ListOrTuple
1213
from kili.domain_api.base import DomainNamespace
1314
from kili.domain_api.namespace_utils import get_available_methods
@@ -63,6 +64,70 @@ def __init__(self, client, gateway):
6364
super().__init__(client, gateway, "exports")
6465
self.raw = self.kili
6566

67+
def _get_workflow_version(self, project_id: str) -> Optional[str]:
68+
"""Get the workflow version for a project.
69+
70+
Args:
71+
project_id: The project identifier
72+
73+
Returns:
74+
The workflow version ("V1" or "V2") or None if project not found
75+
"""
76+
project = self._gateway.get_project(
77+
project_id=ProjectId(project_id), fields=["workflowVersion"]
78+
)
79+
80+
if not project:
81+
return None
82+
83+
return project.get("workflowVersion")
84+
85+
def _get_optimal_export_type(self, workflow_version: Optional[str]) -> ExportType:
86+
"""Determine the optimal export type based on project workflow configuration.
87+
88+
For non-multi-review projects (Workflow V1), uses "latest"
89+
which queries the singular latestLabel field for better performance.
90+
91+
For multi-review projects (Workflow V2), uses "latest_from_last_step"
92+
which queries the plural latestLabels field to support multiple annotators.
93+
94+
Args:
95+
workflow_version: The workflow version ("V1", "V2", or None)
96+
97+
Returns:
98+
The optimal export type for this project
99+
"""
100+
if workflow_version == "V2":
101+
return "latest_from_last_step"
102+
return "latest"
103+
104+
def _validate_export_type_compatibility(
105+
self, workflow_version: Optional[str], export_type: ExportType
106+
) -> None:
107+
"""Validate that the export type is compatible with the project workflow.
108+
109+
Workflow V1 projects cannot use export types that rely on multi-step workflows:
110+
- latest_from_last_step: Uses latestLabels field (V2 multi-review feature)
111+
- latest_from_all_steps: Uses labels with isLastForStep (V2 multi-step feature)
112+
113+
Args:
114+
workflow_version: The workflow version ("V1", "V2", or None)
115+
export_type: The requested export type
116+
117+
Raises:
118+
ValueError: If the export type is incompatible with the project workflow version
119+
"""
120+
if export_type not in ("latest_from_last_step", "latest_from_all_steps"):
121+
return
122+
123+
if workflow_version == "V1":
124+
raise ValueError(
125+
f"Export type '{export_type}' is not compatible with Workflow V1 projects. "
126+
f"Workflow V1 projects should use export_type='latest' or 'normal'. "
127+
f"The '{export_type}' export type is designed for Workflow V2 projects "
128+
f"with multi-step workflows."
129+
)
130+
66131
@deprecated(
67132
"'exports' is a namespace, not a callable method. "
68133
"Use kili.exports.kili() or other available methods instead."
@@ -93,7 +158,7 @@ def kili(
93158
include_sent_back_labels: Optional[bool] = None,
94159
label_type_in: Optional[list[LabelType]] = None,
95160
single_file: Optional[bool] = False,
96-
export_type: ExportType = "latest_from_last_step",
161+
export_type: Optional[ExportType] = None,
97162
):
98163
"""Export project labels in Kili native format.
99164
@@ -114,14 +179,17 @@ def kili(
114179
whose type belongs to that list.
115180
By default, only `DEFAULT` and `REVIEW` labels are exported.
116181
single_file: If True, all labels are exported in a single JSON file.
117-
export_type: Type of export. Options are:
182+
export_type: Type of export. If not specified, automatically selects the optimal type:
183+
- For Workflow V1 projects: uses `"latest"` (single label per asset)
184+
- For Workflow V2 projects: uses `"latest_from_last_step"` (supports multiple annotators)
185+
186+
Available options:
118187
- `"latest_from_last_step"`: exports the latest labels from the last workflow step
119188
(uses `latestLabels` field, supports multiple annotators).
120189
- `"latest_from_all_steps"`: exports latest labels from all workflow steps
121190
- `"latest"`: exports the latest label for each asset
122191
(deprecated, use `"latest_from_last_step"` instead).
123192
- `"normal"`: exports all labels for each asset.
124-
Defaults to `"latest_from_last_step"`.
125193
126194
Returns:
127195
Export information or None if export failed.
@@ -151,7 +219,7 @@ def coco(
151219
include_sent_back_labels: Optional[bool] = None,
152220
label_type_in: Optional[list[LabelType]] = None,
153221
layout: SplitOption = "split",
154-
export_type: ExportType = "latest_from_last_step",
222+
export_type: Optional[ExportType] = None,
155223
):
156224
"""Export project labels in COCO format.
157225
@@ -175,14 +243,17 @@ def coco(
175243
By default, only `DEFAULT` and `REVIEW` labels are exported.
176244
layout: Layout of the exported files. "split" means there is one folder
177245
per job, "merged" that there is one folder with every labels.
178-
export_type: Type of export. Options are:
246+
export_type: Type of export. If not specified, automatically selects the optimal type:
247+
- For Workflow V1 projects: uses `"latest"` (single label per asset)
248+
- For Workflow V2 projects: uses `"latest_from_last_step"` (supports multiple annotators)
249+
250+
Available options:
179251
- `"latest_from_last_step"`: exports the latest labels from the last workflow step
180252
(uses `latestLabels` field, supports multiple annotators).
181253
- `"latest_from_all_steps"`: exports latest labels from all workflow steps
182254
- `"latest"`: exports the latest label for each asset
183255
(deprecated, use `"latest_from_last_step"` instead).
184256
- `"normal"`: exports all labels for each asset.
185-
Defaults to `"latest_from_last_step"`.
186257
187258
Returns:
188259
Export information or None if export failed.
@@ -211,7 +282,7 @@ def yolo_v4(
211282
filter: Optional[ExportAssetFilter] = None,
212283
include_sent_back_labels: Optional[bool] = None,
213284
label_type_in: Optional[list[LabelType]] = None,
214-
export_type: ExportType = "latest_from_last_step",
285+
export_type: Optional[ExportType] = None,
215286
):
216287
"""Export project labels in YOLO v4 format.
217288
@@ -234,14 +305,17 @@ def yolo_v4(
234305
By default, only `DEFAULT` and `REVIEW` labels are exported.
235306
include_sent_back_labels: If True, the export will include the labels that
236307
have been sent back.
237-
export_type: Type of export. Options are:
308+
export_type: Type of export. If not specified, automatically selects the optimal type:
309+
- For Workflow V1 projects: uses `"latest"` (single label per asset)
310+
- For Workflow V2 projects: uses `"latest_from_last_step"` (supports multiple annotators)
311+
312+
Available options:
238313
- `"latest_from_last_step"`: exports the latest labels from the last workflow step
239314
(uses `latestLabels` field, supports multiple annotators).
240315
- `"latest_from_all_steps"`: exports latest labels from all workflow steps
241316
- `"latest"`: exports the latest label for each asset
242317
(deprecated, use `"latest_from_last_step"` instead).
243318
- `"normal"`: exports all labels for each asset.
244-
Defaults to `"latest_from_last_step"`.
245319
246320
Returns:
247321
Export information or None if export failed.
@@ -269,7 +343,7 @@ def yolo_v5(
269343
filter: Optional[ExportAssetFilter] = None,
270344
include_sent_back_labels: Optional[bool] = None,
271345
label_type_in: Optional[list[LabelType]] = None,
272-
export_type: ExportType = "latest_from_last_step",
346+
export_type: Optional[ExportType] = None,
273347
):
274348
"""Export project labels in YOLO v5 format.
275349
@@ -292,14 +366,17 @@ def yolo_v5(
292366
By default, only `DEFAULT` and `REVIEW` labels are exported.
293367
include_sent_back_labels: If True, the export will include the labels that
294368
have been sent back.
295-
export_type: Type of export. Options are:
369+
export_type: Type of export. If not specified, automatically selects the optimal type:
370+
- For Workflow V1 projects: uses `"latest"` (single label per asset)
371+
- For Workflow V2 projects: uses `"latest_from_last_step"` (supports multiple annotators)
372+
373+
Available options:
296374
- `"latest_from_last_step"`: exports the latest labels from the last workflow step
297375
(uses `latestLabels` field, supports multiple annotators).
298376
- `"latest_from_all_steps"`: exports latest labels from all workflow steps
299377
- `"latest"`: exports the latest label for each asset
300378
(deprecated, use `"latest_from_last_step"` instead).
301379
- `"normal"`: exports all labels for each asset.
302-
Defaults to `"latest_from_last_step"`.
303380
304381
Returns:
305382
Export information or None if export failed.
@@ -327,7 +404,7 @@ def yolo_v7(
327404
filter: Optional[ExportAssetFilter] = None,
328405
include_sent_back_labels: Optional[bool] = None,
329406
label_type_in: Optional[list[LabelType]] = None,
330-
export_type: ExportType = "latest_from_last_step",
407+
export_type: Optional[ExportType] = None,
331408
):
332409
"""Export project labels in YOLO v7 format.
333410
@@ -350,14 +427,17 @@ def yolo_v7(
350427
By default, only `DEFAULT` and `REVIEW` labels are exported.
351428
include_sent_back_labels: If True, the export will include the labels that
352429
have been sent back.
353-
export_type: Type of export. Options are:
430+
export_type: Type of export. If not specified, automatically selects the optimal type:
431+
- For Workflow V1 projects: uses `"latest"` (single label per asset)
432+
- For Workflow V2 projects: uses `"latest_from_last_step"` (supports multiple annotators)
433+
434+
Available options:
354435
- `"latest_from_last_step"`: exports the latest labels from the last workflow step
355436
(uses `latestLabels` field, supports multiple annotators).
356437
- `"latest_from_all_steps"`: exports latest labels from all workflow steps
357438
- `"latest"`: exports the latest label for each asset
358439
(deprecated, use `"latest_from_last_step"` instead).
359440
- `"normal"`: exports all labels for each asset.
360-
Defaults to `"latest_from_last_step"`.
361441
362442
Returns:
363443
Export information or None if export failed.
@@ -385,7 +465,7 @@ def yolo_v8(
385465
filter: Optional[ExportAssetFilter] = None,
386466
include_sent_back_labels: Optional[bool] = None,
387467
label_type_in: Optional[list[LabelType]] = None,
388-
export_type: ExportType = "latest_from_last_step",
468+
export_type: Optional[ExportType] = None,
389469
):
390470
"""Export project labels in YOLO v8 format.
391471
@@ -408,14 +488,17 @@ def yolo_v8(
408488
By default, only `DEFAULT` and `REVIEW` labels are exported.
409489
include_sent_back_labels: If True, the export will include the labels that
410490
have been sent back.
411-
export_type: Type of export. Options are:
491+
export_type: Type of export. If not specified, automatically selects the optimal type:
492+
- For Workflow V1 projects: uses `"latest"` (single label per asset)
493+
- For Workflow V2 projects: uses `"latest_from_last_step"` (supports multiple annotators)
494+
495+
Available options:
412496
- `"latest_from_last_step"`: exports the latest labels from the last workflow step
413497
(uses `latestLabels` field, supports multiple annotators).
414498
- `"latest_from_all_steps"`: exports latest labels from all workflow steps
415499
- `"latest"`: exports the latest label for each asset
416500
(deprecated, use `"latest_from_last_step"` instead).
417501
- `"normal"`: exports all labels for each asset.
418-
Defaults to `"latest_from_last_step"`.
419502
420503
Returns:
421504
Export information or None if export failed.
@@ -442,7 +525,7 @@ def pascal_voc(
442525
filter: Optional[ExportAssetFilter] = None,
443526
include_sent_back_labels: Optional[bool] = None,
444527
label_type_in: Optional[list[LabelType]] = None,
445-
export_type: ExportType = "latest_from_last_step",
528+
export_type: Optional[ExportType] = None,
446529
):
447530
"""Export project labels in Pascal VOC format.
448531
@@ -463,14 +546,17 @@ def pascal_voc(
463546
By default, only `DEFAULT` and `REVIEW` labels are exported.
464547
include_sent_back_labels: If True, the export will include the labels that
465548
have been sent back.
466-
export_type: Type of export. Options are:
549+
export_type: Type of export. If not specified, automatically selects the optimal type:
550+
- For Workflow V1 projects: uses `"latest"` (single label per asset)
551+
- For Workflow V2 projects: uses `"latest_from_last_step"` (supports multiple annotators)
552+
553+
Available options:
467554
- `"latest_from_last_step"`: exports the latest labels from the last workflow step
468555
(uses `latestLabels` field, supports multiple annotators).
469556
- `"latest_from_all_steps"`: exports latest labels from all workflow steps
470557
- `"latest"`: exports the latest label for each asset
471558
(deprecated, use `"latest_from_last_step"` instead).
472559
- `"normal"`: exports all labels for each asset.
473-
Defaults to `"latest_from_last_step"`.
474560
475561
Returns:
476562
Export information or None if export failed.
@@ -497,7 +583,7 @@ def geojson(
497583
filter: Optional[ExportAssetFilter] = None,
498584
include_sent_back_labels: Optional[bool] = None,
499585
label_type_in: Optional[list[LabelType]] = None,
500-
export_type: ExportType = "latest_from_last_step",
586+
export_type: Optional[ExportType] = None,
501587
):
502588
"""Export project labels in GeoJSON format.
503589
@@ -518,14 +604,17 @@ def geojson(
518604
By default, only `DEFAULT` and `REVIEW` labels are exported.
519605
include_sent_back_labels: If True, the export will include the labels that
520606
have been sent back.
521-
export_type: Type of export. Options are:
607+
export_type: Type of export. If not specified, automatically selects the optimal type:
608+
- For Workflow V1 projects: uses `"latest"` (single label per asset)
609+
- For Workflow V2 projects: uses `"latest_from_last_step"` (supports multiple annotators)
610+
611+
Available options:
522612
- `"latest_from_last_step"`: exports the latest labels from the last workflow step
523613
(uses `latestLabels` field, supports multiple annotators).
524614
- `"latest_from_all_steps"`: exports latest labels from all workflow steps
525615
- `"latest"`: exports the latest label for each asset
526616
(deprecated, use `"latest_from_last_step"` instead).
527617
- `"normal"`: exports all labels for each asset.
528-
Defaults to `"latest_from_last_step"`.
529618
530619
Returns:
531620
Export information or None if export failed.
@@ -610,7 +699,7 @@ def _export(
610699
project_id: str,
611700
single_file: bool = False,
612701
with_assets: Optional[bool] = True,
613-
export_type: ExportType = "latest_from_last_step",
702+
export_type: Optional[ExportType] = None,
614703
) -> Optional[list[dict[str, Union[list[str], str]]]]:
615704
"""Export the project labels with the requested format into the requested output path.
616705
@@ -657,6 +746,13 @@ def _export(
657746
... filter={"external_id_contains": ["batch_1"]}
658747
... )
659748
"""
749+
workflow_version = self._get_workflow_version(project_id)
750+
751+
if export_type is None:
752+
export_type = self._get_optimal_export_type(workflow_version)
753+
else:
754+
self._validate_export_type_compatibility(workflow_version, export_type)
755+
660756
asset_filter_kwargs = dict(filter) if filter else {}
661757
return self._client.export_labels(
662758
project_id=project_id,

0 commit comments

Comments
 (0)