cwms-cli 0.1.1__tar.gz → 0.2.1__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 (51) hide show
  1. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/PKG-INFO +13 -4
  2. cwms_cli-0.2.1/README.md +26 -0
  3. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/__init__.py +2 -0
  4. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/__main__.py +2 -0
  5. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/blob.py +105 -107
  6. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/commands_cwms.py +39 -10
  7. cwms_cli-0.2.1/cwmscli/load/README.md +7 -0
  8. cwms_cli-0.2.1/cwmscli/load/__init__.py +0 -0
  9. cwms_cli-0.2.1/cwmscli/load/__main__.py +5 -0
  10. cwms_cli-0.2.1/cwmscli/load/location/location.py +131 -0
  11. cwms_cli-0.2.1/cwmscli/load/location/location_ids.py +83 -0
  12. cwms_cli-0.2.1/cwmscli/load/location/location_ids_bygroup.py +127 -0
  13. cwms_cli-0.2.1/cwmscli/load/root.py +123 -0
  14. cwms_cli-0.2.1/cwmscli/load/timeseries/timeseries.py +55 -0
  15. cwms_cli-0.2.1/cwmscli/load/timeseries/timeseries_ids.py +71 -0
  16. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/usgs/__init__.py +24 -16
  17. cwms_cli-0.2.1/cwmscli/usgs/__main__.py +7 -0
  18. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/usgs/getUSGS_ratings_cda.py +26 -12
  19. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/usgs/getusgs_cda.py +7 -2
  20. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/usgs/getusgs_measurements_cda.py +12 -12
  21. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/usgs/rating_ini_file_import.py +0 -1
  22. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/utils/__init__.py +6 -6
  23. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/utils/deps.py +13 -1
  24. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/pyproject.toml +3 -3
  25. cwms_cli-0.1.1/README.md +0 -17
  26. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/LICENSE +0 -0
  27. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/callbacks/__init__.py +0 -0
  28. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/.gitignore +0 -0
  29. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/README.md +0 -0
  30. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/__init__.py +0 -0
  31. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/__main__.py +0 -0
  32. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/examples/complete_config.json +0 -0
  33. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/examples/hourly.json +0 -0
  34. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/examples/minutes.json +0 -0
  35. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/tests/__init__.py +0 -0
  36. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/tests/data/.gitignore +0 -0
  37. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/tests/data/expected_brok_output.json +0 -0
  38. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/tests/data/sample_brok.csv +0 -0
  39. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/tests/data/sample_config.json +0 -0
  40. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/tests/skip_test_integration_pipeline.py +0 -0
  41. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/tests/test_dateutils.py +0 -0
  42. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/tests/test_expressions.py +0 -0
  43. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/tests/test_fileio.py +0 -0
  44. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/utils/__init__.py +0 -0
  45. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/utils/dateutils.py +0 -0
  46. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/utils/expression.py +0 -0
  47. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/utils/fileio.py +0 -0
  48. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/utils/logging.py +0 -0
  49. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/csv2cwms/utils/terminal.py +0 -0
  50. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/commands/shef_critfile_import.py +0 -0
  51. {cwms_cli-0.1.1 → cwms_cli-0.2.1}/cwmscli/requirements.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cwms-cli
3
- Version: 0.1.1
3
+ Version: 0.2.1
4
4
  Summary: Command line utilities for Corps Water Management Systems (CWMS) python scripts. This is a collection of shared scripts across the enterprise Water Management Enterprise System (WMES) teams.
5
5
  License: LICENSE
6
6
  License-File: LICENSE
@@ -22,19 +22,28 @@ Description-Content-Type: text/markdown
22
22
 
23
23
  # cwms-cli
24
24
 
25
- command line utilities used for Corps Water Management Systems (CWMS) processes
25
+ A collection of scripts to create, read, update, list, and delete data through CWMS Data API (CDA) and other commonly used API in the US Army Corps of Engineers water management. CWMS-CLI wraps these API in a friendly to use terminal based interface.
26
26
 
