Skip to content

Commit 928cf2b

Browse files
authored
Create db.py
1 parent 0ef3be9 commit 928cf2b

1 file changed

Lines changed: 227 additions & 0 deletions

File tree

neuro_registry/db.py

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
"""
2+
db.py — Shared DB setup for the SenseIn Schema Registry
3+
--------------------------------------------------------
4+
Single source of truth for:
5+
- LadybugDB connection
6+
- DDL (all node + relationship tables)
7+
- Base identity helpers (make_base, make_uid, make_uri, now_iso)
8+
9+
Import this in seed.py, ingest_linkml.py, align.py, export_json.py
10+
so every script gets the same tables without duplicating DDL.
11+
"""
12+
13+
from __future__ import annotations
14+
import datetime, uuid
15+
from pathlib import Path
16+
17+
import ladybug as lb
18+
19+
# ---------------------------------------------------------------------------
20+
# Registry namespace
21+
# ---------------------------------------------------------------------------
22+
23+
REG = "https://registry.sensein.io/"
24+
25+
# ---------------------------------------------------------------------------
26+
# Identity helpers
27+
# ---------------------------------------------------------------------------
28+
29+
def now_iso() -> str:
30+
return datetime.datetime.now(datetime.UTC).isoformat()
31+
32+
def make_uid() -> str:
33+
return str(uuid.uuid4())
34+
35+
def make_uri(object_id: str, version: str = "1.0.0") -> str:
36+
return f"{REG}obj/{object_id}/v/{version}"
37+
38+
def make_iri(object_id: str) -> str:
39+
return f"{REG}obj/{object_id}"
40+
41+
def make_base(object_id: str, version: str = "1.0.0",
42+
iri: str | None = None) -> dict:
43+
"""Return the shared BaseNode identity fields as a dict."""
44+
return {
45+
"uid": make_uid(),
46+
"iri": iri or make_iri(object_id),
47+
"uri": make_uri(object_id, version),
48+
"version": version,
49+
"created_at": now_iso(),
50+
}
51+
52+
def bump_version(ver: str, bump: str = "patch") -> str:
53+
"""
54+
Bump a semver string.
55+
bump="patch" 1.0.0 → 1.0.1
56+
bump="minor" 1.0.0 → 1.1.0
57+
bump="major" 1.0.0 → 2.0.0
58+
"""
59+
major, minor, patch = (int(x) for x in ver.split("."))
60+
if bump == "major":
61+
return f"{major+1}.0.0"
62+
elif bump == "minor":
63+
return f"{major}.{minor+1}.0"
64+
else:
65+
return f"{major}.{minor}.{patch+1}"
66+
67+
# ---------------------------------------------------------------------------
68+
# DDL
69+
# ---------------------------------------------------------------------------
70+
71+
DDL = [
72+
# ---- Node tables -------------------------------------------------------
73+
# All share base identity: uid PK, iri, uri, version, created_at
74+
# registry_version ties every node to the registry snapshot it was created in
75+
76+
"""CREATE NODE TABLE IF NOT EXISTS SchemaClass (
77+
uid STRING PRIMARY KEY,
78+
iri STRING,
79+
uri STRING,
80+
version STRING,
81+
created_at STRING,
82+
name STRING,
83+
definition STRING,
84+
abstract BOOLEAN,
85+
source_label STRING,
86+
registry_version STRING
87+
)""",
88+
89+
"""CREATE NODE TABLE IF NOT EXISTS SchemaProperty (
90+
uid STRING PRIMARY KEY,
91+
iri STRING,
92+
uri STRING,
93+
version STRING,
94+
created_at STRING,
95+
name STRING,
96+
definition STRING,
97+
datatype STRING,
98+
range_uri STRING,
99+
multivalued BOOLEAN,
100+
required BOOLEAN,
101+
source_label STRING,
102+
registry_version STRING
103+
)""",
104+
105+
"""CREATE NODE TABLE IF NOT EXISTS SchemaRule (
106+
uid STRING PRIMARY KEY,
107+
iri STRING,
108+
uri STRING,
109+
version STRING,
110+
created_at STRING,
111+
name STRING,
112+
rule_spec STRING,
113+
units STRING,
114+
min_val STRING,
115+
max_val STRING,
116+
pattern STRING,
117+
multivalued BOOLEAN,
118+
required BOOLEAN,
119+
registry_version STRING
120+
)""",
121+
122+
"""CREATE NODE TABLE IF NOT EXISTS SchemaTransform (
123+
uid STRING PRIMARY KEY,
124+
iri STRING,
125+
uri STRING,
126+
version STRING,
127+
created_at STRING,
128+
name STRING,
129+
spec STRING,
130+
registry_version STRING
131+
)""",
132+
133+
"""CREATE NODE TABLE IF NOT EXISTS SchemaSource (
134+
uid STRING PRIMARY KEY,
135+
iri STRING,
136+
uri STRING,
137+
version STRING,
138+
created_at STRING,
139+
label STRING,
140+
mime_type STRING,
141+
registry_version STRING
142+
)""",
143+
144+
"""CREATE NODE TABLE IF NOT EXISTS SchemaActivity (
145+
uid STRING PRIMARY KEY,
146+
iri STRING,
147+
uri STRING,
148+
version STRING,
149+
created_at STRING,
150+
activity STRING,
151+
agent STRING,
152+
started_at STRING,
153+
issue_number STRING,
154+
registry_version STRING
155+
)""",
156+
157+
# ---- Relationship tables -----------------------------------------------
158+
"CREATE REL TABLE IF NOT EXISTS PRIOR_VERSION (FROM SchemaClass TO SchemaClass)",
159+
"CREATE REL TABLE IF NOT EXISTS PRIOR_VERSION_P (FROM SchemaProperty TO SchemaProperty)",
160+
"CREATE REL TABLE IF NOT EXISTS PRIOR_VERSION_R (FROM SchemaRule TO SchemaRule)",
161+
"CREATE REL TABLE IF NOT EXISTS HAS_PROPERTY (FROM SchemaClass TO SchemaProperty)",
162+
"CREATE REL TABLE IF NOT EXISTS APPLIES_TO (FROM SchemaRule TO SchemaClass)",
163+
"CREATE REL TABLE IF NOT EXISTS SUBCLASS_OF (FROM SchemaClass TO SchemaClass)",
164+
"CREATE REL TABLE IF NOT EXISTS MIXIN (FROM SchemaClass TO SchemaClass)",
165+
"CREATE REL TABLE IF NOT EXISTS SKOS_BROADER (FROM SchemaClass TO SchemaClass)",
166+
"CREATE REL TABLE IF NOT EXISTS SKOS_RELATED (FROM SchemaClass TO SchemaClass)",
167+
"CREATE REL TABLE IF NOT EXISTS PROV_GENERATED (FROM SchemaClass TO SchemaActivity)",
168+
"CREATE REL TABLE IF NOT EXISTS PROV_GENERATED_P (FROM SchemaProperty TO SchemaActivity)",
169+
"CREATE REL TABLE IF NOT EXISTS PROV_GENERATED_R (FROM SchemaRule TO SchemaActivity)",
170+
"CREATE REL TABLE IF NOT EXISTS FROM_SOURCE (FROM SchemaClass TO SchemaSource)",
171+
"CREATE REL TABLE IF NOT EXISTS FROM_SOURCE_P (FROM SchemaProperty TO SchemaSource)",
172+
173+
# Alignment — distance + per-signal subscores for weight slider in UI
174+
"""CREATE REL TABLE IF NOT EXISTS ALIGNED_TO (
175+
FROM SchemaClass TO SchemaClass,
176+
distance DOUBLE,
177+
method STRING,
178+
score_iri DOUBLE,
179+
score_name DOUBLE,
180+
score_desc DOUBLE,
181+
score_slot DOUBLE,
182+
registry_version STRING
183+
)""",
184+
]
185+
186+
# ---------------------------------------------------------------------------
187+
# Connection factory
188+
# ---------------------------------------------------------------------------
189+
190+
def get_connection(db_path: str = "./registry.lbug") -> lb.Connection:
191+
"""Open (or create) a LadybugDB database and ensure all tables exist."""
192+
Path(db_path).parent.mkdir(parents=True, exist_ok=True)
193+
db = lb.Database(db_path)
194+
conn = lb.Connection(db)
195+
for stmt in DDL:
196+
conn.execute(stmt)
197+
return conn
198+
199+
# ---------------------------------------------------------------------------
200+
# Registry version helpers
201+
# ---------------------------------------------------------------------------
202+
203+
PROVENANCE_PATH = "data/provenance.json"
204+
205+
def current_registry_version(provenance_path: str = PROVENANCE_PATH) -> str:
206+
"""Read current registry version from provenance.json. Default 0.0.0."""
207+
import json
208+
p = Path(provenance_path)
209+
if not p.exists():
210+
return "0.0.0"
211+
entries = json.loads(p.read_text())
212+
if not entries:
213+
return "0.0.0"
214+
return entries[-1]["registry_version"]
215+
216+
def next_registry_version(current: str, bump: str = "minor") -> str:
217+
return bump_version(current, bump)
218+
219+
def append_provenance(entry: dict,
220+
provenance_path: str = PROVENANCE_PATH) -> None:
221+
"""Append a provenance entry to data/provenance.json."""
222+
import json
223+
p = Path(provenance_path)
224+
p.parent.mkdir(parents=True, exist_ok=True)
225+
entries = json.loads(p.read_text()) if p.exists() else []
226+
entries.append(entry)
227+
p.write_text(json.dumps(entries, indent=2))

0 commit comments

Comments
 (0)