Skip to content

Commit d34bd3e

Browse files
author
DhanashreePetare
committed
feat: add manifest summary command
1 parent 62096fc commit d34bd3e

4 files changed

Lines changed: 208 additions & 1 deletion

File tree

‎README.md‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Command-line and Python client for downloading and deploying datasets on DBpedia
2020
- [Delete](#cli-delete)
2121
- [Manifest](#cli-manifest)
2222
- [Replay](#cli-manifest-replay)
23+
- [Summary](#cli-manifest-summary)
2324
- [Module Usage](#module-usage)
2425
- [Deploy](#module-deploy)
2526
- [Development & Contributing](#development--contributing)
@@ -682,6 +683,32 @@ databusclient manifest replay ./manifests/deploy-run.jsonld --apikey YOUR_API_KE
682683
```
683684
Replaying redeploys the same version — if it already exists on Databus, it is updated. WebDAV/Nextcloud deploys cannot be replayed, since the originally uploaded local files may no longer exist at their original paths by the time replay runs.
684685
686+
<a id="cli-manifest-summary"></a>
687+
#### Summary
688+
689+
Print a readable summary of any recorded manifest without replaying it:
690+
691+
```bash
692+
# Python
693+
databusclient manifest summary ./manifests/download-run.jsonld
694+
# Docker
695+
docker run --rm -v $(pwd):/data dbpedia/databus-python-client manifest summary ./manifests/download-run.jsonld
696+
```
697+
698+
Example output:
699+
700+
```
701+
Command : download
702+
Executed : 2024-03-24T10:02:49.500418+00:00
703+
Endpoint : https://databus.dbpedia.org/sparql
704+
Auth : vault_token
705+
Files : 1 succeeded · 0 failed
706+
Total : 100.0 MB
707+
Status : completed
708+
```
709+
710+
Only existing data already stored in the manifest is read — no new files are downloaded or written, and no network access happens.
711+
685712
## Module Usage
686713
687714
<a id="module-deploy"></a>

‎databusclient/cli.py‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
from databusclient.api.download import download as api_download, DownloadAuthError
1111
from databusclient.manifest.context import ManifestContext
1212
from databusclient.manifest.writer import ManifestWriter
13-
from databusclient.manifest.replay import ManifestReplayError, replay_manifest
13+
from databusclient.manifest.replay import ManifestReplayError, replay_manifest, _load_manifest
14+
from databusclient.manifest.summary import format_summary
1415
from databusclient.extensions import webdav
1516

1617

@@ -552,5 +553,20 @@ def manifest_replay(manifest_path, localdir, databus, vault_token, databus_key,
552553
except Exception as e:
553554
raise click.ClickException(str(e))
554555

556+
@manifest.command("summary")
557+
@click.argument("manifest_path", type=click.Path(exists=True, dir_okay=False))
558+
def manifest_summary(manifest_path):
559+
"""
560+
Print a readable summary of a recorded manifest operation.
561+
562+
Reads the existing summary data already stored in the manifest --
563+
no new data is collected and no new file is written.
564+
"""
565+
try:
566+
manifest = _load_manifest(manifest_path)
567+
click.echo(format_summary(manifest))
568+
except ManifestReplayError as e:
569+
raise click.ClickException(str(e))
570+
555571
if __name__ == "__main__":
556572
app()

‎databusclient/manifest/summary.py‎

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"""manifest summary — formats an existing manifest as readable console output.
2+
3+
Reads already-recorded fields from a manifest JSON-LD file and formats
4+
them for display. No new data is collected, no new file is written, and
5+
no network access happens here at all.
6+
"""
7+
8+
from __future__ import annotations
9+
10+
from typing import Any, Dict
11+
12+
13+
def _format_bytes(total_bytes: int) -> str:
14+
"""Format a byte count as a human-readable MB value."""
15+
mb = total_bytes / (1024 * 1024)
16+
return f"{mb:.1f} MB"
17+
18+
19+
def _derive_status(manifest: Dict[str, Any]) -> str:
20+
"""Derive an overall status string for the operation.
21+
22+
- "failed" if a top-level dbus:operationError was recorded.
23+
- "completed with errors" if some individual files failed but the
24+
operation itself did not raise.
25+
- "completed" otherwise.
26+
"""
27+
if manifest.get("dbus:operationError"):
28+
return "failed"
29+
30+
result = manifest.get("dbus:executionResult", {})
31+
if result.get("dbus:failed", 0) > 0:
32+
return "completed with errors"
33+
34+
return "completed"
35+
36+
37+
def format_summary(manifest: Dict[str, Any]) -> str:
38+
"""Format a loaded manifest dict as a readable multi-line summary string.
39+
40+
Args:
41+
manifest: A manifest dict, as produced by loading a manifest
42+
JSON-LD file (e.g. via replay._load_manifest).
43+
44+
Returns:
45+
A formatted multi-line string ready to print to the console.
46+
"""
47+
lines = []
48+
49+
command = manifest.get("dbus:command", "unknown")
50+
lines.append(f"Command : {command}")
51+
52+
issued = manifest.get("dcterms:issued", {}).get("@value")
53+
if issued:
54+
lines.append(f"Executed : {issued}")
55+
56+
endpoint = manifest.get("dbus:endpoint")
57+
if endpoint:
58+
lines.append(f"Endpoint : {endpoint}")
59+
60+
auth_method = manifest.get("dbus:authMethod")
61+
if auth_method:
62+
lines.append(f"Auth : {auth_method}")
63+
64+
result = manifest.get("dbus:executionResult", {})
65+
succeeded = result.get("dbus:succeeded", 0)
66+
failed = result.get("dbus:failed", 0)
67+
lines.append(f"Files : {succeeded} succeeded \u00b7 {failed} failed")
68+
69+
total_bytes = result.get("dbus:totalBytes", 0)
70+
if command == "download" and total_bytes:
71+
lines.append(f"Total : {_format_bytes(total_bytes)}")
72+
73+
lines.append(f"Status : {_derive_status(manifest)}")
74+
75+
return "\n".join(lines)

‎tests/test_manifest_summary.py‎

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
from databusclient.manifest.summary import format_summary
2+
3+
4+
def test_summary_full_download_manifest():
5+
manifest = {
6+
"dbus:command": "download",
7+
"dcterms:issued": {"@value": "2024-03-24T10:00:00Z"},
8+
"dbus:endpoint": "https://databus.dbpedia.org",
9+
"dbus:authMethod": "vault_token",
10+
"dbus:executionResult": {
11+
"dbus:succeeded": 1,
12+
"dbus:failed": 0,
13+
"dbus:totalBytes": 104857600,
14+
},
15+
}
16+
output = format_summary(manifest)
17+
assert "Command : download" in output
18+
assert "Executed : 2024-03-24T10:00:00Z" in output
19+
assert "Endpoint : https://databus.dbpedia.org" in output
20+
assert "Auth : vault_token" in output
21+
assert "Files : 1 succeeded \u00b7 0 failed" in output
22+
assert "Total : 100.0 MB" in output
23+
assert "Status : completed" in output
24+
25+
26+
def test_summary_omits_missing_optional_fields():
27+
"""Delete/deploy manifests without endpoint/authMethod omit those lines."""
28+
manifest = {
29+
"dbus:command": "delete",
30+
"dcterms:issued": {"@value": "2024-03-24T10:00:00Z"},
31+
"dbus:executionResult": {
32+
"dbus:succeeded": 1,
33+
"dbus:failed": 0,
34+
"dbus:totalBytes": 0,
35+
},
36+
}
37+
output = format_summary(manifest)
38+
assert "Endpoint" not in output
39+
assert "Auth" not in output
40+
assert "Total" not in output
41+
assert "Command : delete" in output
42+
assert "Status : completed" in output
43+
44+
45+
def test_summary_shows_failed_files():
46+
manifest = {
47+
"dbus:command": "download",
48+
"dcterms:issued": {"@value": "2024-03-24T10:00:00Z"},
49+
"dbus:executionResult": {
50+
"dbus:succeeded": 2,
51+
"dbus:failed": 1,
52+
"dbus:totalBytes": 2048,
53+
},
54+
}
55+
output = format_summary(manifest)
56+
assert "Files : 2 succeeded \u00b7 1 failed" in output
57+
assert "Status : completed with errors" in output
58+
59+
60+
def test_summary_shows_operation_error_as_failed_status():
61+
manifest = {
62+
"dbus:command": "deploy",
63+
"dcterms:issued": {"@value": "2024-03-24T10:00:00Z"},
64+
"dbus:executionResult": {
65+
"dbus:succeeded": 0,
66+
"dbus:failed": 0,
67+
"dbus:totalBytes": 0,
68+
},
69+
"dbus:operationError": {
70+
"dbus:errorType": "DeployError",
71+
"dbus:errorMessage": "bad API key",
72+
},
73+
}
74+
output = format_summary(manifest)
75+
assert "Status : failed" in output
76+
77+
78+
def test_summary_total_omitted_for_non_download_command():
79+
manifest = {
80+
"dbus:command": "deploy",
81+
"dcterms:issued": {"@value": "2024-03-24T10:00:00Z"},
82+
"dbus:executionResult": {
83+
"dbus:succeeded": 1,
84+
"dbus:failed": 0,
85+
"dbus:totalBytes": 12345,
86+
},
87+
}
88+
output = format_summary(manifest)
89+
assert "Total" not in output

0 commit comments

Comments
 (0)