pltr-cli 0.11.0__py3-none-any.whl → 0.12.0__py3-none-any.whl

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/commands/cp.py ADDED
@@ -0,0 +1,103 @@
1
+ """
2
+ Copy resources between Compass folders.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Optional
8
+
9
+ import typer
10
+ from rich.console import Console
11
+
12
+ from ..auth.base import MissingCredentialsError, ProfileNotFoundError
13
+ from ..services.copy import CopyService
14
+ from ..utils.completion import complete_profile, complete_rid
15
+ from ..utils.formatting import OutputFormatter
16
+
17
+
18
+ def cp_command(
19
+ source_rid: str = typer.Argument(
20
+ ...,
21
+ help="Resource Identifier to copy (dataset or folder)",
22
+ autocompletion=complete_rid,
23
+ ),
24
+ target_folder_rid: str = typer.Argument(
25
+ ...,
26
+ help="Destination Compass folder RID",
27
+ autocompletion=complete_rid,
28
+ ),
29
+ profile: Optional[str] = typer.Option(
30
+ None,
31
+ "--profile",
32
+ help="Authentication profile",
33
+ autocompletion=complete_profile,
34
+ ),
35
+ recursive: bool = typer.Option(
36
+ False,
37
+ "--recursive",
38
+ "-r",
39
+ help="Copy folder contents recursively (required when SOURCE_RID is a folder)",
40
+ ),
41
+ branch: str = typer.Option(
42
+ "master",
43
+ "--branch",
44
+ "-b",
45
+ help="Dataset branch to copy file-backed datasets from",
46
+ ),
47
+ name_suffix: str = typer.Option(
48
+ "-copy",
49
+ "--name-suffix",
50
+ help="Suffix appended to cloned folder/dataset names",
51
+ ),
52
+ copy_schema: bool = typer.Option(
53
+ True,
54
+ "--schema/--no-schema",
55
+ help="Copy dataset schemas when available",
56
+ ),
57
+ dry_run: bool = typer.Option(
58
+ False,
59
+ "--dry-run",
60
+ help="Log actions without creating any resources",
61
+ ),
62
+ fail_fast: bool = typer.Option(
63
+ False,
64
+ "--fail-fast",
65
+ help="Stop immediately on first error when copying folders recursively",
66
+ ),
67
+ debug: bool = typer.Option(
68
+ False,
69
+ "--debug",
70
+ help="Print stack traces when errors occur",
71
+ ),
72
+ ):
73
+ """Copy a resource identified by RID into another Compass folder."""
74
+ console = Console()
75
+ formatter = OutputFormatter(console)
76
+
77
+ try:
78
+ service = CopyService(
79
+ profile=profile,
80
+ branch=branch,
81
+ name_suffix=name_suffix,
82
+ copy_schema=copy_schema,
83
+ dry_run=dry_run,
84
+ fail_fast=fail_fast,
85
+ debug=debug,
86
+ console=console,
87
+ )
88
+ summary = service.copy_resource(
89
+ source_rid, target_folder_rid, recursive=recursive
90
+ )
91
+ formatter.print_success("Copy operation completed")
92
+ formatter.print_info(
93
+ "Folders copied: {folders_copied} | Datasets copied: {datasets_copied} | "
94
+ "Skipped: {skipped} | Errors: {errors}".format(**summary)
95
+ )
96
+ except (ProfileNotFoundError, MissingCredentialsError) as exc:
97
+ formatter.print_error(f"Authentication error: {exc}")
98
+ raise typer.Exit(1)
99
+ except Exception as exc:
100
+ if debug:
101
+ raise
102
+ formatter.print_error(f"Failed to copy resource: {exc}")
103
+ raise typer.Exit(1)
pltr/commands/dataset.py CHANGED
@@ -8,6 +8,7 @@ from rich.console import Console
8
8
 
9
9
  from ..services.dataset import DatasetService
10
10
  from ..utils.formatting import OutputFormatter
11
+ from ..utils.pagination import PaginationConfig
11
12
  from ..utils.progress import SpinnerProgressTracker
12
13
  from ..auth.base import ProfileNotFoundError, MissingCredentialsError
13
14
  from ..utils.completion import (
@@ -73,6 +74,55 @@ def get_dataset(
73
74
  raise typer.Exit(1)
74
75
 
75
76
 
77
+ @app.command("preview")
78
+ def preview_dataset(
79
+ dataset_rid: str = typer.Argument(
80
+ ..., help="Dataset Resource Identifier", autocompletion=complete_rid
81
+ ),
82
+ limit: int = typer.Option(
83
+ 10, "--limit", "-n", help="Number of rows to display", min=1
84
+ ),
85
+ profile: Optional[str] = typer.Option(
86
+ None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
87
+ ),
88
+ format: str = typer.Option(
89
+ "table",
90
+ "--format",
91
+ "-f",
92
+ help="Output format (table, json, csv)",
93
+ autocompletion=complete_output_format,
94
+ ),
95
+ output: Optional[str] = typer.Option(
96
+ None, "--output", "-o", help="Output file path"
97
+ ),
98
+ ):
99
+ """Preview dataset contents."""
100
+ try:
101
+ cache_rid(dataset_rid)
102
+ service = DatasetService(profile=profile)
103
+
104
+ with SpinnerProgressTracker().track_spinner(
105
+ f"Fetching preview of {dataset_rid} (limit: {limit})..."
106
+ ):
107
+ data = service.preview_data(dataset_rid, limit=limit)
108
+
109
+ if not data:
110
+ formatter.print_warning("Dataset is empty or has no readable data")
111
+ return
112
+
113
+ formatter.format_output(data, format, output)
114
+
115
+ if output:
116
+ formatter.print_success(f"Preview saved to {output}")
117
+
118
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
119
+ formatter.print_error(f"Authentication error: {e}")
120
+ raise typer.Exit(1)
121
+ except Exception as e:
122
+ formatter.print_error(f"Failed to preview dataset: {e}")
123
+ raise typer.Exit(1)
124
+
125
+
76
126
  # Schema commands
77
127
  @schema_app.command("get")
78
128
  def get_schema(
@@ -566,21 +616,71 @@ def list_files(
566
616
  output: Optional[str] = typer.Option(
567
617
  None, "--output", "-o", help="Output file path"
568
618
  ),
619
+ page_size: Optional[int] = typer.Option(
620
+ None, "--page-size", help="Number of files per page (default: from settings)"
621
+ ),
622
+ max_pages: Optional[int] = typer.Option(
623
+ 1, "--max-pages", help="Maximum number of pages to fetch (default: 1)"
624
+ ),
625
+ page_token: Optional[str] = typer.Option(
626
+ None, "--page-token", help="Page token to resume from previous response"
627
+ ),
628
+ all: bool = typer.Option(
629
+ False, "--all", help="Fetch all available pages (overrides --max-pages)"
630
+ ),
569
631
  ):
570
- """List files in a dataset."""
632
+ """
633
+ List files in a dataset with pagination support.
634
+
635
+ By default, fetches only the first page of results. Use --all to fetch all files,
636
+ or --max-pages to control how many pages to fetch. Critical for large datasets.
637
+
638
+ Examples:
639
+ # List first page of files (default)
640
+ pltr dataset files list DATASET_RID
641
+
642
+ # List all files
643
+ pltr dataset files list DATASET_RID --all
644
+
645
+ # List first 3 pages
646
+ pltr dataset files list DATASET_RID --max-pages 3
647
+ """
571
648
  try:
572
649
  cache_rid(dataset_rid)
573
650
  service = DatasetService(profile=profile)
574
651
 
652
+ # Create pagination config
653
+ config = PaginationConfig(
654
+ page_size=page_size,
655
+ max_pages=max_pages,
656
+ page_token=page_token,
657
+ fetch_all=all,
658
+ )
659
+
575
660
  with SpinnerProgressTracker().track_spinner(
576
661
  f"Fetching files from {dataset_rid} (branch: {branch})..."
577
662
  ):
578
- files = service.list_files(dataset_rid, branch)
579
-
580
- formatter.format_files(files, format, output)
663
+ result = service.list_files_paginated(dataset_rid, branch, config)
581
664
 
665
+ # Format and display paginated results
582
666
  if output:
667
+ formatter.format_paginated_output(
668
+ result,
669
+ format,
670
+ output,
671
+ formatter_fn=lambda data, fmt, out: formatter.format_files(
672
+ data, fmt, out
673
+ ),
674
+ )
583
675
  formatter.print_success(f"Files information saved to {output}")
676
+ else:
677
+ formatter.format_paginated_output(
678
+ result,
679
+ format,
680
+ formatter_fn=lambda data, fmt, out: formatter.format_files(
681
+ data, fmt, out
682
+ ),
683
+ )
584
684
 
585
685
  except (ProfileNotFoundError, MissingCredentialsError) as e:
586
686
  formatter.print_error(f"Authentication error: {e}")
@@ -405,6 +405,180 @@ def get_media_reference(
405
405
  raise typer.Exit(1)
406
406
 
407
407
 
408
+ @app.command("thumbnail-calculate")
409
+ def thumbnail_calculate(
410
+ media_set_rid: str = typer.Argument(
411
+ ..., help="Media Set Resource Identifier", autocompletion=complete_rid
412
+ ),
413
+ media_item_rid: str = typer.Argument(
414
+ ..., help="Media Item Resource Identifier", autocompletion=complete_rid
415
+ ),
416
+ profile: Optional[str] = typer.Option(
417
+ None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
418
+ ),
419
+ format: str = typer.Option(
420
+ "table",
421
+ "--format",
422
+ "-f",
423
+ help="Output format (table, json, csv)",
424
+ autocompletion=complete_output_format,
425
+ ),
426
+ output: Optional[str] = typer.Option(
427
+ None, "--output", "-o", help="Output file path"
428
+ ),
429
+ preview: bool = typer.Option(False, "--preview", help="Enable preview mode"),
430
+ ):
431
+ """Initiate thumbnail generation for an image."""
432
+ try:
433
+ cache_rid(media_set_rid)
434
+ cache_rid(media_item_rid)
435
+ service = MediaSetsService(profile=profile)
436
+
437
+ with SpinnerProgressTracker().track_spinner(
438
+ f"Initiating thumbnail calculation for {media_item_rid}..."
439
+ ):
440
+ status = service.calculate_thumbnail(
441
+ media_set_rid, media_item_rid, preview=preview
442
+ )
443
+
444
+ formatter.format_thumbnail_status(status, format, output)
445
+
446
+ if output:
447
+ formatter.print_success(f"Thumbnail status saved to {output}")
448
+ else:
449
+ formatter.print_info(
450
+ "Use 'thumbnail-retrieve' to download once calculation completes"
451
+ )
452
+
453
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
454
+ formatter.print_error(f"Authentication error: {e}")
455
+ raise typer.Exit(1)
456
+ except Exception as e:
457
+ formatter.print_error(f"Failed to calculate thumbnail: {e}")
458
+ raise typer.Exit(1)
459
+
460
+
461
+ @app.command("thumbnail-retrieve")
462
+ def thumbnail_retrieve(
463
+ media_set_rid: str = typer.Argument(
464
+ ..., help="Media Set Resource Identifier", autocompletion=complete_rid
465
+ ),
466
+ media_item_rid: str = typer.Argument(
467
+ ..., help="Media Item Resource Identifier", autocompletion=complete_rid
468
+ ),
469
+ output_path: str = typer.Argument(
470
+ ..., help="Local path where thumbnail should be saved"
471
+ ),
472
+ profile: Optional[str] = typer.Option(
473
+ None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
474
+ ),
475
+ preview: bool = typer.Option(False, "--preview", help="Enable preview mode"),
476
+ overwrite: bool = typer.Option(
477
+ False, "--overwrite", help="Overwrite existing file"
478
+ ),
479
+ ):
480
+ """Download a calculated thumbnail from a media set (200px wide webp)."""
481
+ try:
482
+ # Check if output file already exists
483
+ output_path_obj = Path(output_path)
484
+ if output_path_obj.exists() and not overwrite:
485
+ formatter.print_error(f"File already exists: {output_path}")
486
+ formatter.print_info("Use --overwrite to replace existing file")
487
+ raise typer.Exit(1)
488
+
489
+ cache_rid(media_set_rid)
490
+ cache_rid(media_item_rid)
491
+ service = MediaSetsService(profile=profile)
492
+
493
+ with SpinnerProgressTracker().track_spinner("Downloading thumbnail..."):
494
+ result = service.retrieve_thumbnail(
495
+ media_set_rid,
496
+ media_item_rid,
497
+ output_path,
498
+ preview=preview,
499
+ )
500
+
501
+ formatter.print_success("Successfully downloaded thumbnail")
502
+ formatter.print_info(f"Saved to: {result['output_path']}")
503
+ formatter.print_info(f"File size: {result['file_size']} bytes")
504
+ formatter.print_info(f"Format: {result['format']}")
505
+
506
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
507
+ formatter.print_error(f"Authentication error: {e}")
508
+ raise typer.Exit(1)
509
+ except Exception as e:
510
+ formatter.print_error(f"Failed to retrieve thumbnail: {e}")
511
+ raise typer.Exit(1)
512
+
513
+
514
+ @app.command("upload-temp")
515
+ def upload_temp(
516
+ file_path: str = typer.Argument(..., help="Local path to the file to upload"),
517
+ profile: Optional[str] = typer.Option(
518
+ None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
519
+ ),
520
+ filename: Optional[str] = typer.Option(
521
+ None, "--filename", help="Override filename for the upload"
522
+ ),
523
+ attribution: Optional[str] = typer.Option(
524
+ None, "--attribution", help="Attribution string for the media"
525
+ ),
526
+ format: str = typer.Option(
527
+ "table",
528
+ "--format",
529
+ "-f",
530
+ help="Output format (table, json, csv)",
531
+ autocompletion=complete_output_format,
532
+ ),
533
+ output: Optional[str] = typer.Option(
534
+ None, "--output", "-o", help="Output file path"
535
+ ),
536
+ preview: bool = typer.Option(False, "--preview", help="Enable preview mode"),
537
+ ):
538
+ """Upload temporary media (auto-deleted after 1 hour if not persisted)."""
539
+ try:
540
+ # Validate file exists
541
+ file_path_obj = Path(file_path)
542
+ if not file_path_obj.exists():
543
+ formatter.print_error(f"File not found: {file_path}")
544
+ raise typer.Exit(1)
545
+
546
+ service = MediaSetsService(profile=profile)
547
+
548
+ file_size = file_path_obj.stat().st_size
549
+ formatter.print_info(f"Uploading {file_path} ({file_size} bytes)")
550
+
551
+ with SpinnerProgressTracker().track_spinner(
552
+ f"Uploading {file_path_obj.name}..."
553
+ ):
554
+ reference = service.upload_temp_media(
555
+ file_path,
556
+ filename=filename,
557
+ attribution=attribution,
558
+ preview=preview,
559
+ )
560
+
561
+ formatter.print_success("Successfully uploaded temporary media")
562
+ formatter.format_media_reference(reference, format, output)
563
+
564
+ if output:
565
+ formatter.print_success(f"Media reference saved to {output}")
566
+ else:
567
+ formatter.print_warning(
568
+ "This is a temporary upload. It will be deleted after 1 hour if not persisted."
569
+ )
570
+
571
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
572
+ formatter.print_error(f"Authentication error: {e}")
573
+ raise typer.Exit(1)
574
+ except FileNotFoundError as e:
575
+ formatter.print_error(str(e))
576
+ raise typer.Exit(1)
577
+ except Exception as e:
578
+ formatter.print_error(f"Failed to upload temporary media: {e}")
579
+ raise typer.Exit(1)
580
+
581
+
408
582
  @app.callback()
409
583
  def main():
410
584
  """
