datatrail-cli 0.11.0__tar.gz → 0.11.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 (46) hide show
  1. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/CHANGELOG.md +7 -0
  2. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/PKG-INFO +2 -2
  3. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/src/functions.py +14 -21
  4. datatrail_cli-0.11.1/dtcli/unregistered.py +102 -0
  5. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/poetry.lock +1153 -653
  6. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/pyproject.toml +2 -2
  7. datatrail_cli-0.11.0/dtcli/unregistered.py +0 -61
  8. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  9. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  10. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  11. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/.github/workflows/continuous-deployment.yml +0 -0
  12. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/.github/workflows/continuous-integration.yml +0 -0
  13. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/.github/workflows/docs.yml +0 -0
  14. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/.github/workflows/stale.yml +0 -0
  15. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/.gitignore +0 -0
  16. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/.pre-commit-config.yaml +0 -0
  17. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/LICENSE +0 -0
  18. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/README.md +0 -0
  19. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/clear.md +0 -0
  20. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/cli.md +0 -0
  21. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/commands.md +0 -0
  22. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/images/Datatrail-logo.png +0 -0
  23. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/index.md +0 -0
  24. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/initialising.md +0 -0
  25. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/install.md +0 -0
  26. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/list.md +0 -0
  27. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/ps.md +0 -0
  28. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/pull.md +0 -0
  29. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/scout.md +0 -0
  30. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/user_guide.md +0 -0
  31. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/__init__.py +0 -0
  32. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/clear.py +0 -0
  33. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/cli.py +0 -0
  34. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/config.py +0 -0
  35. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/config.yaml +0 -0
  36. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/ls.py +0 -0
  37. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/ps.py +0 -0
  38. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/pull.py +0 -0
  39. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/scout.py +0 -0
  40. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/utilities/cadcclient.py +0 -0
  41. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/utilities/utilities.py +0 -0
  42. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/mkdocs.yml +0 -0
  43. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/tests/test_cli.py +0 -0
  44. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/tests/test_functions.py +0 -0
  45. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/tests/test_utils.py +0 -0
  46. {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/tox.ini +0 -0
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.11.1](https://github.com/CHIMEFRB/datatrail-cli/compare/v0.11.0...v0.11.1) (2026-07-21)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **unregistered:** improve way info displayed ([c5f0fca](https://github.com/CHIMEFRB/datatrail-cli/commit/c5f0fca6f6f53e5004e1f1868517100572d68d1a))
9
+
3
10
  ## [0.11.0](https://github.com/CHIMEFRB/datatrail-cli/compare/v0.10.3...v0.11.0) (2026-07-07)
4
11
 
5
12
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datatrail-cli
3
- Version: 0.11.0
3
+ Version: 0.11.1
4
4
  Summary: CHIME/FRB Datatrail CLI
5
5
  Author: CHIME FRB Project Office
6
6
  Author-email: Tarik Zegmott <tarik.zegmott@mcgill.ca>
@@ -13,7 +13,7 @@ Requires-Dist: cadcutils>=1.5.1.1
13
13
  Requires-Dist: click-aliasing>=1.0.0
14
14
  Requires-Dist: click>=8.1.3
15
15
  Requires-Dist: dill>=0.3.6
16
- Requires-Dist: lxml>=4.9.2
16
+ Requires-Dist: lxml>=6.1.0
17
17
  Requires-Dist: mergedeep>=1.3.4
18
18
  Requires-Dist: pyaml>=21.10.1
19
19
  Requires-Dist: requests>=2.29.0
@@ -6,7 +6,7 @@ import re
6
6
  import shutil
7
7
  import subprocess
8
8
  import time
9
- from collections import defaultdict
9
+ from collections import Counter
10
10
  from pathlib import Path
11
11
  from typing import Any, Dict, List, Optional, Tuple
12
12
 
@@ -486,6 +486,15 @@ def get_unregistered_dataset(dataset: str, scope: str) -> Optional[Dict[str, Any
486
486
  return response[0]
487
487
 
488
488
 
489
+ ATTACH_RE = re.compile(
490
+ r"Could not attach datasets: .+? ERROR: \"?dataset (.+?), (.+?) not found" # noqa: E501
491
+ )
492
+
493
+ CREATE_RE = re.compile(
494
+ r"Could not create dataset: (.+?), scope: (.+?)\. .*UniqueViolation" # noqa: E501
495
+ )
496
+
497
+
489
498
  def signature(msg: str) -> str:
490
499
  """Create a signature for a reason unregistered message.
491
500
 
@@ -495,16 +504,6 @@ def signature(msg: str) -> str:
495
504
  Returns:
496
505
  str: Signature for error message.
497
506
  """
498
- ATTACH_RE = re.compile(
499
- r"Could not attach datasets: .+ ERROR: dataset (.+), (.+) not found" # noqa: E501
500
- )
501
-
502
- CREATE_RE = re.compile(
503
- r"Could not create dataset: (.+), scope: (.+)\. .*UniqueViolation" # noqa: E501
504
- )
505
-
506
- POSTGRES_RE = re.compile(r".*psycopg.*")
507
-
508
507
  msg = msg.strip()
509
508
 
510
509
  # Attach-dataset errors
@@ -517,11 +516,12 @@ def signature(msg: str) -> str:
517
516
  m = CREATE_RE.search(msg)
518
517
  if m:
519
518
  dataset, scope = m.groups()
519
+ dataset = re.sub(r"\d+", "<ID>", dataset)
520
520
  return f"CREATE_DUPLICATE:{dataset}:{scope}"
521
521
 
522
522
  # PostgreSQL violation
523
- m = POSTGRES_RE.search(msg)
524
- if m:
523
+ if "psycopg" in msg:
524
+ msg = re.sub(r"\s+", " ", msg)
525
525
  return f"POSTGRES:{msg[:120]}"
526
526
 
527
527
  # Short status / token messages
@@ -552,11 +552,4 @@ def summarise_unregistered_datasets() -> Dict[str, int]:
552
552
  Dict[str, int]: Dictionary of error message signatures and their counts.
553
553
  """
554
554
  response = get_all_unregistered_datasets()
555
- reason_groups: Dict[str, int] = defaultdict(int)
556
- messages = [str(r["results"]["reason"]) for r in response]
557
-
558
- for msg in messages:
559
- sig = signature(msg)
560
- reason_groups[sig] += 1
561
-
562
- return reason_groups
555
+ return Counter(signature(str(r["results"]["reason"])) for r in response)
@@ -0,0 +1,102 @@
1
+ """Datatrail Unregistered datasets commands."""
2
+
3
+ import logging
4
+ from collections import defaultdict
5
+ from typing import DefaultDict, List, Tuple
6
+
7
+ import click
8
+ from rich.console import Console
9
+ from rich.table import Table
10
+ from rich.text import Text
11
+
12
+ from dtcli.src import functions
13
+ from dtcli.utilities.utilities import set_log_level
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ console = Console()
18
+ error_console = Console(stderr=True, style="bold red")
19
+
20
+ CATEGORY_STYLES = {
21
+ "ATTACH_MISSING": "yellow",
22
+ "CREATE_DUPLICATE": "magenta",
23
+ "POSTGRES": "red",
24
+ "OTHER": "red",
25
+ "STATUS": "cyan",
26
+ }
27
+
28
+
29
+ @click.group(help="Commands related to unregistered datasets.")
30
+ def unregistered():
31
+ """Group of commands related to unregistered datasets."""
32
+ pass
33
+
34
+
35
+ @unregistered.command(help="Summarise the reasons for unregistered datasets.")
36
+ @click.option("-v", "--verbose", count=True, help="Verbosity: v=INFO, vv=DEBUG.")
37
+ @click.option("-q", "--quiet", is_flag=True, help="Only errors shown in logs.")
38
+ @click.pass_context
39
+ def summary(
40
+ ctx: click.Context,
41
+ verbose: int = 0,
42
+ quiet: bool = False,
43
+ ):
44
+ """Show a summary of the unregistered datasets.
45
+
46
+ Args:
47
+ ctx (click.Context): Click context.
48
+ verbose (int): Verbosity: v=INFO, vv=DEBUG.
49
+ quiet (bool): Only errors shown in logs.
50
+ """
51
+ # Set logging level.
52
+ set_log_level(logger, verbose, quiet)
53
+ logger.debug("`summary` called with:")
54
+ logger.debug(f"verbose: {verbose} [{type(verbose)}]")
55
+ logger.debug(f"quiet: {quiet} [{type(quiet)}]")
56
+
57
+ results = functions.summarise_unregistered_datasets()
58
+
59
+ if not results:
60
+ console.print("No unregistered datasets found.")
61
+ return
62
+
63
+ total = sum(results.values())
64
+
65
+ table = Table(
66
+ title=f"Summary of reasons — {total:,} unregistered datasets",
67
+ header_style="magenta",
68
+ title_style="bold magenta",
69
+ row_styles=["none", "dim"],
70
+ )
71
+ table.add_column("Category")
72
+ table.add_column("Detail")
73
+ table.add_column("Count", justify="right")
74
+ table.add_column("%", justify="right")
75
+
76
+ # Group signatures by their category prefix, e.g. "ATTACH_MISSING:...".
77
+ groups: DefaultDict[str, List[Tuple[str, int]]] = defaultdict(list)
78
+ for sig, count in results.items():
79
+ category, _, detail = sig.partition(":")
80
+ groups[category].append((detail, count))
81
+
82
+ ordered = sorted(
83
+ groups.items(),
84
+ key=lambda group: sum(count for _, count in group[1]),
85
+ reverse=True,
86
+ )
87
+ for index, (category, reasons) in enumerate(ordered):
88
+ if index:
89
+ table.add_section()
90
+ style = CATEGORY_STYLES.get(category, "white")
91
+ reasons.sort(key=lambda reason: reason[1], reverse=True)
92
+ for row, (detail, count) in enumerate(reasons):
93
+ if category in ("ATTACH_MISSING", "CREATE_DUPLICATE"):
94
+ detail = detail.replace(":", " → ", 1)
95
+ table.add_row(
96
+ Text(category, style=style) if row == 0 else "",
97
+ Text(detail) if detail else "(no reason recorded)",
98
+ f"{count:,}",
99
+ f"{count / total:.1%}",
100
+ )
101
+
102
+ console.print(table)