das-cli 1.2.0__tar.gz → 1.2.5__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.
Files changed (50) hide show
  1. das_cli-1.2.5/PKG-INFO +1076 -0
  2. das_cli-1.2.5/README.md +1053 -0
  3. {das_cli-1.2.0 → das_cli-1.2.5}/das/ai/plugins/dasai.py +11 -2
  4. {das_cli-1.2.0 → das_cli-1.2.5}/das/cli.py +1234 -1219
  5. {das_cli-1.2.0 → das_cli-1.2.5}/das/managers/entries_manager.py +37 -1
  6. {das_cli-1.2.0 → das_cli-1.2.5}/das/services/downloads.py +100 -100
  7. {das_cli-1.2.0 → das_cli-1.2.5}/das/services/entries.py +29 -3
  8. das_cli-1.2.5/das_cli.egg-info/PKG-INFO +1076 -0
  9. {das_cli-1.2.0 → das_cli-1.2.5}/das_cli.egg-info/SOURCES.txt +10 -1
  10. {das_cli-1.2.0 → das_cli-1.2.5}/pyproject.toml +1 -1
  11. das_cli-1.2.5/tests/__init__.py +3 -0
  12. das_cli-1.2.5/tests/attributes_test.py +77 -0
  13. das_cli-1.2.5/tests/download_manager_test.py +34 -0
  14. das_cli-1.2.5/tests/entries_manager_test.py +119 -0
  15. das_cli-1.2.5/tests/entries_service_test.py +86 -0
  16. das_cli-1.2.5/tests/entries_test.py +37 -0
  17. das_cli-1.2.5/tests/file_utils_test.py +115 -0
  18. das_cli-1.2.5/tests/run_tests.py +18 -0
  19. das_cli-1.2.5/tests/search_manager_test.py +35 -0
  20. das_cli-1.2.0/PKG-INFO +0 -445
  21. das_cli-1.2.0/README.md +0 -422
  22. das_cli-1.2.0/das_cli.egg-info/PKG-INFO +0 -445
  23. {das_cli-1.2.0 → das_cli-1.2.5}/LICENSE +0 -0
  24. {das_cli-1.2.0 → das_cli-1.2.5}/MANIFEST.in +0 -0
  25. {das_cli-1.2.0 → das_cli-1.2.5}/das/__init__.py +0 -0
  26. {das_cli-1.2.0 → das_cli-1.2.5}/das/ai/plugins/entries/entries_plugin.py +0 -0
  27. {das_cli-1.2.0 → das_cli-1.2.5}/das/app.py +0 -0
  28. {das_cli-1.2.0 → das_cli-1.2.5}/das/authentication/auth.py +0 -0
  29. {das_cli-1.2.0 → das_cli-1.2.5}/das/authentication/secure_input.py +0 -0
  30. {das_cli-1.2.0 → das_cli-1.2.5}/das/common/api.py +0 -0
  31. {das_cli-1.2.0 → das_cli-1.2.5}/das/common/config.py +0 -0
  32. {das_cli-1.2.0 → das_cli-1.2.5}/das/common/entry_fields_constants.py +0 -0
  33. {das_cli-1.2.0 → das_cli-1.2.5}/das/common/enums.py +0 -0
  34. {das_cli-1.2.0 → das_cli-1.2.5}/das/common/file_utils.py +0 -0
  35. {das_cli-1.2.0 → das_cli-1.2.5}/das/managers/__init__.py +0 -0
  36. {das_cli-1.2.0 → das_cli-1.2.5}/das/managers/digital_objects_manager.py +0 -0
  37. {das_cli-1.2.0 → das_cli-1.2.5}/das/managers/download_manager.py +0 -0
  38. {das_cli-1.2.0 → das_cli-1.2.5}/das/managers/search_manager.py +0 -0
  39. {das_cli-1.2.0 → das_cli-1.2.5}/das/services/attributes.py +0 -0
  40. {das_cli-1.2.0 → das_cli-1.2.5}/das/services/cache.py +0 -0
  41. {das_cli-1.2.0 → das_cli-1.2.5}/das/services/digital_objects.py +0 -0
  42. {das_cli-1.2.0 → das_cli-1.2.5}/das/services/entry_fields.py +0 -0
  43. {das_cli-1.2.0 → das_cli-1.2.5}/das/services/hangfire.py +0 -0
  44. {das_cli-1.2.0 → das_cli-1.2.5}/das/services/search.py +0 -0
  45. {das_cli-1.2.0 → das_cli-1.2.5}/das/services/users.py +0 -0
  46. {das_cli-1.2.0 → das_cli-1.2.5}/das_cli.egg-info/dependency_links.txt +0 -0
  47. {das_cli-1.2.0 → das_cli-1.2.5}/das_cli.egg-info/entry_points.txt +0 -0
  48. {das_cli-1.2.0 → das_cli-1.2.5}/das_cli.egg-info/requires.txt +0 -0
  49. {das_cli-1.2.0 → das_cli-1.2.5}/das_cli.egg-info/top_level.txt +0 -0
  50. {das_cli-1.2.0 → das_cli-1.2.5}/setup.cfg +0 -0