@@ -415,6 +589,8 @@ def main():
415
589
  - Create, commit, and abort upload transactions
416
590
  - Upload media files to media sets
417
591
  - Download media items from media sets
592
+ - Generate and retrieve image thumbnails
593
+ - Upload temporary media
418
594
 
419
595
  All operations require Resource Identifiers (RIDs) like:
420
596
  ri.mediasets.main.media-set.12345678-1234-1234-1234-123456789abc
pltr/commands/ontology.py CHANGED
@@ -15,6 +15,7 @@ from ..services.ontology import (
15
15
  QueryService,
16
16
  )
17
17
  from ..utils.formatting import OutputFormatter
18
+ from ..utils.pagination import PaginationConfig
18
19
  from ..utils.progress import SpinnerProgressTracker
19
20
  from ..auth.base import ProfileNotFoundError, MissingCredentialsError
20
21
 
@@ -176,36 +177,66 @@ def list_objects(
176
177
  None, "--output", "-o", help="Output file path"
177
178
  ),
178
179
  page_size: Optional[int] = typer.Option(
179
- None, "--page-size", help="Number of results per page"
180
+ None, "--page-size", help="Number of objects per page (default: from settings)"
181
+ ),
182
+ max_pages: Optional[int] = typer.Option(
183
+ 1, "--max-pages", help="Maximum number of pages to fetch (default: 1)"
184
+ ),
185
+ page_token: Optional[str] = typer.Option(
186
+ None, "--page-token", help="Page token to resume from previous response"
187
+ ),
188
+ all: bool = typer.Option(
189
+ False, "--all", help="Fetch all available pages (overrides --max-pages)"
180
190
  ),
