x4d-devkit 0.48.0rc9__tar.gz → 0.49.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/PKG-INFO +1 -1
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/pyproject.toml +1 -1
- x4d_devkit-0.49.0/tests/test_project_discovery.py +264 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_sdk_api.py +1 -1
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/__init__.py +2 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/projects.py +23 -2
- x4d_devkit-0.49.0/x4d_devkit/project_discovery.py +361 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit.egg-info/PKG-INFO +1 -1
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit.egg-info/SOURCES.txt +2 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/LICENSE +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/README.md +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/setup.cfg +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_accounts.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_annotation_contract.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_annotation_reference.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_assets_client.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_boundary.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_box.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_build_info.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_category_audit.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_clean_basemap.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_clean_basemap_client.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_cli_auth.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_client_json_contract.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_client_projects.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_clip_archives.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_contract_validation.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_coordinate_frames.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_current_state.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_current_state_identity_consumers_example.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_dataset_contract.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_dataset_identity.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_detection_eval.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_evaluate.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_export_bundle.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_image_clip.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_image_export.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_image_geometry.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_image_preannotation.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_image_reference.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_image_review.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_image_schema_upgrade.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_image_work_session.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_inference_input.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_inference_schemas.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_inference_service.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_label_schemas.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_lidar_materialization.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_loader.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_matching.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_metrics.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_models.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_platform_pose.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_preannotation_client.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_reconstruction_client.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_release_gate.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_roadmark_preview.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_semantic_scene_authoring.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_semantic_scene_client.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_semantic_scene_contract.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_semantic_scene_independent.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_semantic_scene_native_clip.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_source_attributes.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_time_contract.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_token.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_tracking_eval.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_training_identity.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_transform.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_transform_chain.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_validation.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_vector_map.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_vector_map_geometry.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_vector_map_session.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_work_session.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/tests/test_work_session_validation_reuse.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/__init__.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/_image_contract.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/accounts.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/annotation_contract.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/annotation_reference.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/audit.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/build_info.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/clean_basemap.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/cli.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/_origin.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/accounts.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/annotation_reference.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/assets.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/bags.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/capabilities.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/checkpoints.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/clean_basemap.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/client.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/clip_work_sessions.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/clips.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/dataset_identity.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/image_clips.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/image_exports.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/image_preannotations.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/image_review.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/image_schemas.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/image_work_sessions.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/label_schemas.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/preannotation.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/reconstruction.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/semantic_scene.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/semantic_scene_authoring.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/services.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/client/test_sets.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/consumer_identity.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/coordinate_contract.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/core/__init__.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/core/loader.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/core/models.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/core/token.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/core/transform.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/current_state.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/dataset_contract.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/dataset_identity.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/eval/__init__.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/eval/api.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/eval/box.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/eval/detection.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/eval/matching.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/eval/metrics.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/eval/tracking.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/export_bundle.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/image_clip.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/image_export.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/image_geometry.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/image_preannotation.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/image_reference.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/image_review.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/image_schema.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/image_schema_upgrade.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/image_work_session.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/inference/__init__.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/inference/input.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/inference/models.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/inference/schemas.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/inference/service.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/inference/tasks.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/json_contract.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/label_schema.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/lidar_materialization.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/materialize.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/roadmark_preview.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/semantic_scene_authoring.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/semantic_scene_contract.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/time_contract.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/training_identity.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/validation/__init__.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/validation/contract.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/validation/platform_pose.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/validation/report.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/validation/validator.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/vector_map.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/vector_map_geometry.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit/work_session.py +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit.egg-info/dependency_links.txt +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit.egg-info/entry_points.txt +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit.egg-info/requires.txt +0 -0
- {x4d_devkit-0.48.0rc9 → x4d_devkit-0.49.0}/x4d_devkit.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "x4d-devkit"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.49.0"
|
|
8
8
|
description = "Official X-4D external SDK and CLI for datasets, platform APIs, training bridges, and automation"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from x4d_devkit.client.projects import ProjectsAPI
|
|
8
|
+
from x4d_devkit.project_discovery import (
|
|
9
|
+
PROJECT_DISCOVERY_VERSION,
|
|
10
|
+
ProjectDiscoveryError,
|
|
11
|
+
project_discovery_query,
|
|
12
|
+
validate_project_discovery_page,
|
|
13
|
+
validate_project_image_task,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def discovery_page(task_id="a_new_task", *, revision=None, page=1):
|
|
18
|
+
return {
|
|
19
|
+
"items": [
|
|
20
|
+
{
|
|
21
|
+
"id": 7,
|
|
22
|
+
"name": "Project",
|
|
23
|
+
"project_type": "2d",
|
|
24
|
+
"image_task": {
|
|
25
|
+
"status": "registered",
|
|
26
|
+
"id": task_id,
|
|
27
|
+
"revision": revision or 1,
|
|
28
|
+
"title": "Registered title",
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
if page <= 2
|
|
33
|
+
else [],
|
|
34
|
+
"total": 1 if revision else 2,
|
|
35
|
+
"page": page,
|
|
36
|
+
"page_size": 1,
|
|
37
|
+
"discovery": {
|
|
38
|
+
"contract_version": PROJECT_DISCOVERY_VERSION,
|
|
39
|
+
"applied_filters": {
|
|
40
|
+
"image_task_id": task_id,
|
|
41
|
+
"image_task_revision": revision,
|
|
42
|
+
},
|
|
43
|
+
"facets": {
|
|
44
|
+
"image_task": {
|
|
45
|
+
"options": [
|
|
46
|
+
{
|
|
47
|
+
"id": task_id,
|
|
48
|
+
"title": "Registered title",
|
|
49
|
+
"project_count": 2,
|
|
50
|
+
"revisions": [
|
|
51
|
+
{
|
|
52
|
+
"revision": 1,
|
|
53
|
+
"title": "Registered title",
|
|
54
|
+
"project_count": 1,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"revision": 2,
|
|
58
|
+
"title": "Registered title",
|
|
59
|
+
"project_count": 1,
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"unresolved_project_count": 1,
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class Client:
|
|
72
|
+
def __init__(self, value):
|
|
73
|
+
self.value, self.calls = value, []
|
|
74
|
+
|
|
75
|
+
def get(self, path, params=None):
|
|
76
|
+
self.calls.append((path, params))
|
|
77
|
+
return deepcopy(self.value)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_new_task_needs_no_business_enum_and_retains_old_call_shape():
|
|
81
|
+
client = Client(discovery_page("new_after_sdk_release"))
|
|
82
|
+
result = ProjectsAPI(client).list(
|
|
83
|
+
1,
|
|
84
|
+
1,
|
|
85
|
+
None,
|
|
86
|
+
"2d",
|
|
87
|
+
image_task_id="new_after_sdk_release",
|
|
88
|
+
include_facets=["image_task", "image_task"],
|
|
89
|
+
)
|
|
90
|
+
assert result["items"][0]["image_task"]["id"] == "new_after_sdk_release"
|
|
91
|
+
assert client.calls == [
|
|
92
|
+
(
|
|
93
|
+
"/projects",
|
|
94
|
+
{
|
|
95
|
+
"page": 1,
|
|
96
|
+
"page_size": 1,
|
|
97
|
+
"project_type": "2d",
|
|
98
|
+
"image_task_id": "new_after_sdk_release",
|
|
99
|
+
"include_facets": ["image_task"],
|
|
100
|
+
},
|
|
101
|
+
)
|
|
102
|
+
]
|
|
103
|
+
old = {"items": [], "total": 0, "page": 1, "page_size": 50}
|
|
104
|
+
client = Client(old)
|
|
105
|
+
assert ProjectsAPI(client).list() == old
|
|
106
|
+
assert client.calls == [("/projects", {"page": 1, "page_size": 50})]
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@pytest.mark.parametrize(
|
|
110
|
+
"kwargs",
|
|
111
|
+
[
|
|
112
|
+
{"image_task_id": "task"},
|
|
113
|
+
{"project_type": "3d", "include_facets": ["image_task"]},
|
|
114
|
+
{"project_type": "2d", "image_task_revision": 1},
|
|
115
|
+
{"project_type": "2d", "image_task_id": " task"},
|
|
116
|
+
{"project_type": "2d", "image_task_id": ""},
|
|
117
|
+
{"project_type": "2d", "image_task_id": "x" * 1025},
|
|
118
|
+
{"project_type": "2d", "image_task_id": "task", "image_task_revision": True},
|
|
119
|
+
{"project_type": "2d", "image_task_id": "task", "image_task_revision": 0},
|
|
120
|
+
{"project_type": "2d", "include_facets": "image_task"},
|
|
121
|
+
{"project_type": "2d", "include_facets": [""]},
|
|
122
|
+
{"project_type": "2d", "include_facets": ["private_json"]},
|
|
123
|
+
],
|
|
124
|
+
)
|
|
125
|
+
def test_bad_query_is_rejected_before_transport(kwargs):
|
|
126
|
+
client = Client({})
|
|
127
|
+
with pytest.raises(ProjectDiscoveryError) as exc:
|
|
128
|
+
ProjectsAPI(client).list(**kwargs)
|
|
129
|
+
assert exc.value.code == "invalid_project_discovery_query"
|
|
130
|
+
assert client.calls == []
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def test_exact_revision_and_out_of_range_pages():
|
|
134
|
+
assert (
|
|
135
|
+
ProjectsAPI(Client(discovery_page(revision=2))).list(
|
|
136
|
+
page_size=1,
|
|
137
|
+
project_type="2d",
|
|
138
|
+
image_task_id="a_new_task",
|
|
139
|
+
image_task_revision=2,
|
|
140
|
+
)["total"]
|
|
141
|
+
== 1
|
|
142
|
+
)
|
|
143
|
+
assert (
|
|
144
|
+
ProjectsAPI(Client(discovery_page(page=3))).list(
|
|
145
|
+
page=3, page_size=1, project_type="2d", image_task_id="a_new_task"
|
|
146
|
+
)["items"]
|
|
147
|
+
== []
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def test_facet_scope_ignores_current_task_filter_and_counts_unresolved():
|
|
152
|
+
value = discovery_page()
|
|
153
|
+
value["discovery"]["applied_filters"]["image_task_id"] = None
|
|
154
|
+
value["total"], value["page_size"] = 3, 3
|
|
155
|
+
value["items"].extend(
|
|
156
|
+
[
|
|
157
|
+
{
|
|
158
|
+
"id": 8,
|
|
159
|
+
"name": "Another",
|
|
160
|
+
"project_type": "2d",
|
|
161
|
+
"image_task": {
|
|
162
|
+
"status": "registered",
|
|
163
|
+
"id": "a_new_task",
|
|
164
|
+
"revision": 2,
|
|
165
|
+
"title": "Registered title",
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"id": 9,
|
|
170
|
+
"name": "Missing",
|
|
171
|
+
"project_type": "2d",
|
|
172
|
+
"image_task": {
|
|
173
|
+
"status": "missing",
|
|
174
|
+
"id": None,
|
|
175
|
+
"revision": None,
|
|
176
|
+
"title": None,
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
]
|
|
180
|
+
)
|
|
181
|
+
assert (
|
|
182
|
+
ProjectsAPI(Client(value)).list(
|
|
183
|
+
page_size=3, project_type="2d", include_facets=["image_task"]
|
|
184
|
+
)["total"]
|
|
185
|
+
== 3
|
|
186
|
+
)
|
|
187
|
+
value["items"], value["total"] = [], 0
|
|
188
|
+
value["discovery"]["applied_filters"]["image_task_id"] = "not_registered"
|
|
189
|
+
assert (
|
|
190
|
+
ProjectsAPI(Client(value)).list(
|
|
191
|
+
page_size=3,
|
|
192
|
+
project_type="2d",
|
|
193
|
+
image_task_id="not_registered",
|
|
194
|
+
include_facets=["image_task"],
|
|
195
|
+
)["items"]
|
|
196
|
+
== []
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@pytest.mark.parametrize(
|
|
201
|
+
"mutation",
|
|
202
|
+
[
|
|
203
|
+
lambda value: value.pop("discovery"),
|
|
204
|
+
lambda value: value["discovery"].update(contract_version="unknown"),
|
|
205
|
+
lambda value: value["discovery"]["applied_filters"].update(image_task_id=None),
|
|
206
|
+
lambda value: value["discovery"]["applied_filters"].update(
|
|
207
|
+
image_task_revision=True
|
|
208
|
+
),
|
|
209
|
+
lambda value: value["discovery"].update(facets={}),
|
|
210
|
+
lambda value: value.update(page=2),
|
|
211
|
+
lambda value: value.update(total=True),
|
|
212
|
+
lambda value: value.update(total=3),
|
|
213
|
+
lambda value: value.update(items=[]),
|
|
214
|
+
lambda value: value["items"][0].update(project_type="3d"),
|
|
215
|
+
lambda value: value["items"][0]["image_task"].update(id="other"),
|
|
216
|
+
lambda value: value["items"][0]["image_task"].update(revision=4),
|
|
217
|
+
lambda value: value["items"][0]["image_task"].update(title="invented"),
|
|
218
|
+
lambda value: value["discovery"]["facets"]["image_task"]["options"][0].update(
|
|
219
|
+
project_count=3
|
|
220
|
+
),
|
|
221
|
+
lambda value: value["discovery"]["facets"]["image_task"]["options"][0][
|
|
222
|
+
"revisions"
|
|
223
|
+
].reverse(),
|
|
224
|
+
lambda value: value["discovery"]["facets"]["image_task"].update(
|
|
225
|
+
unresolved_project_count=-1
|
|
226
|
+
),
|
|
227
|
+
],
|
|
228
|
+
)
|
|
229
|
+
def test_old_ignored_filters_and_invalid_response_never_escape_to_ui(mutation):
|
|
230
|
+
value = discovery_page()
|
|
231
|
+
mutation(value)
|
|
232
|
+
with pytest.raises(ProjectDiscoveryError):
|
|
233
|
+
ProjectsAPI(Client(value)).list(
|
|
234
|
+
page_size=1,
|
|
235
|
+
project_type="2d",
|
|
236
|
+
image_task_id="a_new_task",
|
|
237
|
+
include_facets=["image_task"],
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
@pytest.mark.parametrize("status", ["registered", "missing", "invalid", "unavailable"])
|
|
242
|
+
def test_binding_statuses_do_not_fill_defaults(status):
|
|
243
|
+
value = {"status": status, "id": None, "revision": None, "title": None}
|
|
244
|
+
if status in {"registered", "unavailable"}:
|
|
245
|
+
value.update(id="task", revision=1)
|
|
246
|
+
if status == "registered":
|
|
247
|
+
value["title"] = "Title"
|
|
248
|
+
assert validate_project_image_task(value) is value
|
|
249
|
+
broken = {**value, "revision": False}
|
|
250
|
+
with pytest.raises(ProjectDiscoveryError):
|
|
251
|
+
validate_project_image_task(broken)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def test_optional_response_extensions_do_not_force_new_clients():
|
|
255
|
+
value = discovery_page()
|
|
256
|
+
value["discovery"]["future_optional"] = {"anything": True}
|
|
257
|
+
value["items"][0]["future_optional"] = "retained"
|
|
258
|
+
query = project_discovery_query(
|
|
259
|
+
project_type="2d", image_task_id="a_new_task", include_facets=["image_task"]
|
|
260
|
+
)
|
|
261
|
+
assert (
|
|
262
|
+
validate_project_discovery_page(value, query=query, page=1, page_size=1)
|
|
263
|
+
is value
|
|
264
|
+
)
|
|
@@ -11,7 +11,7 @@ from x4d_devkit.eval.matching import MetricData
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def test_package_version_is_current_release():
|
|
14
|
-
assert x4d_devkit.__version__ == "0.
|
|
14
|
+
assert x4d_devkit.__version__ == "0.49.0"
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def test_loader_exposes_and_enforces_only_current_schema(tmp_path):
|
|
@@ -30,6 +30,7 @@ from x4d_devkit.client.semantic_scene_authoring import (
|
|
|
30
30
|
SemanticSceneAuthoringWorkSessionsAPI,
|
|
31
31
|
)
|
|
32
32
|
from x4d_devkit.client.projects import ProjectResolutionError, ProjectsAPI
|
|
33
|
+
from x4d_devkit.project_discovery import ProjectDiscoveryError
|
|
33
34
|
from x4d_devkit.client.reconstruction import (
|
|
34
35
|
ReconstructionAPI,
|
|
35
36
|
StaticRgbMapGeneration,
|
|
@@ -67,6 +68,7 @@ __all__ = [
|
|
|
67
68
|
"PlatformAsset",
|
|
68
69
|
"PlatformAssetReference",
|
|
69
70
|
"ProjectResolutionError",
|
|
71
|
+
"ProjectDiscoveryError",
|
|
70
72
|
"ProjectsAPI",
|
|
71
73
|
"ReconstructionAPI",
|
|
72
74
|
"RESOLVED_LABEL_SCHEMA_CONTEXT_VERSION",
|
|
@@ -2,8 +2,14 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
from dataclasses import dataclass
|
|
5
|
+
from collections.abc import Sequence
|
|
5
6
|
from typing import TYPE_CHECKING
|
|
6
7
|
|
|
8
|
+
from x4d_devkit.project_discovery import (
|
|
9
|
+
project_discovery_query,
|
|
10
|
+
validate_project_discovery_page,
|
|
11
|
+
)
|
|
12
|
+
|
|
7
13
|
if TYPE_CHECKING:
|
|
8
14
|
from x4d_devkit.client.client import X4DClient
|
|
9
15
|
|
|
@@ -27,8 +33,14 @@ class ProjectsAPI:
|
|
|
27
33
|
def list(
|
|
28
34
|
self, page: int = 1, page_size: int = 50, search: str | None = None,
|
|
29
35
|
project_type: str | None = None,
|
|
36
|
+
*, image_task_id: str | None = None, image_task_revision: int | None = None,
|
|
37
|
+
include_facets: Sequence[str] | None = None,
|
|
30
38
|
) -> dict:
|
|
31
|
-
"""List
|
|
39
|
+
"""List accessible Projects, optionally discovering registered image tasks."""
|
|
40
|
+
query = project_discovery_query(
|
|
41
|
+
project_type=project_type, image_task_id=image_task_id,
|
|
42
|
+
image_task_revision=image_task_revision, include_facets=include_facets,
|
|
43
|
+
)
|
|
32
44
|
params = {"page": page, "page_size": page_size}
|
|
33
45
|
if search:
|
|
34
46
|
params["search"] = search
|
|
@@ -36,7 +48,16 @@ class ProjectsAPI:
|
|
|
36
48
|
if project_type not in {"2d", "3d"}:
|
|
37
49
|
raise ValueError("project_type must be 2d or 3d")
|
|
38
50
|
params["project_type"] = project_type
|
|
39
|
-
|
|
51
|
+
if query.image_task_id is not None:
|
|
52
|
+
params["image_task_id"] = query.image_task_id
|
|
53
|
+
if query.image_task_revision is not None:
|
|
54
|
+
params["image_task_revision"] = query.image_task_revision
|
|
55
|
+
if query.include_facets:
|
|
56
|
+
params["include_facets"] = list(query.include_facets)
|
|
57
|
+
value = self._client.get("/projects", params=params)
|
|
58
|
+
if query.enabled:
|
|
59
|
+
validate_project_discovery_page(value, query=query, page=page, page_size=page_size)
|
|
60
|
+
return value
|
|
40
61
|
|
|
41
62
|
def get(self, project_id: int) -> dict:
|
|
42
63
|
"""Get a project by ID."""
|