das_cli-1.2.5/PKG-INFO ADDED
@@ -0,0 +1,1076 @@
1
+ Metadata-Version: 2.4
2
+ Name: das-cli
3
+ Version: 1.2.5
4
+ Summary: DAS api client.
5
+ Author: Royal Netherlands Institute for Sea Research
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://git.nioz.nl/ict-projects/das-cli
8
+ Project-URL: Bug Tracker, https://git.nioz.nl/ict-projects/das-cli/-/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: click>=8.2.1
15
+ Requires-Dist: requests>=2.32.3
16
+ Requires-Dist: python-dotenv>=1.0.0
17
+ Provides-Extra: excel
18
+ Requires-Dist: pandas>=2.2.3; extra == "excel"
19
+ Requires-Dist: openpyxl>=3.1.5; extra == "excel"
20
+ Provides-Extra: ai
21
+ Requires-Dist: semantic-kernel>=1.37.0; extra == "ai"
22
+ Dynamic: license-file
23
+
24
+ # DAS Python Client
25
+
26
+ A Python library and CLI for interacting with the Data Archive System (DAS) API.
27
+
28
+ ## Table of Contents
29
+
30
+ - Quickstart
31
+ - Installation
32
+ - CLI Usage
33
+ - Python API
34
+ - Configuration
35
+ - Examples
36
+ - Troubleshooting
37
+ - Development
38
+ - Contributing
39
+ - License
40
+
41
+ ## Quickstart
42
+
43
+ ```bash
44
+ # Install
45
+ pip install das-cli
46
+
47
+ # Login (prompts for credentials if omitted)
48
+ das login --api-url https://your-das-instance/api
49
+
50
+ # Search entries
51
+ das search entries --attribute Name --query 'name(*core*)' --format table
52
+
53
+ # Get an entry
54
+ das entry get --code 7b.b.4c
55
+ ```
56
+
57
+ ## Installation
58
+
59
+ ### Requirements
60
+
61
+ - Python 3.13 or higher
62
+
63
+ ### Install from PyPI
64
+
65
+ ```bash
66
+ pip install das-cli
67
+ ```
68
+
69
+ ### Install from Source
70
+
71
+ ```bash
72
+ git clone https://git.nioz.nl/ict-projects/das-cli.git
73
+ cd das-cli
74
+ pip install -e .
75
+ ```
76
+
77
+ ## CLI Usage
78
+
79
+ The package installs a `das` executable for interacting with the DAS API.
80
+
81
+ ### Authentication
82
+
83
+ ```bash
84
+ das login --api-url https://your-das-instance/api --username your_username --password your_password
85
+ ```
86
+
87
+ - Omit `--username`/`--password` to be prompted securely.
88
+
89
+ ### Search
90
+
91
+ ```bash
92
+ das search entries --attribute <AttributeName> --query '<SearchQuery>' \
93
+ --max-results 10 --page 1 --sort-by Name --sort-order asc --format table
94
+ ```
95
+
96
+ Options:
97
+
98
+ - `--max-results <n>`: Maximum results per page (default: 10)
99
+ - `--page <num>`: Page number (default: 1)
100
+ - `--sort-by <field>`: Field to sort by (default: Name)
101
+ - `--sort-order <order>`: `asc` or `desc` (default: asc)
102
+ - `--format <format>`: `table`, `json`, or `compact` (default: table)
103
+ - `--raw`: Print raw API response
104
+
105
+ Get a specific entry by ID:
106
+
107
+ ```bash
108
+ das search entry <ID> --format table
109
+ ```
110
+
111
+ Help for query syntax:
112
+
113
+ ```bash
114
+ das search help
115
+ ```
116
+
117
+ Example queries:
118
+
119
+ - Name contains pattern: `name(*pattern*)`
120
+ - Multiple conditions: `name(*pattern*);code(*ABC*)`
121
+ - Date comparison: `Created at(>2023-01-01)`
122
+
123
+ ### Entries
124
+
125
+ ```bash
126
+ # Get entry by code
127
+ das entry get --code CODE
128
+
129
+ # Get entry by ID
130
+ das entry get --id ID
131
+
132
+ # Delete an entry
133
+ das entry delete --code CODE
134
+ # Or by ID
135
+ das entry delete --id ID
136
+ # Skip confirmation prompt
137
+ das entry delete --code CODE --force
138
+
139
+ # Create entries from file or data
140
+ das entry create --attribute <AttributeName> <file_path>
141
+ # Examples:
142
+ # JSON file (can contain a list of entries)
143
+ # das entry create --attribute core c:\data\entries.json
144
+ # CSV file (rows become entries)
145
+ # das entry create --attribute core c:\data\entries.csv
146
+ # Excel file (rows become entries)
147
+ # das entry create --attribute core c:\data\entries.xls
148
+ # Single entry from data string
149
+ # das entry create --attribute core --data { 'Name': 'Entry 1', ... }
150
+ # Multiple entries from data string
151
+ # das entry create --attribute core --data [{ 'Name': 'Entry 1' }, { 'Name': 'Entry 2' }]
152
+
153
+ # Update entries from file or data
154
+ das entry update --attribute <AttributeName> [--code CODE] <file_path>
155
+ # Notes:
156
+ # - For bulk updates, each entry must include a Code field
157
+ # - For single updates via --data, you can pass --code or include Code in data
158
+ # Examples:
159
+ # JSON file (each object must include Code)
160
+ # das entry update --attribute core c:\data\entries.json
161
+ # CSV file (must have Code column)
162
+ # das entry update --attribute core c:\data\entries.csv
163
+ # Excel file (must have Code column)
164
+ # das entry update --attribute core c:\data\entries.xls
165
+ # Single entry with explicit code
166
+ # das entry update --attribute core --code ENT001 --data { 'Grant Public Access': Yes }
167
+ # Single entry with Code in data
168
+ # das entry update --attribute core --data { 'Code': 'ENT001', 'Grant Public Access': Yes }
169
+ # Multiple entries from data string (each must include Code)
170
+ # das entry update --attribute core --data [{ 'Code': 'ENT001' }, { 'Code': 'ENT002' }]
171
+ ```
172
+
173
+ #### Upload and link a digital object
174
+
175
+ ```bash
176
+ # Upload a file as a digital object and link it to an entry
177
+ das entry upload-digital-object --entry-code ENT001 --type Dataset --description "CTD raw" c:\data\ctd.zip
178
+ ```
179
+
180
+ #### Link or unlink digital objects
181
+
182
+ ```bash
183
+ # Link digital objects by their codes to an entry
184
+ das entry link-digital-objects --entry-code ENT001 -d DO001 -d DO002
185
+
186
+ # Unlink digital objects from an entry
187
+ das entry link-digital-objects --entry-code ENT001 -d DO003 --unlink
188
+ ```
189
+
190
+ #### Change ownership
191
+
192
+ ```bash
193
+ # Transfer ownership of one or more entries to a user
194
+ das entry chown --user alice --code ENT001 --code ENT002
195
+ ```
196
+
197
+ ### Hangfire
198
+
199
+ ```bash
200
+ das hangfire sync-doi ID
201
+ ```
202
+
203
+ ### Attributes
204
+
205
+ ```bash
206
+ # By ID
207
+ das attribute get --id 123
208
+
209
+ # By name
210
+ das attribute get --name "temperature"
211
+
212
+ # By alias
213
+ das attribute get --alias "temp"
214
+
215
+ # By table name
216
+ das attribute get --table-name "measurements"
217
+
218
+ # Converters
219
+ das attribute get-name 123
220
+ das attribute get-id "temperature"
221
+ ```
222
+
223
+ ### Cache
224
+
225
+ ```bash
226
+ das cache list
227
+ das cache clear "cache-name"
228
+ das cache clear-all
229
+ ```
230
+
231
+ ### Configuration
232
+
233
+ ```bash
234
+ # Enable SSL certificate verification (recommended)
235
+ das config ssl-verify true
236
+
237
+ # Disable SSL certificate verification (development/testing only)
238
+ das config ssl-verify false
239
+
240
+ # Show current SSL verification status
241
+ das config ssl-status
242
+
243
+ # Reset all configuration (clears credentials and settings)
244
+ das config reset --force
245
+ ```
246
+
247
+ ### AI
248
+
249
+ ```bash
250
+ # Start interactive DAS AI session (prompts for OpenAI API key if needed)
251
+ das ai enable
252
+
253
+ # Clear saved OpenAI key and auth token
254
+ das ai clear [--force]
255
+
256
+ # Alias for `das ai clear`
257
+ das ai logout [--force]
258
+ ```
259
+
260
+ ## Python API
261
+
262
+ The DAS CLI provides three layers of interaction: CLI commands, Manager layer, and Service layer. Below are examples for each command showing usage at all three layers.
263
+
264
+ ### Entries
265
+
266
+ #### Get Entry
267
+
268
+ **CLI:**
269
+ ```bash
270
+ # Get entry by code
271
+ das entry get --code 7b.b.4c
272
+
273
+ # Get entry by ID
274
+ das entry get --id 123
275
+ ```
276
+
277
+ **Manager Layer:**
278
+ ```python
279
+ from das.managers.entries_manager import EntryManager
280
+
281
+ entry_manager = EntryManager()
282
+
283
+ # Get entry by code
284
+ entry = entry_manager.get(code="7b.b.4c")
285
+
286
+ # Get entry by ID
287
+ entry = entry_manager.get(id="123")
288
+ ```
289
+
290
+ **Service Layer:**
291
+ ```python
292
+ from das.services.entries import EntriesService
293
+ from das.common.config import load_api_url
294
+
295
+ base_url = load_api_url()
296
+ entry_service = EntriesService(base_url)
297
+
298
+ # Get entry by code
299
+ entry = entry_service.get(code="7b.b.4c")
300
+
301
+ # Get entry by ID
302
+ entry = entry_service.get(id="123")
303
+ ```
304
+
305
+ #### Create Entry
306
+
307
+ **CLI:**
308
+ ```bash
309
+ # Create from JSON file
310
+ das entry create --attribute core c:\data\entries.json
311
+
312
+ # Create from CSV file
313
+ das entry create --attribute core c:\data\entries.csv
314
+
315
+ # Create single entry from data string
316
+ das entry create --attribute core --data "{ 'Name': 'Entry 1', 'Description': 'Test entry' }"
317
+
318
+ # Create multiple entries from data string
319
+ das entry create --attribute core --data "[{ 'Name': 'Entry 1' }, { 'Name': 'Entry 2' }]"
320
+ ```
321
+
322
+ **Manager Layer:**
323
+ ```python
324
+ from das.managers.entries_manager import EntryManager
325
+
326
+ entry_manager = EntryManager()
327
+
328
+ # Create single entry
329
+ result = entry_manager.create(
330
+ attribute="core",
331
+ entry={"Name": "Entry 1", "Description": "Test entry"}
332
+ )
333
+
334
+ # Create multiple entries
335
+ results = entry_manager.create(
336
+ attribute="core",
337
+ entries=[
338
+ {"Name": "Entry 1", "Description": "Test entry 1"},
339
+ {"Name": "Entry 2", "Description": "Test entry 2"}
340
+ ]
341
+ )
342
+ ```
343
+
344
+ **Service Layer:**
345
+ ```python
346
+ from das.services.entries import EntriesService
347
+ from das.services.attributes import AttributesService
348
+ from das.common.config import load_api_url
349
+
350
+ base_url = load_api_url()
351
+ entry_service = EntriesService(base_url)
352
+ attribute_service = AttributesService(base_url)
353
+
354
+ # Get attribute ID first
355
+ attribute_id = attribute_service.get_id(name="core")
356
+
357
+ # Create entry
358
+ entry_id = entry_service.create(
359
+ attribute_id=attribute_id,
360
+ entry={"name": "Entry 1", "description": "Test entry"}
361
+ )
362
+ ```
363
+
364
+ #### Update Entry
365
+
366
+ **CLI:**
367
+ ```bash
368
+ # Update from JSON file
369
+ das entry update --attribute core c:\data\entries.json
370
+
371
+ # Update from CSV file
372
+ das entry update --attribute core c:\data\entries.csv
373
+
374
+ # Update single entry with explicit code
375
+ das entry update --attribute core --code ENT001 --data "{ 'Grant Public Access': 'Yes' }"
376
+
377
+ # Update single entry with Code in data
378
+ das entry update --attribute core --data "{ 'Code': 'ENT001', 'Grant Public Access': 'Yes' }"
379
+
380
+ # Update multiple entries from data string
381
+ das entry update --attribute core --data "[{ 'Code': 'ENT001', 'Name': 'Updated 1' }, { 'Code': 'ENT002', 'Name': 'Updated 2' }]"
382
+ ```
383
+
384
+ **Manager Layer:**
385
+ ```python
386
+ from das.managers.entries_manager import EntryManager
387
+
388
+ entry_manager = EntryManager()
389
+
390
+ # Update single entry
391
+ result = entry_manager.update(
392
+ attribute="core",
393
+ code="ENT001",
394
+ entry={"Grant Public Access": "Yes"}
395
+ )
396
+
397
+ # Update multiple entries
398
+ results = entry_manager.update(
399
+ attribute="core",
400
+ entries=[
401
+ {"Code": "ENT001", "Name": "Updated Entry 1"},
402
+ {"Code": "ENT002", "Name": "Updated Entry 2"}
403
+ ]
404
+ )
405
+ ```
406
+
407
+ **Service Layer:**
408
+ ```python
409
+ from das.services.entries import EntriesService
410
+ from das.services.attributes import AttributesService
411
+ from das.common.config import load_api_url
412
+
413
+ base_url = load_api_url()
414
+ entry_service = EntriesService(base_url)
415
+ attribute_service = AttributesService(base_url)
416
+
417
+ # Get existing entry to retrieve attribute_id
418
+ existing_entry = entry_service.get(code="ENT001")
419
+ attribute_id = existing_entry.get("attributeId")
420
+
421
+ # Update entry
422
+ entry_id = entry_service.update(
423
+ attribute_id=attribute_id,
424
+ entry={"name": "Updated Entry 1", "grantpublicaccess": "Yes"}
425
+ )
426
+ ```
427
+
428
+ #### Delete Entry
429
+
430
+ **CLI:**
431
+ ```bash
432
+ # Delete by code (with confirmation)
433
+ das entry delete --code ENT001
434
+
435
+ # Delete by code (skip confirmation)
436
+ das entry delete --code ENT001 --force
437
+
438
+ # Delete by ID
439
+ das entry delete --id 123
440
+ ```
441
+
442
+ **Manager Layer:**
443
+ ```python
444
+ from das.managers.entries_manager import EntryManager
445
+
446
+ entry_manager = EntryManager()
447
+
448
+ # Delete by code
449
+ success = entry_manager.delete(code="ENT001")
450
+
451
+ # Delete by ID
452
+ success = entry_manager.delete(id="123")
453
+ ```
454
+
455
+ **Service Layer:**
456
+ ```python
457
+ from das.services.entries import EntriesService
458
+ from das.common.config import load_api_url
459
+
460
+ base_url = load_api_url()
461
+ entry_service = EntriesService(base_url)
462
+
463
+ # Delete by code
464
+ success = entry_service.delete(code="ENT001")
465
+
466
+ # Delete by ID
467
+ success = entry_service.delete_by_id(id="123")
468
+ ```
469
+
470
+ #### Change Ownership
471
+
472
+ **CLI:**
473
+ ```bash
474
+ # Transfer ownership of entries to a user
475
+ das entry chown --user alice --code ENT001 --code ENT002
476
+ ```
477
+
478
+ **Manager Layer:**
479
+ ```python
480
+ from das.managers.entries_manager import EntryManager
481
+
482
+ entry_manager = EntryManager()
483
+
484
+ # Change ownership
485
+ result = entry_manager.chown(
486
+ user_name="alice",
487
+ entry_code_list=["ENT001", "ENT002"]
488
+ )
489
+ ```
490
+
491
+ **Service Layer:**
492
+ ```python
493
+ from das.services.entries import EntriesService
494
+ from das.services.users import UsersService
495
+ from das.common.config import load_api_url
496
+
497
+ base_url = load_api_url()
498
+ entry_service = EntriesService(base_url)
499
+ user_service = UsersService(base_url)
500
+
501
+ # Get user ID
502
+ user = user_service.get_user("alice")
503
+ user_id = user.get("id")
504
+
505
+ # Get entry IDs
506
+ entry1 = entry_service.get(code="ENT001")
507
+ entry2 = entry_service.get(code="ENT002")
508
+ entry_ids = [entry1.get("entry", {}).get("id"), entry2.get("entry", {}).get("id")]
509
+
510
+ # Change ownership
511
+ result = entry_service.chown(
512
+ new_user_id=user_id,
513
+ entry_list_ids=entry_ids
514
+ )
515
+ ```
516
+
517
+ ### Search
518
+
519
+ **CLI:**
520
+ ```bash
521
+ # Search entries
522
+ das search entries --attribute Cores --query "name(*64*)" --max-results 10 --page 1 --sort-by Name --sort-order asc --format table
523
+
524
+ # Get detailed entry by ID
525
+ das search entry 6b0e68e6-00cd-43a7-9c51-d56c9c091123 --format json
526
+ ```
527
+
528
+ **Manager Layer:**
529
+ ```python
530
+ from das.managers.search_manager import SearchManager
531
+
532
+ search_manager = SearchManager()
533
+
534
+ # Search entries
535
+ results = search_manager.search_entries(
536
+ attribute="Cores",
537
+ query="name(*64*)",
538
+ max_results=10,
539
+ page=1,
540
+ sort_by="Name",
541
+ sort_order="asc"
542
+ )
543
+ ```
544
+
545
+ **Service Layer:**
546
+ ```python
547
+ from das.services.search import SearchService
548
+ from das.services.attributes import AttributesService
549
+ from das.common.config import load_api_url
550
+
551
+ base_url = load_api_url()
552
+ search_service = SearchService(base_url)
553
+ attribute_service = AttributesService(base_url)
554
+
555
+ # Get attribute ID
556
+ attr_response = attribute_service.get_attribute(name="Cores")
557
+ attribute_id = attr_response.get("result", {}).get("items", [])[0].get("id")
558
+
559
+ # Search entries
560
+ results = search_service.search_entries(
561
+ attributeId=attribute_id,
562
+ queryString="name(*64*);",
563
+ maxResultCount=10,
564
+ skipCount=0,
565
+ sorting="name asc"
566
+ )
567
+ ```
568
+
569
+ ### Downloads
570
+
571
+ #### Create Download Request
572
+
573
+ **CLI:**
574
+ ```bash
575
+ # Create request for all files from an entry
576
+ das download request --entry ENT001 --name "My Download Request"
577
+
578
+ # Create request with specific files
579
+ das download request --entry ENT001 --file FILE001 --file FILE002
580
+
581
+ # Create request from JSON file
582
+ das download request --from-file request.json
583
+ ```
584
+
585
+ **Manager Layer:**
586
+ ```python
587
+ from das.managers.download_manager import DownloadManager
588
+
589
+ download_manager = DownloadManager()
590
+
591
+ # Create download request
592
+ request_data = {
593
+ 'name': 'My Download Request',
594
+ 'ENT001': ['FILE001', 'FILE002'], # Specific files
595
+ 'ENT002': [] # All files from this entry
596
+ }
597
+ request_id = download_manager.create_download_request(request_data)
598
+ ```
599
+
600
+ **Service Layer:**
601
+ ```python
602
+ from das.services.downloads import DownloadRequestService
603
+ from das.common.config import load_api_url
604
+
605
+ base_url = load_api_url()
606
+ download_service = DownloadRequestService(base_url)
607
+
608
+ # Create download request (requires formatted request items)
609
+ request_items = {
610
+ 'items': [
611
+ {
612
+ 'name': 'My Download Request',
613
+ 'sourceId': 'entry_id_1',
614
+ 'sourceAttributeId': 123,
615
+ 'id': 'digital_object_id_1'
616
+ }
617
+ ]
618
+ }
619
+ request_id = download_service.create(request_items)
620
+ ```
621
+
622
+ #### List Download Requests
623
+
624
+ **CLI:**
625
+ ```bash
626
+ # List your download requests
627
+ das download my-requests --format table
628
+ ```
629
+
630
+ **Manager Layer:**
631
+ ```python
632
+ from das.managers.download_manager import DownloadManager
633
+
634
+ download_manager = DownloadManager()
635
+
636
+ # Get all download requests
637
+ my_requests = download_manager.get_my_requests()
638
+ ```
639
+
640
+ **Service Layer:**
641
+ ```python
642
+ from das.services.downloads import DownloadRequestService
643
+ from das.common.config import load_api_url
644
+
645
+ base_url = load_api_url()
646
+ download_service = DownloadRequestService(base_url)
647
+
648
+ # Get all download requests
649
+ my_requests = download_service.get_my_requests()
650
+ ```
651
+
652
+ #### Delete Download Request
653
+
654
+ **CLI:**
655
+ ```bash
656
+ # Delete a download request
657
+ das download delete-request 6b0e68e6-00cd-43a7-9c51-d56c9c091123
658
+ ```
659
+
660
+ **Manager Layer:**
661
+ ```python
662
+ from das.managers.download_manager import DownloadManager
663
+
664
+ download_manager = DownloadManager()
665
+
666
+ # Delete download request
667
+ result = download_manager.delete_download_request("6b0e68e6-00cd-43a7-9c51-d56c9c091123")
668
+ ```
669
+
670
+ **Service Layer:**
671
+ ```python
672
+ from das.services.downloads import DownloadRequestService
673
+ from das.common.config import load_api_url
674
+
675
+ base_url = load_api_url()
676
+ download_service = DownloadRequestService(base_url)
677
+
678
+ # Delete download request
679
+ result = download_service.delete("6b0e68e6-00cd-43a7-9c51-d56c9c091123")
680
+ ```
681
+
682
+ #### Download Files
683
+
684
+ **CLI:**
685
+ ```bash
686
+ # Download files to current directory
687
+ das download files 6b0e68e6-00cd-43a7-9c51-d56c9c091123
688
+
689
+ # Download to specific folder
690
+ das download files 6b0e68e6-00cd-43a7-9c51-d56c9c091123 --out C:\Downloads
691
+
692
+ # Download with explicit filename, overwriting if exists
693
+ das download files 6b0e68e6-00cd-43a7-9c51-d56c9c091123 --out C:\Downloads\bundle.zip --force
694
+ ```
695
+
696
+ **Manager Layer:**
697
+ ```python
698
+ from das.managers.download_manager import DownloadManager
699
+
700
+ download_manager = DownloadManager()
701
+
702
+ # Save download to disk
703
+ saved_path = download_manager.save_download(
704
+ request_id="6b0e68e6-00cd-43a7-9c51-d56c9c091123",
705
+ output_path="C:\\Downloads",
706
+ overwrite=False
707
+ )
708
+ ```
709
+
710
+ **Service Layer:**
711
+ ```python
712
+ from das.services.downloads import DownloadRequestService
713
+ from das.common.config import load_api_url
714
+
715
+ base_url = load_api_url()
716
+ download_service = DownloadRequestService(base_url)
717
+
718
+ # Get streaming response
719
+ response = download_service.download_files("6b0e68e6-00cd-43a7-9c51-d56c9c091123")
720
+
721
+ # Save to disk manually
722
+ with open("download.zip", "wb") as f:
723
+ for chunk in response.iter_content(chunk_size=8192):
724
+ if chunk:
725
+ f.write(chunk)
726
+ ```
727
+
728
+ ### Attributes
729
+
730
+ **CLI:**
731
+ ```bash
732
+ # Get attribute by ID
733
+ das attribute get --id 123
734
+
735
+ # Get attribute by name
736
+ das attribute get --name "temperature"
737
+
738
+ # Get attribute name by ID
739
+ das attribute get-name 123
740
+
741
+ # Get attribute ID by name
742
+ das attribute get-id "temperature"
743
+ ```
744
+
745
+ **Manager Layer:**
746
+ ```python
747
+ from das.services.attributes import AttributesService
748
+ from das.common.config import load_api_url
749
+
750
+ base_url = load_api_url()
751
+ attribute_service = AttributesService(base_url)
752
+
753
+ # Get attribute by various methods
754
+ attribute = attribute_service.get_attribute(id=123)
755
+ attribute = attribute_service.get_attribute(name="temperature")
756
+ attribute = attribute_service.get_attribute(alias="temp")
757
+ attribute = attribute_service.get_attribute(table_name="measurements")
758
+
759
+ # Get name/ID converters
760
+ name = attribute_service.get_name(123)
761
+ attr_id = attribute_service.get_id("temperature")
762
+ ```
763
+
764
+ **Service Layer:**
765
+ ```python
766
+ from das.services.attributes import AttributesService
767
+ from das.common.config import load_api_url
768
+
769
+ base_url = load_api_url()
770
+ attribute_service = AttributesService(base_url)
771
+
772
+ # Get attribute
773
+ result = attribute_service.get_attribute(id=123, name="temperature", alias="temp", table_name="measurements")
774
+
775
+ # Get name/ID converters
776
+ name = attribute_service.get_name(123)
777
+ attr_id = attribute_service.get_id("temperature")
778
+ ```
779
+
780
+ ### Cache
781
+
782
+ **CLI:**
783
+ ```bash
784
+ # List all cache entries
785
+ das cache list
786
+
787
+ # Clear a specific cache
788
+ das cache clear "cache-name"
789
+
790
+ # Clear all caches
791
+ das cache clear-all
792
+ ```
793
+
794
+ **Manager Layer:**
795
+ ```python
796
+ from das.app import Das
797
+
798
+ client = Das("https://your-das-instance/api")
799
+ client.authenticate("username", "password")
800
+
801
+ # List all caches
802
+ cache_entries = client.cache.get_all()
803
+
804
+ # Clear specific cache
805
+ result = client.cache.clear_cache("cache-name")
806
+
807
+ # Clear all caches
808
+ result = client.cache.clear_all()
809
+ ```
810
+
811
+ **Service Layer:**
812
+ ```python
813
+ from das.services.cache import CacheService
814
+ from das.common.config import load_api_url
815
+
816
+ base_url = load_api_url()
817
+ cache_service = CacheService(base_url)
818
+
819
+ # List all caches
820
+ cache_entries = cache_service.get_all()
821
+
822
+ # Clear specific cache
823
+ result = cache_service.clear_cache("cache-name")
824
+
825
+ # Clear all caches
826
+ result = cache_service.clear_all()
827
+ ```
828
+
829
+ ### Hangfire
830
+
831
+ **CLI:**
832
+ ```bash
833
+ # Trigger DOI synchronization
834
+ das hangfire sync-doi 123
835
+ ```
836
+
837
+ **Manager Layer:**
838
+ ```python
839
+ from das.app import Das
840
+
841
+ client = Das("https://your-das-instance/api")
842
+ client.authenticate("username", "password")
843
+
844
+ # Sync DOI
845
+ client.hangfire.sync_doi("123")
846
+ ```
847
+
848
+ **Service Layer:**
849
+ ```python
850
+ from das.services.hangfire import HangfireService
851
+ from das.common.config import load_api_url
852
+
853
+ base_url = load_api_url()
854
+ hangfire_service = HangfireService(base_url)
855
+
856
+ # Sync DOI
857
+ result = hangfire_service.sync_doi("123")
858
+ ```
859
+
860
+ ### Digital Objects
861
+
862
+ #### Upload Digital Object
863
+
864
+ **CLI:**
865
+ ```bash
866
+ # Upload a file as digital object and link to entry
867
+ das entry upload-digital-object --entry-code ENT001 --type Dataset --description "CTD raw" c:\data\ctd.zip
868
+ ```
869
+
870
+ **Manager Layer:**
871
+ ```python
872
+ from das.managers.digital_objects_manager import DigitalObjectsManager
873
+
874
+ digital_objects_manager = DigitalObjectsManager()
875
+
876
+ # Upload digital object
877
+ digital_object_id = digital_objects_manager.upload_digital_object(
878
+ entry_code="ENT001",
879
+ file_description="CTD raw",
880
+ digital_object_type="Dataset",
881
+ file_path="c:\\data\\ctd.zip"
882
+ )
883
+ ```
884
+
885
+ #### Link/Unlink Digital Objects
886
+
887
+ **CLI:**
888
+ ```bash
889
+ # Link digital objects to an entry
890
+ das entry link-digital-objects --entry-code ENT001 -d DO001 -d DO002
891
+
892
+ # Unlink digital objects from an entry
893
+ das entry link-digital-objects --entry-code ENT001 -d DO003 --unlink
894
+ ```
895
+
896
+ **Manager Layer:**
897
+ ```python
898
+ from das.managers.digital_objects_manager import DigitalObjectsManager
899
+
900
+ digital_objects_manager = DigitalObjectsManager()
901
+
902
+ # Link digital objects
903
+ success = digital_objects_manager.link_existing_digital_objects(
904
+ entry_code="ENT001",
905
+ digital_object_code_list=["DO001", "DO002"],
906
+ is_unlink=False
907
+ )
908
+
909
+ # Unlink digital objects
910
+ success = digital_objects_manager.link_existing_digital_objects(
911
+ entry_code="ENT001",
912
+ digital_object_code_list=["DO003"],
913
+ is_unlink=True
914
+ )
915
+ ```
916
+
917
+ ### Configuration
918
+
919
+ **CLI:**
920
+ ```bash
921
+ # Enable SSL verification
922
+ das config ssl-verify true
923
+
924
+ # Disable SSL verification
925
+ das config ssl-verify false
926
+
927
+ # Check SSL status
928
+ das config ssl-status
929
+
930
+ # Reset all configuration
931
+ das config reset --force
932
+ ```
933
+
934
+ **Python:**
935
+ ```python
936
+ from das.common.config import save_verify_ssl, load_verify_ssl, save_api_url, load_api_url
937
+
938
+ # SSL verification
939
+ save_verify_ssl(True) # enable (recommended)
940
+ save_verify_ssl(False) # disable (dev/testing)
941
+ current_setting = load_verify_ssl()
942
+
943
+ # API URL
944
+ save_api_url("https://your-das-instance/api")
945
+ api_url = load_api_url()
946
+ ```
947
+
948
+ ## Configuration
949
+
950
+ ### SSL certificate verification
951
+
952
+ ```bash
953
+ # Enable (default)
954
+ das config ssl-verify true
955
+
956
+ # Disable (development/testing only)
957
+ das config ssl-verify false
958
+
959
+ # Check status
960
+ das config ssl-status
961
+ ```
962
+
963
+ ## Downloads
964
+
965
+ ```bash
966
+ # Create a download request
967
+ das download request --entry ENT001 --name "My Download Request"
968
+
969
+ # Create a request with specific files
970
+ das download request --entry ENT001 --file FILE001 --file FILE002
971
+
972
+ # Create a request with multiple entries
973
+ das download request --entry ENT001 --entry ENT002 --name "Multiple Entries"
974
+
975
+ # Create a request from JSON file
976
+ das download request --from-file request.json
977
+
978
+ # List your download requests
979
+ das download my-requests --format table
980
+
981
+ # Delete a download request
982
+ das download delete-request 6b0e68e6-00cd-43a7-9c51-d56c9c091123
983
+
984
+ # Download files for a completed request
985
+ das download files 6b0e68e6-00cd-43a7-9c51-d56c9c091123
986
+
987
+ # Save to a specific folder
988
+ das download files 6b0e68e6-00cd-43a7-9c51-d56c9c091123 --out C:\Downloads
989
+
990
+ # Save to an explicit filename, overwriting if it exists
991
+ das download files 6b0e68e6-00cd-43a7-9c51-d56c9c091123 --out C:\Downloads\bundle.zip --force
992
+ ```
993
+
994
+ ## Examples
995
+
996
+ ```bash
997
+ # List cache and clear a specific cache
998
+ das cache list
999
+ das cache clear "attributes"
1000
+
1001
+ # Dump a single entry as JSON
1002
+ das search entry 123 --format json
1003
+
1004
+ # Paginate through search results
1005
+ das search entries --attribute Name --query 'name(*core*)' --max-results 50 --page 2
1006
+
1007
+ # List your download requests
1008
+ das download my-requests --format table
1009
+
1010
+ # Reset all configuration
1011
+ das config reset --force
1012
+ ```
1013
+
1014
+ ## Troubleshooting
1015
+
1016
+ - Authentication failed:
1017
+ - Ensure `--api-url` points to the correct DAS instance base URL (often ends with `/api`).
1018
+ - Try re-running `das login` without passing password to be prompted.
1019
+ - SSL certificate errors:
1020
+ - Use `das config ssl-verify false` temporarily in non-production environments.
1021
+ - Consider configuring your corporate CA store instead of disabling verification.
1022
+ - Proxy/network issues:
1023
+ - Respect environment variables `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` if required.
1024
+ - Windows PowerShell quoting:
1025
+ - Prefer single quotes for queries, or escape `*` and `;` as needed (e.g., `\*`).
1026
+ - Unexpected output formatting:
1027
+ - Switch format with `--format json` or `--format compact`.
1028
+
1029
+ ## Dependencies
1030
+
1031
+ Runtime and development dependencies are declared in `requirements.txt` and `pyproject.toml`. Install via `pip install das-cli` or `pip install -e .` for development.
1032
+
1033
+ ## Development
1034
+
1035
+ ### Setting Up Development Environment
1036
+
1037
+ ```bash
1038
+ # Clone the repository
1039
+ git clone https://git.nioz.nl/ict-projects/das-cli.git
1040
+ cd das-cli
1041
+
1042
+ # Create and activate a virtual environment
1043
+ python -m venv venv
1044
+ .\venv\Scripts\activate # On Windows
1045
+ source venv/bin/activate # On Unix/macOS
1046
+
1047
+ # Install in editable mode
1048
+ pip install -e .
1049
+
1050
+ # (Optional) Install dev/test tooling
1051
+ pip install -e .[dev]
1052
+ ```
1053
+
1054
+ ### Running Tests
1055
+
1056
+ ```bash
1057
+ # Run all tests
1058
+ python run_tests.py
1059
+
1060
+ # Run a specific test file
1061
+ python -m pytest tests/specific_test_file.py
1062
+ ```
1063
+
1064
+ ## Contributing
1065
+
1066
+ Contributions are welcome! Please open a Pull Request.
1067
+
1068
+ Report bugs or request features via the [DAS CLI issue tracker](https://git.nioz.nl/ict-projects/das-cli/-/issues).
1069
+
1070
+ ## License
1071
+
1072
+ MIT License
1073
+
1074
+ ## Maintainers
1075
+
1076
+ This project is maintained by the Royal Netherlands Institute for Sea Research (NIOZ).