27
- [![Docs](https://readthedocs.org/projects/cwms-cli/badge/?version=latest)](https://cwms-cli.readthedocs.io/en/latest/)
27
+ [![Docs](https://readthedocs.org/projects/cwms-cli/badge/?version=latest)](https://cwms-cli.readthedocs.io/en/latest/) - 📖 Read the docs: https://cwms-cli.readthedocs.io/en/latest/
28
28
 
29
29
  ## Install
30
30
 
31
31
  ```sh
32
- pip install git+https://github.com/HydrologicEngineeringCenter/cwms-cli.git@main
32
+ pip3 install git+https://github.com/HydrologicEngineeringCenter/cwms-cli.git@main
33
33
  ```
34
+ Note: If you are on Windows OS, you may just need to use the command `pip`
34
35
 
35
36
  ## Command line implementation
36
37
 
38
+ View the help in terminal:
37
39
  ```sh
38
40
  cwms-cli --help
39
41
  ```
40
42
 
43
+ ## run from within python
44
+ ```python
45
+ from cwmscli.usgs.getusgs_cda import getusgs_cda
46
+ from cwmscli.usgs.getusgs_measurements_cda import getusgs_measurements_cda
47
+ from cwmscli.usgs.getUSGS_ratings_cda import getusgs_rating_cda
48
+ ```
49
+
@@ -0,0 +1,26 @@
1
+ # cwms-cli
2
+
3
+ A collection of scripts to create, read, update, list, and delete data through CWMS Data API (CDA) and other commonly used API in the US Army Corps of Engineers water management. CWMS-CLI wraps these API in a friendly to use terminal based interface.
4
+
5
+ [![Docs](https://readthedocs.org/projects/cwms-cli/badge/?version=latest)](https://cwms-cli.readthedocs.io/en/latest/) - 📖 Read the docs: https://cwms-cli.readthedocs.io/en/latest/
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ pip3 install git+https://github.com/HydrologicEngineeringCenter/cwms-cli.git@main
11
+ ```
12
+ Note: If you are on Windows OS, you may just need to use the command `pip`
13
+
14
+ ## Command line implementation
15
+
16
+ View the help in terminal:
17
+ ```sh
18
+ cwms-cli --help
19
+ ```
20
+
21
+ ## run from within python
22
+ ```python
23
+ from cwmscli.usgs.getusgs_cda import getusgs_cda
24
+ from cwmscli.usgs.getusgs_measurements_cda import getusgs_measurements_cda
25
+ from cwmscli.usgs.getUSGS_ratings_cda import getusgs_rating_cda
26
+ ```
@@ -1,5 +1,7 @@
1
1
  import logging as lg
2
2
 
3
+ from cwmscli import load
4
+
3
5
  # create logging for logging
4
6
  logging = lg.getLogger()
5
7
  if logging.hasHandlers():
@@ -1,6 +1,7 @@
1
1
  import click
2
2
 
3
3
  from cwmscli.commands import commands_cwms
4
+ from cwmscli.load import __main__ as load
4
5
  from cwmscli.usgs import usgs_group
5
6
 
6
7
 
@@ -13,3 +14,4 @@ cli.add_command(usgs_group, name="usgs")
13
14
  cli.add_command(commands_cwms.shefcritimport)
14
15
  cli.add_command(commands_cwms.csv2cwms_cmd)
15
16
  cli.add_command(commands_cwms.blob_group)
17
+ cli.add_command(load.load_group)
@@ -7,10 +7,6 @@ import re
7
7
  import sys
8
8
  from typing import Optional, Sequence
9
9
 
10
- import cwms
11
- import pandas as pd
12
- import requests
13
-
14
10
  from cwmscli.utils import get_api_key
15
11
  from cwmscli.utils.deps import requires
16
12
 
@@ -27,22 +23,21 @@ DATA_URL_RE = re.compile(r"^data:(?P<mime>[^;]+);base64,(?P<data>.+)$", re.I | r
27
23
  "link": "https://docs.python.org/3/library/imghdr.html",
28
24
  }
29
25
  )
30
- def _determine_ext(data: bytes | str, write_type: str) -> str:
26
+ def _determine_ext(data: bytes) -> str:
31
27
  """
32
28
  Attempt to determine the file extension from the data itself.
33
29
  Requires the imghdr module (lazy import) to inspect the bytes for image types.
34
30
  If not an image, defaults to .bin
35
31
 
36
32
  Args:
37
- data: The binary data or base64 string to inspect.
38
- write_type: The mode in which the data will be written ('wb' for binary, 'w' for text).
33
+ data: The binary data to inspect.
39
34
 
40
35
  Returns:
41
36
  The determined file extension, including the leading dot (e.g., '.png', '.jpg').
42
37
  """
43
38
  import imghdr
44
39
 
45
- kind = imghdr.what(None, data)
40
+ kind: Optional[str] = imghdr.what(None, data)
46
41
  if kind == "jpeg":
47
42
  kind = "jpg"
48
43
  return f".{kind}" if kind else ".bin"
@@ -51,7 +46,7 @@ def _determine_ext(data: bytes | str, write_type: str) -> str:
51
46
  def _save_base64(
52
47
  b64_or_dataurl: str,
53
48
  dest: str,
54
- media_type_hint: str | None = None,
49
+ media_type_hint: Optional[str] = None,
55
50
  ) -> str:
56
51
  m = DATA_URL_RE.match(b64_or_dataurl.strip())
57
52
  if m:
@@ -80,8 +75,8 @@ def _save_base64(
80
75
  ext = ".jpg"
81
76
  # last resort, try to determine from the data itself
82
77
  # requires imghdr to dig into the bytes to determine image type
83
- if not ext:
84
- ext = _determine_ext(data, write_type)
78
+ if not ext and isinstance(data, bytes):
79
+ ext = _determine_ext(data)
85
80
  dest = base + ext
86
81
 
87
82
  os.makedirs(os.path.dirname(dest) or ".", exist_ok=True)
@@ -91,8 +86,11 @@ def _save_base64(
91
86
 
92
87
 
93
88
  def store_blob(**kwargs):
89
+ import cwms
90
+ import requests
91
+
94
92
  file_data = kwargs.get("file_data")
95
- blob_id = kwargs.get("blob_id").upper()
93
+ blob_id = kwargs.get("blob_id", "").upper()
96
94
  # Attempt to determine what media type should be used for the mime-type if one is not presented based on the file extension
97
95
  media = kwargs.get("media_type") or get_media_type(kwargs.get("input_file"))
98
96
 
@@ -146,13 +144,15 @@ def store_blob(**kwargs):
146
144
 
147
145
 
148
146
  def retrieve_blob(**kwargs):
149
- blob_id = kwargs.get("blob_id", "")
147
+ import cwms
148
+ import requests
149
+
150
+ blob_id = kwargs.get("blob_id", "").upper()
150
151
  if not blob_id:
151
152
  logging.warning(
152
153
  "Valid blob_id required to download a blob. cwms-cli blob download --blob-id=myid. Run the list directive to see options for your office."
153
154
  )
154
155
  sys.exit(0)
155
- blob_id = blob_id.upper()
156
156
  logging.debug(f"Office: {kwargs.get('office')} Blob ID: {blob_id}")
157
157
  try:
158
158
  blob = cwms.get_blob(
@@ -174,14 +174,17 @@ def retrieve_blob(**kwargs):
174
174
 
175
175
 
176
176
  def delete_blob(**kwargs):
177
+ import cwms
178
+ import requests
179
+
177
180
  blob_id = kwargs.get("blob_id").upper()
178
181
  logging.debug(f"Office: {kwargs.get('office')} Blob ID: {blob_id}")
179
182
 
180
183
  try:
181
- # cwms.delete_blob(
182
- # office_id=kwargs.get("office"),
183
- # blob_id=kwargs.get("blob_id").upper(),
184
- # )
184
+ cwms.delete_blob(
185
+ office_id=kwargs.get("office"),
186
+ blob_id=kwargs.get("blob_id").upper(),
187
+ )
185
188
  logging.info(f"Successfully deleted blob with ID: {blob_id}")
186
189
  except requests.HTTPError as e:
187
190
  details = getattr(e.response, "text", "") or str(e)
@@ -199,8 +202,11 @@ def list_blobs(
199
202
  sort_by: Optional[Sequence[str]] = None,
200
203
  ascending: bool = True,
201
204
  limit: Optional[int] = None,
202
- ) -> pd.DataFrame:
205
+ ):
203
206
  logging.info(f"Listing blobs for office: {office!r}...")
207
+ import cwms
208
+ import pandas as pd
209
+
204
210
  result = cwms.get_blobs(office_id=office, blob_id_like=blob_id_like)
205
211
 
206
212
  # Accept either a DataFrame or a JSON/dict-like response
@@ -229,7 +235,7 @@ def list_blobs(
229
235
  if limit is not None:
230
236
  df = df.head(limit)
231
237
 
232
- logging.info(f"Found {len(df):,} blobs")
238
+ logging.info(f"Found {len(df):,} blob(s)")
233
239
  # List the blobs in the logger
234
240
  for _, row in df.iterrows():
235
241
  logging.info(f"Blob ID: {row['id']}, Description: {row.get('description')}")
@@ -241,77 +247,6 @@ def get_media_type(file_path: str) -> str:
241
247
  return mime_type or "application/octet-stream"
242
248
 
243
249
 
244
- def main(
245
- directive: str,
246
- input_file: str,
247
- blob_id: str,
248
- description: Optional[str],
249
- media_type: Optional[str],
250
- office: str,
251
- api_root: str,
252
- api_key: str,
253
- overwrite: Optional[bool] = True,
254
- dry_run: Optional[bool] = False,
255
- ):
256
- """
257
- Upload, Download, Delete, or Update blob data in CWMS.
258
-
259
- DIRECTIVE is the action to perform (upload, download, delete, update).
260
- INPUT_FILE is the path to the file on disk.
261
- BLOB_ID is the blob ID to store under.
262
- """
263
-
264
- cwms.api.init_session(api_root=api_root, api_key=api_key)
265
- file_data = None
266
- if directive in ["upload", "update"]:
267
- if not input_file or not os.path.isfile(input_file):
268
- logging.warning(
269
- "Valid input_file required for upload/update. Use --input-file to specify."
270
- )
271
- sys.exit(0)
272
- try:
273
- file_size = os.path.getsize(input_file)
274
- with open(input_file, "rb") as f:
275
- file_data = f.read()
276
- logging.info(f"Read file: {input_file} ({file_size} bytes)")
277
- except Exception as e:
278
- logging.error(f"Failed to read file: {e}")
279
- sys.exit(1)
280
-
281
- # Determine what should be done based on directive
282
- if directive == "upload":
283
- store_blob(
284
- office=office,
285
- api_root=api_root,
286
- input_file=input_file,
287
- blob_id=blob_id,
288
- description=description,
289
- media_type=media_type,
290
- file_data=file_data,
291
- overwrite=overwrite,
292
- dry_run=dry_run,
293
- )
294
- elif directive == "list":
295
- list_blobs(office=office, blob_id_like=blob_id, sort_by="blob_id")
296
- elif directive == "download":
297
- retrieve_blob(
298
- office=office,
299
- blob_id=blob_id,
300
- )
301
- elif directive == "delete":
302
- # TODO: Delete endpoint does not exist in cwms-python yet
303
- logging.warning(
304
- "[NOT IMPLEMENTED] Delete Blob is not supported yet!\n\thttps://github.com/HydrologicEngineeringCenter/cwms-python/issues/192"
305
- )
306
- pass
307
- elif directive == "update":
308
- # TODO: Patch endpoint does not exist in cwms-python yet
309
- logging.warning(
310
- "[NOT IMPLEMENTED] Update Blob is not supported yet! Consider overwriting instead if a rename is not needed.\n\thttps://github.com/HydrologicEngineeringCenter/cwms-python/issues/192"
311
- )
312
- pass
313
-
314
-
315
250
  def upload_cmd(
316
251
  input_file: str,
317
252
  blob_id: str,
@@ -323,7 +258,10 @@ def upload_cmd(
323
258
  api_root: str,
324
259
  api_key: str,
325
260
  ):
326
- cwms.api.init_session(api_root=api_root, api_key=get_api_key(api_key, ""))
261
+ import cwms
262
+ import requests
263
+
264
+ cwms.init_session(api_root=api_root, api_key=get_api_key(api_key, ""))
327
265
  try:
328
266
  file_size = os.path.getsize(input_file)
329
267
  with open(input_file, "rb") as f:
@@ -351,7 +289,7 @@ def upload_cmd(
351
289
  params = {"fail-if-exists": not overwrite}
352
290
 
353
291
  if dry_run:
354
- logging.info(f"--dry-run: would POST {api_root}blobs with params={params}")
292
+ logging.info(f"DRY RUN: would POST {api_root}blobs with params={params}")
355
293
  logging.info(
356
294
  json.dumps(
357
295
  {
@@ -365,7 +303,7 @@ def upload_cmd(
365
303
  return
366
304
 
367
305
  try:
368
- cwms.store_blobs(blob, fail_if_exists=overwrite)
306
+ cwms.store_blobs(blob, fail_if_exists=not overwrite)
369
307
  logging.info(f"Uploaded blob: {blob_id_up}")
370
308
  logging.info(f"View: {api_root}blobs/{blob_id_up}?office={office}")
371
309
  except requests.HTTPError as e:
@@ -377,8 +315,18 @@ def upload_cmd(
377
315
  sys.exit(1)
378
316
 
379
317
 
380
- def download_cmd(blob_id: str, dest: str, office: str, api_root: str, api_key: str):
381
- cwms.api.init_session(api_root=api_root, api_key=get_api_key(api_key, ""))
318
+ def download_cmd(
319
+ blob_id: str, dest: str, office: str, api_root: str, api_key: str, dry_run: bool
320
+ ):
321
+ import cwms
322
+ import requests
323
+
324
+ if dry_run:
325
+ logging.info(
326
+ f"DRY RUN: would GET {api_root} blob with blob-id={blob_id} office={office}."
327
+ )
328
+ return
329
+ cwms.init_session(api_root=api_root, api_key=get_api_key(api_key, ""))
382
330
  bid = blob_id.upper()
383
331
  logging.debug(f"Office={office} BlobID={bid}")
384
332
 
@@ -396,18 +344,65 @@ def download_cmd(blob_id: str, dest: str, office: str, api_root: str, api_key: s
396
344
  sys.exit(1)
397
345
 
398
346
 
399
- def delete_cmd(blob_id: str, office: str, api_root: str, api_key: str):
400
- logging.warning(
401
- "[NOT IMPLEMENTED] Delete Blob is not supported yet.\n"
402
- "See: https://github.com/HydrologicEngineeringCenter/cwms-python/issues/192"
403
- )
347
+ def delete_cmd(blob_id: str, office: str, api_root: str, api_key: str, dry_run: bool):
348
+ import cwms
349
+
350
+ if dry_run:
351
+ logging.info(
352
+ f"DRY RUN: would DELETE {api_root} blob with blob-id={blob_id} office={office}"
353
+ )
354
+ return
355
+ cwms.init_session(api_root=api_root, api_key=api_key)
356
+ cwms.delete_blob(office_id=office, blob_id=blob_id)
357
+ logging.info(f"Deleted blob: {blob_id} for office: {office}")
404
358
 
405
359
 
406
- def update_cmd(blob_id: str, input_file: str, office: str, api_root: str, api_key: str):
407
- logging.warning(
408
- "[NOT IMPLEMENTED] Update Blob is not supported yet. Consider --overwrite with upload.\n"
409
- "See: https://github.com/HydrologicEngineeringCenter/cwms-python/issues/192"
410
- )
360
+ def update_cmd(
361
+ input_file: str,
362
+ blob_id: str,
363
+ description: str,
364
+ media_type: str,
365
+ overwrite: bool,
366
+ dry_run: bool,
367
+ office: str,
368
+ api_root: str,
369
+ api_key: str,
370
+ ):
371
+ import cwms
372
+
373
+ if dry_run:
374
+ logging.info(
375
+ f"DRY RUN: would PATCH {api_root} blob with blob-id={blob_id} office={office}"
376
+ )
377
+ return
378
+ file_data = None
379
+ if input_file:
380
+ try:
381
+ file_size = os.path.getsize(input_file)
382
+ with open(input_file, "rb") as f:
383
+ file_data = f.read()
384
+ logging.info(f"Read file: {input_file} ({file_size} bytes)")
385
+ except Exception as e:
386
+ logging.error(f"Failed to read file: {e}")
387
+ sys.exit(1)
388
+ # Setup minimum required payload
389
+ blob = {"office-id": office, "id": blob_id.upper()}
390
+ if description:
391
+ blob["description"] = description
392
+ if media_type:
393
+ blob["media-type-id"] = media_type
394
+ else:
395
+ logging.info("Media type not specified; Retrieving existing media type...")
396
+ blob_metadata = cwms.get_blobs(office_id=office, blob_id_like=blob_id)
397
+ blob["media-type-id"] = blob_metadata.df.get(
398
+ "media-type-id", "application/octet-stream"
399
+ )[0]
400
+ logging.info(f"Using existing media type: {blob['media-type-id']}")
401
+
402
+ if file_data:
403
+ blob["value"] = base64.b64encode(file_data).decode("utf-8")
404
+ cwms.init_session(api_root=api_root, api_key=api_key)
405
+ cwms.update_blob(blob, fail_if_not_exists=not overwrite)
411
406
 
412
407
 
413
408
  def list_cmd(
@@ -421,7 +416,10 @@ def list_cmd(
421
416
  api_root: str,
422
417
  api_key: str,
423
418
  ):
424
- cwms.api.init_session(api_root=api_root, api_key=get_api_key(api_key, None))
419
+ import cwms
420
+ import pandas as pd
421
+
422
+ cwms.init_session(api_root=api_root, api_key=get_api_key(api_key, None))
425
423
  df = list_blobs(
426
424
  office=office,
427
425
  blob_id_like=blob_id_like,
@@ -1,3 +1,5 @@
1
+ import textwrap
2
+
1
3
  import click
2
4
 
3
5
  from cwmscli import requirements as reqs
@@ -23,6 +25,7 @@ from cwmscli.utils.deps import requires
23
25
  @requires(reqs.cwms)
24
26
  def shefcritimport(filename, office, api_root, api_key, api_key_loc):
25
27
  from cwmscli.commands.shef_critfile_import import import_shef_critfile
28
+ from cwmscli.utils import get_api_key
26
29
 
27
30
  api_key = get_api_key(api_key, api_key_loc)
28
31
  import_shef_critfile(
@@ -94,14 +97,16 @@ def csv2cwms_cmd(**kwargs):
94
97
  @click.group(
95
98
  "blob",
96
99
  help="Manage CWMS Blobs (upload, download, delete, update, list)",
97
- epilog="""
98
- * Store a PDF/image as a CWMS blob with optional description
99
- * Download a blob by id to your local filesystem
100
- * Update a blob's name/description
101
- * Bulk list blobs for an office
102
- """,
100
+ epilog=textwrap.dedent(
101
+ """
102
+ Example Usage:\n
103
+ - Store a PDF/image as a CWMS blob with optional description\n
104
+ - Download a blob by id to your local filesystem\n
105
+ - Update a blob's name/description/mime-type\n
106
+ - Bulk list blobs for an office
107
+ """
108
+ ),
103
109
  )
104
- @requires(reqs.cwms)
105
110
  def blob_group():
106
111
  pass
107
112
 
@@ -124,13 +129,14 @@ def blob_group():
124
129
  help="Override media type (guessed from file if omitted).",
125
130
  )
126
131
  @click.option(
127
- "--overwrite/--no-overwrite",
132
+ "--overwrite",
128
133
  default=False,
129
134
  show_default=True,
130
135
  help="If true, replace existing blob.",
131
136
  )
132
137
  @click.option("--dry-run", is_flag=True, help="Show request; do not send.")
133
138
  @common_api_options
139
+ @requires(reqs.cwms)
134
140
  def blob_upload(**kwargs):
135
141
  from cwmscli.commands.blob import upload_cmd
136
142
 
@@ -148,7 +154,9 @@ def blob_upload(**kwargs):
148
154
  default=None,
149
155
  help="Destination file path. Defaults to blob-id.",
150
156
  )
157
+ @click.option("--dry-run", is_flag=True, help="Show request; do not send.")
151
158
  @common_api_options
159
+ @requires(reqs.cwms)
152
160
  def blob_download(**kwargs):
153
161
  from cwmscli.commands.blob import download_cmd
154
162
 
@@ -158,9 +166,11 @@ def blob_download(**kwargs):
158
166
  # ================================================================================
159
167
  # Delete
160
168
  # ================================================================================
161
- @blob_group.command("delete", help="[Not implemented] Delete a blob by ID")
169
+ @blob_group.command("delete", help="Delete a blob by ID")
162
170
  @click.option("--blob-id", required=True, type=str, help="Blob ID to delete.")
171
+ @click.option("--dry-run", is_flag=True, help="Show request; do not send.")
163
172
  @common_api_options
173
+ @requires(reqs.cwms)
164
174
  def delete_cmd(**kwargs):
165
175
  from cwmscli.commands.blob import delete_cmd
166
176
 
@@ -170,15 +180,33 @@ def delete_cmd(**kwargs):
170
180
  # ================================================================================
171
181
  # Update
172
182
  # ================================================================================
173
- @blob_group.command("update", help="[Not implemented] Update/patch a blob by ID")
183
+ @blob_group.command("update", help="Update/patch a blob by ID")
174
184
  @click.option("--blob-id", required=True, type=str, help="Blob ID to update.")
185
+ @click.option("--dry-run", is_flag=True, help="Show request; do not send.")
186
+ @click.option(
187
+ "--description",
188
+ default=None,
189
+ help="New description JSON or text.",
190
+ )
191
+ @click.option(
192
+ "--media-type",
193
+ default=None,
194
+ help="New media type (guessed from file if omitted).",
195
+ )
175
196
  @click.option(
176
197
  "--input-file",
177
198
  required=False,
178
199
  type=click.Path(exists=True, dir_okay=False, readable=True, path_type=str),
179
200
  help="Optional file content to upload with update.",
180
201
  )
202
+ @click.option(
203
+ "--overwrite/--no-overwrite",
204
+ default=False,
205
+ show_default=True,
206
+ help="If true, replace existing blob.",
207
+ )
181
208
  @common_api_options
209
+ @requires(reqs.cwms)
182
210
  def update_cmd(**kwargs):
183
211
  from cwmscli.commands.blob import update_cmd
184
212
 
@@ -218,6 +246,7 @@ def update_cmd(**kwargs):
218
246
  help="If set, write results to this CSV file.",
219
247
  )
220
248
  @common_api_options
249
+ @requires(reqs.cwms)
221
250
  def list_cmd(**kwargs):
222
251
  from cwmscli.commands.blob import list_cmd
223
252
 
@@ -0,0 +1,7 @@
1
+ # Loader Scripts
2
+
3
+ This command supports loading data in some form or fashion.
4
+
5
+ Initially it was intended to load data across from one CDA instance (GET) to another (POST). But could be expanded to other forms of loading including backloading jobs that potentially wrap existing other commands.
6
+
7
+ It differs from the other scripts in that it is effectively a `CDA2CDA` script which is usually only needed for `loading` data in initial setups. (Dev/Docker Compose)
File without changes
@@ -0,0 +1,5 @@
1
+ # cwmscli/load/__main__.py
2
+ # Side-effect imports to register subcommands under `load_group`
3
+ from cwmscli.load.location import location as _locations
4
+ from cwmscli.load.root import load_group # export for callers
5
+ from cwmscli.load.timeseries import timeseries as _timeseries