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.
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/CHANGELOG.md +7 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/PKG-INFO +2 -2
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/src/functions.py +14 -21
- datatrail_cli-0.11.1/dtcli/unregistered.py +102 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/poetry.lock +1153 -653
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/pyproject.toml +2 -2
- datatrail_cli-0.11.0/dtcli/unregistered.py +0 -61
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/.github/workflows/continuous-deployment.yml +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/.github/workflows/continuous-integration.yml +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/.github/workflows/docs.yml +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/.github/workflows/stale.yml +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/.gitignore +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/.pre-commit-config.yaml +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/LICENSE +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/README.md +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/clear.md +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/cli.md +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/commands.md +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/images/Datatrail-logo.png +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/index.md +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/initialising.md +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/install.md +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/list.md +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/ps.md +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/pull.md +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/scout.md +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/docs/user_guide.md +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/__init__.py +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/clear.py +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/cli.py +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/config.py +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/config.yaml +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/ls.py +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/ps.py +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/pull.py +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/scout.py +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/utilities/cadcclient.py +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/dtcli/utilities/utilities.py +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/mkdocs.yml +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/tests/test_cli.py +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/tests/test_functions.py +0 -0
- {datatrail_cli-0.11.0 → datatrail_cli-0.11.1}/tests/test_utils.py +0 -0
- {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.
|
|
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>=
|
|
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
|
|
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
|
-
|
|
524
|
-
|
|
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
|
-
|
|
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)
|