181
191
  properties: Optional[str] = typer.Option(
182
192
  None, "--properties", help="Comma-separated list of properties to include"
183
193
  ),
184
194
  ):
185
- """List objects of a specific type."""
195
+ """
196
+ List objects of a specific type with pagination support.
197
+
198
+ By default, fetches only the first page of results. Use --all to fetch all objects,
199
+ or --max-pages to control how many pages to fetch.
200
+
201
+ Examples:
202
+ # List first page of objects (default)
203
+ pltr ontology object-list ONTOLOGY_RID ObjectType
204
+
205
+ # List all objects
206
+ pltr ontology object-list ONTOLOGY_RID ObjectType --all
207
+
208
+ # List first 3 pages
209
+ pltr ontology object-list ONTOLOGY_RID ObjectType --max-pages 3
210
+
211
+ # Resume from a specific page
212
+ pltr ontology object-list ONTOLOGY_RID ObjectType --page-token abc123
213
+ """
186
214
  try:
187
215
  service = OntologyObjectService(profile=profile)
188
216
 
189
217
  prop_list = properties.split(",") if properties else None
190
218
 
219
+ # Create pagination config
220
+ config = PaginationConfig(
221
+ page_size=page_size,
222
+ max_pages=max_pages,
223
+ page_token=page_token,
224
+ fetch_all=all,
225
+ )
226
+
191
227
  with SpinnerProgressTracker().track_spinner(
192
228
  f"Fetching {object_type} objects..."
193
229
  ):
194
- objects = service.list_objects(
195
- ontology_rid, object_type, page_size=page_size, properties=prop_list
230
+ result = service.list_objects_paginated(
231
+ ontology_rid, object_type, config, properties=prop_list
196
232
  )
197
233
 
198
- if format == "table" and objects:
199
- # Use first object's keys as columns
200
- columns = list(objects[0].keys()) if objects else []
201
- formatter.format_table(
202
- objects, columns=columns, format=format, output=output
203
- )
204
- else:
205
- formatter.format_list(objects, format=format, output=output)
206
-
234
+ # Format and display paginated results
207
235
  if output:
236
+ formatter.format_paginated_output(result, format, output)
208
237
  formatter.print_success(f"Objects saved to {output}")
238
+ else:
239
+ formatter.format_paginated_output(result, format)
209
240
 
210
241
  except (ProfileNotFoundError, MissingCredentialsError) as e:
211
242
  formatter.print_error(f"Authentication error: {e}")