pltr-cli 0.9.3__tar.gz → 0.11.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.
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/PKG-INFO +1 -1
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/pyproject.toml +1 -1
- pltr_cli-0.11.0/src/pltr/__init__.py +1 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/commands/folder.py +3 -3
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/commands/ontology.py +93 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/commands/orchestration.py +40 -1
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/commands/resource.py +63 -2
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/services/dataset.py +5 -8
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/services/ontology.py +71 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/services/resource.py +16 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/utils/formatting.py +9 -3
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_commands/test_ontology.py +105 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_services/test_ontology.py +79 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_services/test_resource.py +36 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/uv.lock +1 -1
- pltr_cli-0.9.3/src/pltr/__init__.py +0 -1
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/.github/workflows/ci.yml +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/.github/workflows/publish.yml +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/.github/workflows/test-publish.yml +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/.gitignore +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/.pre-commit-config.yaml +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/CHANGELOG.md +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/CLAUDE.md +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/LICENSE +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/README.md +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/RELEASE.md +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/children.csv +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/docs/README.md +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/docs/api/wrapper.md +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/docs/examples/csv-upload.md +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/docs/examples/gallery.md +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/docs/features/dataset-transactions.md +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/docs/user-guide/aliases.md +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/docs/user-guide/authentication.md +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/docs/user-guide/commands.md +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/docs/user-guide/quick-start.md +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/docs/user-guide/troubleshooting.md +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/docs/user-guide/workflows.md +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/folder_info.json +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/folders.json +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/mypy.ini +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/scripts/release.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/__main__.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/auth/__init__.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/auth/base.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/auth/manager.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/auth/oauth.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/auth/storage.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/auth/token.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/cli.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/commands/__init__.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/commands/admin.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/commands/alias.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/commands/completion.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/commands/configure.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/commands/connectivity.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/commands/dataset.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/commands/mediasets.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/commands/project.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/commands/resource_role.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/commands/shell.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/commands/space.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/commands/sql.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/commands/verify.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/config/__init__.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/config/aliases.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/config/profiles.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/config/settings.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/services/__init__.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/services/admin.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/services/base.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/services/connectivity.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/services/folder.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/services/mediasets.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/services/orchestration.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/services/project.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/services/resource_role.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/services/space.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/services/sql.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/utils/__init__.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/utils/alias_resolver.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/utils/completion.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/src/pltr/utils/progress.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/__init__.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/conftest.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/integration/README.md +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/integration/__init__.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/integration/conftest.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/integration/test_auth_flow.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/integration/test_cli_integration.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/integration/test_data_workflows.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/integration/test_data_workflows_simple.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/integration/test_simple_integration.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_auth/__init__.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_auth/test_base.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_auth/test_manager.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_auth/test_oauth.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_auth/test_storage.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_auth/test_token.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_commands/__init__.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_commands/test_admin.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_commands/test_alias.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_commands/test_completion.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_commands/test_connectivity.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_commands/test_dataset.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_commands/test_folder.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_commands/test_orchestration.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_commands/test_shell.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_commands/test_sql.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_commands/test_verify_simple.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_config/__init__.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_config/test_aliases.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_config/test_profiles.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_config/test_settings.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_services/__init__.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_services/test_admin.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_services/test_base.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_services/test_connectivity.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_services/test_dataset.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_services/test_dataset_transactions.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_services/test_folder.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_services/test_orchestration.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_services/test_project.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_services/test_resource_role.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_services/test_space.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_services/test_sql.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_utils/__init__.py +0 -0
- {pltr_cli-0.9.3 → pltr_cli-0.11.0}/tests/test_utils/test_alias_resolver.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.11.0"
|
|
@@ -279,7 +279,7 @@ def _format_children_table(children: List[dict]):
|
|
|
279
279
|
table = Table(title="Folder Children", show_header=True, header_style="bold cyan")
|
|
280
280
|
table.add_column("Type", style="cyan")
|
|
281
281
|
table.add_column("Display Name")
|
|
282
|
-
table.add_column("RID")
|
|
282
|
+
table.add_column("RID", no_wrap=True)
|
|
283
283
|
|
|
284
284
|
for child in children:
|
|
285
285
|
table.add_row(
|
|
@@ -296,8 +296,8 @@ def _format_folders_batch_table(folders: List[dict]):
|
|
|
296
296
|
"""Format multiple folders as a table."""
|
|
297
297
|
table = Table(title="Folders", show_header=True, header_style="bold cyan")
|
|
298
298
|
table.add_column("Display Name")
|
|
299
|
-
table.add_column("RID")
|
|
300
|
-
table.add_column("Parent Folder")
|
|
299
|
+
table.add_column("RID", no_wrap=True)
|
|
300
|
+
table.add_column("Parent Folder", no_wrap=True)
|
|
301
301
|
table.add_column("Description")
|
|
302
302
|
|
|
303
303
|
for folder in folders:
|
|
@@ -366,6 +366,99 @@ def list_linked_objects(
|
|
|
366
366
|
raise typer.Exit(1)
|
|
367
367
|
|
|
368
368
|
|
|
369
|
+
@app.command("object-count")
|
|
370
|
+
def count_objects(
|
|
371
|
+
ontology_rid: str = typer.Argument(..., help="Ontology Resource Identifier"),
|
|
372
|
+
object_type: str = typer.Argument(..., help="Object type API name"),
|
|
373
|
+
profile: Optional[str] = typer.Option(None, "--profile", "-p", help="Profile name"),
|
|
374
|
+
format: str = typer.Option(
|
|
375
|
+
"table", "--format", "-f", help="Output format (table, json, csv)"
|
|
376
|
+
),
|
|
377
|
+
output: Optional[str] = typer.Option(
|
|
378
|
+
None, "--output", "-o", help="Output file path"
|
|
379
|
+
),
|
|
380
|
+
branch: Optional[str] = typer.Option(None, "--branch", "-b", help="Branch name"),
|
|
381
|
+
):
|
|
382
|
+
"""Count objects of a specific type."""
|
|
383
|
+
try:
|
|
384
|
+
service = OntologyObjectService(profile=profile)
|
|
385
|
+
|
|
386
|
+
with SpinnerProgressTracker().track_spinner(
|
|
387
|
+
f"Counting {object_type} objects..."
|
|
388
|
+
):
|
|
389
|
+
result = service.count_objects(ontology_rid, object_type, branch=branch)
|
|
390
|
+
|
|
391
|
+
formatter.format_dict(result, format=format, output=output)
|
|
392
|
+
|
|
393
|
+
if output:
|
|
394
|
+
formatter.print_success(f"Count result saved to {output}")
|
|
395
|
+
|
|
396
|
+
except (ProfileNotFoundError, MissingCredentialsError) as e:
|
|
397
|
+
formatter.print_error(f"Authentication error: {e}")
|
|
398
|
+
raise typer.Exit(1)
|
|
399
|
+
except Exception as e:
|
|
400
|
+
formatter.print_error(f"Failed to count objects: {e}")
|
|
401
|
+
raise typer.Exit(1)
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
@app.command("object-search")
|
|
405
|
+
def search_objects(
|
|
406
|
+
ontology_rid: str = typer.Argument(..., help="Ontology Resource Identifier"),
|
|
407
|
+
object_type: str = typer.Argument(..., help="Object type API name"),
|
|
408
|
+
query: str = typer.Option(..., "--query", "-q", help="Search query string"),
|
|
409
|
+
profile: Optional[str] = typer.Option(None, "--profile", "-p", help="Profile name"),
|
|
410
|
+
format: str = typer.Option(
|
|
411
|
+
"table", "--format", "-f", help="Output format (table, json, csv)"
|
|
412
|
+
),
|
|
413
|
+
output: Optional[str] = typer.Option(
|
|
414
|
+
None, "--output", "-o", help="Output file path"
|
|
415
|
+
),
|
|
416
|
+
page_size: Optional[int] = typer.Option(
|
|
417
|
+
None, "--page-size", help="Number of results per page"
|
|
418
|
+
),
|
|
419
|
+
properties: Optional[str] = typer.Option(
|
|
420
|
+
None, "--properties", help="Comma-separated list of properties to include"
|
|
421
|
+
),
|
|
422
|
+
branch: Optional[str] = typer.Option(None, "--branch", "-b", help="Branch name"),
|
|
423
|
+
):
|
|
424
|
+
"""Search objects by query."""
|
|
425
|
+
try:
|
|
426
|
+
service = OntologyObjectService(profile=profile)
|
|
427
|
+
|
|
428
|
+
prop_list = properties.split(",") if properties else None
|
|
429
|
+
|
|
430
|
+
with SpinnerProgressTracker().track_spinner(
|
|
431
|
+
f"Searching {object_type} objects..."
|
|
432
|
+
):
|
|
433
|
+
objects = service.search_objects(
|
|
434
|
+
ontology_rid,
|
|
435
|
+
object_type,
|
|
436
|
+
query,
|
|
437
|
+
page_size=page_size,
|
|
438
|
+
properties=prop_list,
|
|
439
|
+
branch=branch,
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
if format == "table" and objects:
|
|
443
|
+
# Use first object's keys as columns
|
|
444
|
+
columns = list(objects[0].keys()) if objects else []
|
|
445
|
+
formatter.format_table(
|
|
446
|
+
objects, columns=columns, format=format, output=output
|
|
447
|
+
)
|
|
448
|
+
else:
|
|
449
|
+
formatter.format_list(objects, format=format, output=output)
|
|
450
|
+
|
|
451
|
+
if output:
|
|
452
|
+
formatter.print_success(f"Search results saved to {output}")
|
|
453
|
+
|
|
454
|
+
except (ProfileNotFoundError, MissingCredentialsError) as e:
|
|
455
|
+
formatter.print_error(f"Authentication error: {e}")
|
|
456
|
+
raise typer.Exit(1)
|
|
457
|
+
except Exception as e:
|
|
458
|
+
formatter.print_error(f"Failed to search objects: {e}")
|
|
459
|
+
raise typer.Exit(1)
|
|
460
|
+
|
|
461
|
+
|
|
369
462
|
# Action commands
|
|
370
463
|
@app.command("action-apply")
|
|
371
464
|
def apply_action(
|
|
@@ -92,7 +92,46 @@ def create_build(
|
|
|
92
92
|
"table", "--format", "-f", help="Output format (table, json, csv)"
|
|
93
93
|
),
|
|
94
94
|
):
|
|
95
|
-
"""
|
|
95
|
+
"""
|
|
96
|
+
Create a new build with specified target datasets.
|
|
97
|
+
|
|
98
|
+
The target parameter must be a JSON object with a 'type' field that specifies
|
|
99
|
+
the build strategy. Three types are supported:
|
|
100
|
+
|
|
101
|
+
\b
|
|
102
|
+
1. Manual Build - Explicitly specify datasets to build:
|
|
103
|
+
{
|
|
104
|
+
"type": "manual",
|
|
105
|
+
"targetRids": ["ri.foundry.main.dataset.abc123..."]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
\b
|
|
109
|
+
2. Upstream Build - Build target datasets and all their upstream dependencies:
|
|
110
|
+
{
|
|
111
|
+
"type": "upstream",
|
|
112
|
+
"targetRids": ["ri.foundry.main.dataset.abc123..."],
|
|
113
|
+
"ignoredRids": []
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
\b
|
|
117
|
+
3. Connecting Build - Build datasets between input and target datasets:
|
|
118
|
+
{
|
|
119
|
+
"type": "connecting",
|
|
120
|
+
"inputRids": ["ri.foundry.main.dataset.input123..."],
|
|
121
|
+
"targetRids": ["ri.foundry.main.dataset.target123..."],
|
|
122
|
+
"ignoredRids": []
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
Examples:
|
|
126
|
+
|
|
127
|
+
\b
|
|
128
|
+
# Build specific datasets manually
|
|
129
|
+
pltr orchestration builds create '{"type": "manual", "targetRids": ["ri.foundry.main.dataset.abc123"]}' --branch master
|
|
130
|
+
|
|
131
|
+
\b
|
|
132
|
+
# Build dataset and all upstream dependencies
|
|
133
|
+
pltr orchestration builds create '{"type": "upstream", "targetRids": ["ri.foundry.main.dataset.abc123"], "ignoredRids": []}' --branch master --force
|
|
134
|
+
"""
|
|
96
135
|
try:
|
|
97
136
|
service = OrchestrationService(profile=profile)
|
|
98
137
|
|
|
@@ -79,6 +79,64 @@ def get_resource(
|
|
|
79
79
|
raise typer.Exit(1)
|
|
80
80
|
|
|
81
81
|
|
|
82
|
+
@app.command("get-by-path")
|
|
83
|
+
def get_resource_by_path(
|
|
84
|
+
path: str = typer.Argument(
|
|
85
|
+
...,
|
|
86
|
+
help="Absolute path to the resource (e.g., '/My Organization/Project/Dataset')",
|
|
87
|
+
),
|
|
88
|
+
profile: Optional[str] = typer.Option(
|
|
89
|
+
None, "--profile", help="Profile name", autocompletion=complete_profile
|
|
90
|
+
),
|
|
91
|
+
format: str = typer.Option(
|
|
92
|
+
"table",
|
|
93
|
+
"--format",
|
|
94
|
+
"-f",
|
|
95
|
+
help="Output format (table, json, csv)",
|
|
96
|
+
autocompletion=complete_output_format,
|
|
97
|
+
),
|
|
98
|
+
output: Optional[str] = typer.Option(
|
|
99
|
+
None, "--output", "-o", help="Output file path"
|
|
100
|
+
),
|
|
101
|
+
):
|
|
102
|
+
"""Get detailed information about a specific resource by its path."""
|
|
103
|
+
try:
|
|
104
|
+
service = ResourceService(profile=profile)
|
|
105
|
+
|
|
106
|
+
with SpinnerProgressTracker().track_spinner(
|
|
107
|
+
f"Fetching resource at path '{path}'..."
|
|
108
|
+
):
|
|
109
|
+
resource = service.get_resource_by_path(path)
|
|
110
|
+
|
|
111
|
+
# Cache the RID for future completions
|
|
112
|
+
if resource.get("rid"):
|
|
113
|
+
cache_rid(resource["rid"])
|
|
114
|
+
|
|
115
|
+
# Format output
|
|
116
|
+
if format == "json":
|
|
117
|
+
if output:
|
|
118
|
+
formatter.save_to_file(resource, output, "json")
|
|
119
|
+
else:
|
|
120
|
+
formatter.format_dict(resource)
|
|
121
|
+
elif format == "csv":
|
|
122
|
+
if output:
|
|
123
|
+
formatter.save_to_file([resource], output, "csv")
|
|
124
|
+
else:
|
|
125
|
+
formatter.format_list([resource])
|
|
126
|
+
else:
|
|
127
|
+
_format_resource_table(resource)
|
|
128
|
+
|
|
129
|
+
if output:
|
|
130
|
+
formatter.print_success(f"Resource information saved to {output}")
|
|
131
|
+
|
|
132
|
+
except (ProfileNotFoundError, MissingCredentialsError) as e:
|
|
133
|
+
formatter.print_error(f"Authentication error: {e}")
|
|
134
|
+
raise typer.Exit(1)
|
|
135
|
+
except Exception as e:
|
|
136
|
+
formatter.print_error(f"Failed to get resource: {e}")
|
|
137
|
+
raise typer.Exit(1)
|
|
138
|
+
|
|
139
|
+
|
|
82
140
|
@app.command("list")
|
|
83
141
|
def list_resources(
|
|
84
142
|
folder_rid: Optional[str] = typer.Option(
|
|
@@ -469,12 +527,15 @@ def main():
|
|
|
469
527
|
|
|
470
528
|
Manage resources in the Foundry filesystem. Get resource information,
|
|
471
529
|
search resources, manage metadata, and perform operations using Resource
|
|
472
|
-
Identifiers (RIDs).
|
|
530
|
+
Identifiers (RIDs) or paths.
|
|
473
531
|
|
|
474
532
|
Examples:
|
|
475
|
-
# Get resource information
|
|
533
|
+
# Get resource information by RID
|
|
476
534
|
pltr resource get ri.compass.main.dataset.xyz123
|
|
477
535
|
|
|
536
|
+
# Get resource information by path
|
|
537
|
+
pltr resource get-by-path "/My Organization/Project/Dataset Name"
|
|
538
|
+
|
|
478
539
|
# List all resources
|
|
479
540
|
pltr resource list
|
|
480
541
|
|
|
@@ -487,18 +487,15 @@ class DatasetService(BaseService):
|
|
|
487
487
|
# Ensure output directory exists
|
|
488
488
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
489
489
|
|
|
490
|
-
|
|
490
|
+
# Use Dataset.File.content() which returns bytes directly
|
|
491
|
+
# Note: In SDK v1.27.0, the method is 'content' not 'read'
|
|
492
|
+
file_content = self.service.Dataset.File.content(
|
|
491
493
|
dataset_rid=dataset_rid, file_path=file_path, branch_name=branch
|
|
492
494
|
)
|
|
493
495
|
|
|
494
|
-
# Write file content to disk
|
|
496
|
+
# Write file content to disk (file_content is bytes)
|
|
495
497
|
with open(output_path, "wb") as f:
|
|
496
|
-
|
|
497
|
-
# If it's a stream
|
|
498
|
-
f.write(file_content.read())
|
|
499
|
-
else:
|
|
500
|
-
# If it's bytes
|
|
501
|
-
f.write(file_content)
|
|
498
|
+
f.write(file_content)
|
|
502
499
|
|
|
503
500
|
return {
|
|
504
501
|
"dataset_rid": dataset_rid,
|
|
@@ -287,6 +287,77 @@ class OntologyObjectService(BaseService):
|
|
|
287
287
|
except Exception as e:
|
|
288
288
|
raise RuntimeError(f"Failed to list linked objects: {e}")
|
|
289
289
|
|
|
290
|
+
def count_objects(
|
|
291
|
+
self,
|
|
292
|
+
ontology_rid: str,
|
|
293
|
+
object_type: str,
|
|
294
|
+
branch: Optional[str] = None,
|
|
295
|
+
) -> Dict[str, Any]:
|
|
296
|
+
"""
|
|
297
|
+
Count objects of a specific type.
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
ontology_rid: Ontology Resource Identifier
|
|
301
|
+
object_type: Object type API name
|
|
302
|
+
branch: Branch name (optional)
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
Dictionary containing count information
|
|
306
|
+
"""
|
|
307
|
+
try:
|
|
308
|
+
count = self.service.OntologyObject.count(
|
|
309
|
+
ontology_rid,
|
|
310
|
+
object_type,
|
|
311
|
+
branch_name=branch,
|
|
312
|
+
)
|
|
313
|
+
return {
|
|
314
|
+
"ontology_rid": ontology_rid,
|
|
315
|
+
"object_type": object_type,
|
|
316
|
+
"count": count,
|
|
317
|
+
"branch": branch,
|
|
318
|
+
}
|
|
319
|
+
except Exception as e:
|
|
320
|
+
raise RuntimeError(f"Failed to count objects: {e}")
|
|
321
|
+
|
|
322
|
+
def search_objects(
|
|
323
|
+
self,
|
|
324
|
+
ontology_rid: str,
|
|
325
|
+
object_type: str,
|
|
326
|
+
query: str,
|
|
327
|
+
page_size: Optional[int] = None,
|
|
328
|
+
properties: Optional[List[str]] = None,
|
|
329
|
+
branch: Optional[str] = None,
|
|
330
|
+
) -> List[Dict[str, Any]]:
|
|
331
|
+
"""
|
|
332
|
+
Search objects by query.
|
|
333
|
+
|
|
334
|
+
Args:
|
|
335
|
+
ontology_rid: Ontology Resource Identifier
|
|
336
|
+
object_type: Object type API name
|
|
337
|
+
query: Search query string
|
|
338
|
+
page_size: Number of results per page
|
|
339
|
+
properties: List of properties to include
|
|
340
|
+
branch: Branch name (optional)
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
List of matching object dictionaries
|
|
344
|
+
"""
|
|
345
|
+
try:
|
|
346
|
+
result = self.service.OntologyObject.search(
|
|
347
|
+
ontology_rid,
|
|
348
|
+
object_type,
|
|
349
|
+
query=query,
|
|
350
|
+
page_size=page_size,
|
|
351
|
+
properties=properties,
|
|
352
|
+
branch_name=branch,
|
|
353
|
+
)
|
|
354
|
+
objects = []
|
|
355
|
+
for obj in result:
|
|
356
|
+
objects.append(self._format_object(obj))
|
|
357
|
+
return objects
|
|
358
|
+
except Exception as e:
|
|
359
|
+
raise RuntimeError(f"Failed to search objects: {e}")
|
|
360
|
+
|
|
290
361
|
def _format_object(self, obj: Any) -> Dict[str, Any]:
|
|
291
362
|
"""Format object for consistent output."""
|
|
292
363
|
# Objects may have various properties - extract them dynamically
|
|
@@ -30,6 +30,22 @@ class ResourceService(BaseService):
|
|
|
30
30
|
except Exception as e:
|
|
31
31
|
raise RuntimeError(f"Failed to get resource {resource_rid}: {e}")
|
|
32
32
|
|
|
33
|
+
def get_resource_by_path(self, path: str) -> Dict[str, Any]:
|
|
34
|
+
"""
|
|
35
|
+
Get information about a specific resource by its path.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
path: Absolute path to the resource (e.g., "/My Organization/Project/Dataset")
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Resource information dictionary
|
|
42
|
+
"""
|
|
43
|
+
try:
|
|
44
|
+
resource = self.service.Resource.get_by_path(path=path, preview=True)
|
|
45
|
+
return self._format_resource_info(resource)
|
|
46
|
+
except Exception as e:
|
|
47
|
+
raise RuntimeError(f"Failed to get resource at path '{path}': {e}")
|
|
48
|
+
|
|
33
49
|
def list_resources(
|
|
34
50
|
self,
|
|
35
51
|
folder_rid: Optional[str] = None,
|
|
@@ -64,7 +64,8 @@ class OutputFormatter:
|
|
|
64
64
|
f.write(json_str)
|
|
65
65
|
return None
|
|
66
66
|
else:
|
|
67
|
-
|
|
67
|
+
# Use plain print to ensure valid JSON output without ANSI codes
|
|
68
|
+
print(json_str)
|
|
68
69
|
return json_str
|
|
69
70
|
|
|
70
71
|
def _format_csv(
|
|
@@ -141,7 +142,11 @@ class OutputFormatter:
|
|
|
141
142
|
|
|
142
143
|
# Add columns to table
|
|
143
144
|
for column in columns:
|
|
144
|
-
|
|
145
|
+
# Don't truncate RID columns - they need full visibility
|
|
146
|
+
if "rid" in column.lower():
|
|
147
|
+
table.add_column(column, no_wrap=True, overflow="fold")
|
|
148
|
+
else:
|
|
149
|
+
table.add_column(column, overflow="fold")
|
|
145
150
|
|
|
146
151
|
# Add rows
|
|
147
152
|
for item in data:
|
|
@@ -424,7 +429,8 @@ class OutputFormatter:
|
|
|
424
429
|
else:
|
|
425
430
|
# For simple values, just print them
|
|
426
431
|
if format_type == "json":
|
|
427
|
-
|
|
432
|
+
# Use plain print to ensure valid JSON output without ANSI codes
|
|
433
|
+
print(json.dumps(data, indent=2, default=str))
|
|
428
434
|
else:
|
|
429
435
|
rich_print(str(data))
|
|
430
436
|
|
|
@@ -220,6 +220,111 @@ def test_list_linked_objects_command(mock_services):
|
|
|
220
220
|
mock_instance.list_linked_objects.assert_called_once()
|
|
221
221
|
|
|
222
222
|
|
|
223
|
+
def test_count_objects_command(mock_services):
|
|
224
|
+
"""Test count objects command."""
|
|
225
|
+
mock_instance = Mock()
|
|
226
|
+
mock_instance.count_objects.return_value = {
|
|
227
|
+
"ontology_rid": "ri.ontology.main.ontology.test",
|
|
228
|
+
"object_type": "Employee",
|
|
229
|
+
"count": 42,
|
|
230
|
+
"branch": None,
|
|
231
|
+
}
|
|
232
|
+
mock_services["object"].return_value = mock_instance
|
|
233
|
+
|
|
234
|
+
result = runner.invoke(
|
|
235
|
+
app, ["object-count", "ri.ontology.main.ontology.test", "Employee"]
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
assert result.exit_code == 0
|
|
239
|
+
mock_instance.count_objects.assert_called_once_with(
|
|
240
|
+
"ri.ontology.main.ontology.test", "Employee", branch=None
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def test_count_objects_with_branch(mock_services):
|
|
245
|
+
"""Test count objects with branch specified."""
|
|
246
|
+
mock_instance = Mock()
|
|
247
|
+
mock_instance.count_objects.return_value = {
|
|
248
|
+
"ontology_rid": "ri.ontology.main.ontology.test",
|
|
249
|
+
"object_type": "Employee",
|
|
250
|
+
"count": 24,
|
|
251
|
+
"branch": "master",
|
|
252
|
+
}
|
|
253
|
+
mock_services["object"].return_value = mock_instance
|
|
254
|
+
|
|
255
|
+
result = runner.invoke(
|
|
256
|
+
app,
|
|
257
|
+
[
|
|
258
|
+
"object-count",
|
|
259
|
+
"ri.ontology.main.ontology.test",
|
|
260
|
+
"Employee",
|
|
261
|
+
"--branch",
|
|
262
|
+
"master",
|
|
263
|
+
],
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
assert result.exit_code == 0
|
|
267
|
+
mock_instance.count_objects.assert_called_once_with(
|
|
268
|
+
"ri.ontology.main.ontology.test", "Employee", branch="master"
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def test_search_objects_command(mock_services):
|
|
273
|
+
"""Test search objects command."""
|
|
274
|
+
mock_instance = Mock()
|
|
275
|
+
mock_instance.search_objects.return_value = [
|
|
276
|
+
{"employee_id": "EMP001", "name": "John Doe"}
|
|
277
|
+
]
|
|
278
|
+
mock_services["object"].return_value = mock_instance
|
|
279
|
+
|
|
280
|
+
result = runner.invoke(
|
|
281
|
+
app,
|
|
282
|
+
[
|
|
283
|
+
"object-search",
|
|
284
|
+
"ri.ontology.main.ontology.test",
|
|
285
|
+
"Employee",
|
|
286
|
+
"--query",
|
|
287
|
+
"John",
|
|
288
|
+
],
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
assert result.exit_code == 0
|
|
292
|
+
mock_instance.search_objects.assert_called_once()
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def test_search_objects_with_options(mock_services):
|
|
296
|
+
"""Test search objects with all options."""
|
|
297
|
+
mock_instance = Mock()
|
|
298
|
+
mock_instance.search_objects.return_value = [
|
|
299
|
+
{"employee_id": "EMP001", "name": "John Doe"}
|
|
300
|
+
]
|
|
301
|
+
mock_services["object"].return_value = mock_instance
|
|
302
|
+
|
|
303
|
+
result = runner.invoke(
|
|
304
|
+
app,
|
|
305
|
+
[
|
|
306
|
+
"object-search",
|
|
307
|
+
"ri.ontology.main.ontology.test",
|
|
308
|
+
"Employee",
|
|
309
|
+
"--query",
|
|
310
|
+
"Jane",
|
|
311
|
+
"--page-size",
|
|
312
|
+
"10",
|
|
313
|
+
"--properties",
|
|
314
|
+
"name,department",
|
|
315
|
+
"--branch",
|
|
316
|
+
"master",
|
|
317
|
+
],
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
assert result.exit_code == 0
|
|
321
|
+
call_args = mock_instance.search_objects.call_args
|
|
322
|
+
assert call_args[0] == ("ri.ontology.main.ontology.test", "Employee", "Jane")
|
|
323
|
+
assert call_args[1]["page_size"] == 10
|
|
324
|
+
assert call_args[1]["properties"] == ["name", "department"]
|
|
325
|
+
assert call_args[1]["branch"] == "master"
|
|
326
|
+
|
|
327
|
+
|
|
223
328
|
# Action command tests
|
|
224
329
|
def test_apply_action_command(mock_services):
|
|
225
330
|
"""Test apply action command."""
|
|
@@ -335,6 +335,85 @@ def test_list_linked_objects(mock_ontology_object_service, sample_object):
|
|
|
335
335
|
mock_ontology_object_class.list_linked_objects.assert_called_once()
|
|
336
336
|
|
|
337
337
|
|
|
338
|
+
def test_count_objects(mock_ontology_object_service):
|
|
339
|
+
"""Test counting objects."""
|
|
340
|
+
service, mock_ontology_object_class = mock_ontology_object_service
|
|
341
|
+
mock_ontology_object_class.count.return_value = 42
|
|
342
|
+
|
|
343
|
+
result = service.count_objects("ri.ontology.main.ontology.test", "Employee")
|
|
344
|
+
|
|
345
|
+
assert result["count"] == 42
|
|
346
|
+
assert result["ontology_rid"] == "ri.ontology.main.ontology.test"
|
|
347
|
+
assert result["object_type"] == "Employee"
|
|
348
|
+
assert result["branch"] is None
|
|
349
|
+
mock_ontology_object_class.count.assert_called_once_with(
|
|
350
|
+
"ri.ontology.main.ontology.test", "Employee", branch_name=None
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def test_count_objects_with_branch(mock_ontology_object_service):
|
|
355
|
+
"""Test counting objects with branch specified."""
|
|
356
|
+
service, mock_ontology_object_class = mock_ontology_object_service
|
|
357
|
+
mock_ontology_object_class.count.return_value = 24
|
|
358
|
+
|
|
359
|
+
result = service.count_objects(
|
|
360
|
+
"ri.ontology.main.ontology.test", "Employee", branch="master"
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
assert result["count"] == 24
|
|
364
|
+
assert result["branch"] == "master"
|
|
365
|
+
mock_ontology_object_class.count.assert_called_once_with(
|
|
366
|
+
"ri.ontology.main.ontology.test", "Employee", branch_name="master"
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def test_search_objects(mock_ontology_object_service, sample_object):
|
|
371
|
+
"""Test searching objects."""
|
|
372
|
+
service, mock_ontology_object_class = mock_ontology_object_service
|
|
373
|
+
mock_ontology_object_class.search.return_value = [sample_object]
|
|
374
|
+
|
|
375
|
+
result = service.search_objects(
|
|
376
|
+
"ri.ontology.main.ontology.test", "Employee", "John"
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
assert len(result) == 1
|
|
380
|
+
assert result[0]["employee_id"] == "EMP001"
|
|
381
|
+
assert result[0]["name"] == "John Doe"
|
|
382
|
+
mock_ontology_object_class.search.assert_called_once_with(
|
|
383
|
+
"ri.ontology.main.ontology.test",
|
|
384
|
+
"Employee",
|
|
385
|
+
query="John",
|
|
386
|
+
page_size=None,
|
|
387
|
+
properties=None,
|
|
388
|
+
branch_name=None,
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def test_search_objects_with_options(mock_ontology_object_service, sample_object):
|
|
393
|
+
"""Test searching objects with all options."""
|
|
394
|
+
service, mock_ontology_object_class = mock_ontology_object_service
|
|
395
|
+
mock_ontology_object_class.search.return_value = [sample_object]
|
|
396
|
+
|
|
397
|
+
result = service.search_objects(
|
|
398
|
+
"ri.ontology.main.ontology.test",
|
|
399
|
+
"Employee",
|
|
400
|
+
"Jane",
|
|
401
|
+
page_size=10,
|
|
402
|
+
properties=["name", "department"],
|
|
403
|
+
branch="master",
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
assert len(result) == 1
|
|
407
|
+
mock_ontology_object_class.search.assert_called_once_with(
|
|
408
|
+
"ri.ontology.main.ontology.test",
|
|
409
|
+
"Employee",
|
|
410
|
+
query="Jane",
|
|
411
|
+
page_size=10,
|
|
412
|
+
properties=["name", "department"],
|
|
413
|
+
branch_name="master",
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
|
|
338
417
|
# ActionService Tests
|
|
339
418
|
def test_apply_action(mock_action_service, sample_action_result):
|
|
340
419
|
"""Test applying an action."""
|
|
@@ -66,6 +66,42 @@ class TestResourceService:
|
|
|
66
66
|
):
|
|
67
67
|
resource_service.get_resource("ri.compass.main.dataset.123")
|
|
68
68
|
|
|
69
|
+
def test_get_resource_by_path(self, resource_service, mock_client):
|
|
70
|
+
"""Test getting a resource by path."""
|
|
71
|
+
mock_resource = Mock()
|
|
72
|
+
mock_resource.rid = "ri.compass.main.dataset.123"
|
|
73
|
+
mock_resource.display_name = "Test Dataset"
|
|
74
|
+
mock_resource.type = "dataset"
|
|
75
|
+
mock_resource.path = "/My Organization/Project/Test Dataset"
|
|
76
|
+
|
|
77
|
+
mock_client.filesystem.Resource.get_by_path.return_value = mock_resource
|
|
78
|
+
resource_service._client = mock_client
|
|
79
|
+
|
|
80
|
+
result = resource_service.get_resource_by_path(
|
|
81
|
+
"/My Organization/Project/Test Dataset"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
mock_client.filesystem.Resource.get_by_path.assert_called_once_with(
|
|
85
|
+
path="/My Organization/Project/Test Dataset", preview=True
|
|
86
|
+
)
|
|
87
|
+
assert result["rid"] == "ri.compass.main.dataset.123"
|
|
88
|
+
assert result["display_name"] == "Test Dataset"
|
|
89
|
+
assert result["type"] == "dataset"
|
|
90
|
+
assert result["path"] == "/My Organization/Project/Test Dataset"
|
|
91
|
+
|
|
92
|
+
def test_get_resource_by_path_failure(self, resource_service, mock_client):
|
|
93
|
+
"""Test handling resource get by path failure."""
|
|
94
|
+
mock_client.filesystem.Resource.get_by_path.side_effect = Exception(
|
|
95
|
+
"Path not found"
|
|
96
|
+
)
|
|
97
|
+
resource_service._client = mock_client
|
|
98
|
+
|
|
99
|
+
with pytest.raises(
|
|
100
|
+
RuntimeError,
|
|
101
|
+
match="Failed to get resource at path '/Invalid/Path': Path not found",
|
|
102
|
+
):
|
|
103
|
+
resource_service.get_resource_by_path("/Invalid/Path")
|
|
104
|
+
|
|
69
105
|
def test_list_resources(self, resource_service, mock_client):
|
|
70
106
|
"""Test listing resources."""
|
|
71
107
|
mock_resources = [Mock(), Mock()]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.9.3"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|