datacontract-framework 1.0.0__py3-none-any.whl

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.
dashboard/app.py ADDED
@@ -0,0 +1,107 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+
5
+ import httpx
6
+ import pandas as pd
7
+ import streamlit as st
8
+
9
+ REGISTRY_URL = os.environ.get("REGISTRY_URL", "http://localhost:8000")
10
+
11
+ st.set_page_config(page_title="DCF Dashboard", page_icon="📋", layout="wide")
12
+
13
+
14
+ def _get(path: str):
15
+ try:
16
+ resp = httpx.get(f"{REGISTRY_URL}{path}", timeout=5)
17
+ resp.raise_for_status()
18
+ return resp.json()
19
+ except Exception as exc:
20
+ st.error(f"Registry unavailable: {exc}")
21
+ return []
22
+
23
+
24
+ page = st.sidebar.radio(
25
+ "Navigate",
26
+ ["Overview", "Contract Detail", "Breach History", "Discovery"],
27
+ index=0,
28
+ )
29
+
30
+ # ── Overview ──────────────────────────────────────────────────────────────────
31
+ if page == "Overview":
32
+ st.title("DCF — DataContract Framework")
33
+ st.subheader("Overview")
34
+
35
+ contracts = _get("/contracts")
36
+ results = _get("/validation-results?limit=100")
37
+
38
+ compliant = sum(1 for r in results if r["overall_status"] == "COMPLIANT")
39
+ breach = sum(1 for r in results if r["overall_status"] == "BREACH")
40
+ error = sum(1 for r in results if r["overall_status"] == "ERROR")
41
+
42
+ c1, c2, c3, c4 = st.columns(4)
43
+ c1.metric("Contracts", len(contracts))
44
+ c2.metric("Compliant", compliant, delta=None)
45
+ c3.metric("Breaching", breach, delta=f"+{breach}" if breach else None)
46
+ c4.metric("Errors", error)
47
+
48
+ st.subheader("Recent Breach Events")
49
+ breach_events = [r for r in results if r["overall_status"] == "BREACH"][:10]
50
+ if breach_events:
51
+ df = pd.DataFrame(breach_events)[
52
+ ["contract_name", "contract_version", "validated_at", "overall_status"]
53
+ ]
54
+ st.dataframe(df, use_container_width=True)
55
+ else:
56
+ st.success("No recent breach events.")
57
+
58
+ # ── Contract Detail ────────────────────────────────────────────────────────────
59
+ elif page == "Contract Detail":
60
+ st.title("Contract Detail")
61
+ contracts = _get("/contracts")
62
+ names = [c["name"] for c in contracts] if contracts else []
63
+
64
+ if not names:
65
+ st.info("No contracts registered yet.")
66
+ else:
67
+ selected = st.selectbox("Select contract", names)
68
+ detail = _get(f"/contracts/{selected}")
69
+ if detail:
70
+ st.json(detail)
71
+
72
+ results = _get(f"/validation-results?contract_name={selected}&limit=30")
73
+ if results:
74
+ st.subheader("Validation History (last 30)")
75
+ df = pd.DataFrame(results)[
76
+ ["validated_at", "overall_status", "row_count", "contract_version"]
77
+ ]
78
+ st.dataframe(df, use_container_width=True)
79
+
80
+ # ── Breach History ─────────────────────────────────────────────────────────────
81
+ elif page == "Breach History":
82
+ st.title("Breach History")
83
+ results = _get("/validation-results?limit=200")
84
+ if results:
85
+ df = pd.DataFrame(results)
86
+ status_filter = st.multiselect(
87
+ "Filter by status",
88
+ options=["COMPLIANT", "BREACH", "ERROR"],
89
+ default=["BREACH", "ERROR"],
90
+ )
91
+ filtered = df[df["overall_status"].isin(status_filter)] if status_filter else df
92
+ st.dataframe(
93
+ filtered[["validated_at", "contract_name", "contract_version", "overall_status", "row_count"]],
94
+ use_container_width=True,
95
+ )
96
+ else:
97
+ st.info("No validation results yet.")
98
+
99
+ # ── Discovery ──────────────────────────────────────────────────────────────────
100
+ elif page == "Discovery":
101
+ st.title("Dataset Discovery")
102
+ search = st.text_input("Search contracts by name")
103
+ contracts = _get("/contracts")
104
+ for c in contracts:
105
+ if search.lower() in c["name"].lower():
106
+ with st.expander(f"{c['name']} (v{c['version']})"):
107
+ st.json(c)