Spaces:
Sleeping
Sleeping
DocUA commited on
Commit ·
5a3f318
1
Parent(s): 04fe8ef
fix(graph): visible edges + measurement values in node labels
Browse files- livemedcard/api.py +18 -10
- livemedcard/static/index.html +9 -6
livemedcard/api.py
CHANGED
|
@@ -383,16 +383,24 @@ def graph(state: _State = Depends(_get_state)):
|
|
| 383 |
лише мітку (сам текст — за `/document/{id}`), щоб відповідь лишалась легкою.
|
| 384 |
"""
|
| 385 |
with state.lock:
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
"
|
| 393 |
-
|
| 394 |
-
for
|
| 395 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 396 |
edges = [
|
| 397 |
{"source": u, "target": v, "rel": d.get("rel", "")}
|
| 398 |
for u, v, d in g.edges(data=True)
|
|
|
|
| 383 |
лише мітку (сам текст — за `/document/{id}`), щоб відповідь лишалась легкою.
|
| 384 |
"""
|
| 385 |
with state.lock:
|
| 386 |
+
card = state.card
|
| 387 |
+
g = card.graph.g
|
| 388 |
+
# Вузол-вимірювання без значення — це просто повторений підпис («Креатинін»
|
| 389 |
+
# чотири рази поспіль). Значення робить граф читабельним: видно і ЩО, і СКІЛЬКИ.
|
| 390 |
+
values = {
|
| 391 |
+
f"Observation/{o.id}": (
|
| 392 |
+
f"{o.valueQuantity.value:g} {o.valueQuantity.unit or ''}".strip()
|
| 393 |
+
)
|
| 394 |
+
for o in card.observations
|
| 395 |
+
if o.valueQuantity is not None
|
| 396 |
+
}
|
| 397 |
+
nodes = []
|
| 398 |
+
for n, d in g.nodes(data=True):
|
| 399 |
+
# у вузла-документа мітки немає — показуємо його id
|
| 400 |
+
label = d.get("label") or n.split("/", 1)[-1]
|
| 401 |
+
if n in values:
|
| 402 |
+
label = f"{label} {values[n]}"
|
| 403 |
+
nodes.append({"id": n, "type": d.get("type", "?"), "label": label})
|
| 404 |
edges = [
|
| 405 |
{"source": u, "target": v, "rel": d.get("rel", "")}
|
| 406 |
for u, v, d in g.edges(data=True)
|
livemedcard/static/index.html
CHANGED
|
@@ -330,14 +330,17 @@
|
|
| 330 |
.glegend i{width:9px;height:9px;border-radius:50%;display:inline-block}
|
| 331 |
.gwrap{overflow:auto;margin-top:6px}
|
| 332 |
.gwrap svg{display:block}
|
| 333 |
-
.gwrap .edge{stroke:var(--
|
| 334 |
-
|
|
|
|
|
|
|
|
|
|
| 335 |
.gwrap .node{cursor:pointer}
|
| 336 |
-
.gwrap .node text{font:
|
| 337 |
.gwrap .node:hover text{fill:var(--ink)}
|
| 338 |
-
.gwrap.sel .edge{opacity:.
|
| 339 |
.gwrap.sel .edge.on{opacity:1;stroke:var(--accent);stroke-width:2}
|
| 340 |
-
.gwrap.sel .node{opacity:.
|
| 341 |
.gwrap.sel .node.on{opacity:1}
|
| 342 |
.gwrap.sel .node.on text{fill:var(--ink);font-weight:700}
|
| 343 |
.drawer .body{padding:14px 18px 20px;overflow:auto;white-space:pre-wrap;
|
|
@@ -1087,7 +1090,7 @@ function drawGraph(g){
|
|
| 1087 |
|
| 1088 |
const nodes=g.nodes.filter(n=>pos[n.id]).map(n=>{
|
| 1089 |
const p=pos[n.id], right=p.x===X[2];
|
| 1090 |
-
const label=n.label.length>
|
| 1091 |
return `<g class="node" data-id="${n.id}">
|
| 1092 |
<circle cx="${p.x}" cy="${p.y}" r="5.5" fill="${GCOLOR[n.type]||'#888'}"/>
|
| 1093 |
<text x="${right?p.x+11:p.x-11}" y="${p.y+4}" text-anchor="${right?'start':'end'}">${label}</text>
|
|
|
|
| 330 |
.glegend i{width:9px;height:9px;border-radius:50%;display:inline-block}
|
| 331 |
.gwrap{overflow:auto;margin-top:6px}
|
| 332 |
.gwrap svg{display:block}
|
| 333 |
+
.gwrap .edge{stroke:color-mix(in srgb,var(--muted) 42%,transparent);stroke-width:1.3;
|
| 334 |
+
fill:none;transition:.16s}
|
| 335 |
+
/* provenance (факт → документ) — головне ребро графа, тож видиме й іменоване */
|
| 336 |
+
.gwrap .edge.prov{stroke:color-mix(in srgb,var(--accent) 55%,transparent);
|
| 337 |
+
stroke-width:1.5;stroke-dasharray:4 3}
|
| 338 |
.gwrap .node{cursor:pointer}
|
| 339 |
+
.gwrap .node text{font:11.5px/1 var(--mono);fill:var(--ink);transition:.16s}
|
| 340 |
.gwrap .node:hover text{fill:var(--ink)}
|
| 341 |
+
.gwrap.sel .edge{opacity:.1}
|
| 342 |
.gwrap.sel .edge.on{opacity:1;stroke:var(--accent);stroke-width:2}
|
| 343 |
+
.gwrap.sel .node{opacity:.28}
|
| 344 |
.gwrap.sel .node.on{opacity:1}
|
| 345 |
.gwrap.sel .node.on text{fill:var(--ink);font-weight:700}
|
| 346 |
.drawer .body{padding:14px 18px 20px;overflow:auto;white-space:pre-wrap;
|
|
|
|
| 1090 |
|
| 1091 |
const nodes=g.nodes.filter(n=>pos[n.id]).map(n=>{
|
| 1092 |
const p=pos[n.id], right=p.x===X[2];
|
| 1093 |
+
const label=n.label.length>30?n.label.slice(0,29)+'…':n.label;
|
| 1094 |
return `<g class="node" data-id="${n.id}">
|
| 1095 |
<circle cx="${p.x}" cy="${p.y}" r="5.5" fill="${GCOLOR[n.type]||'#888'}"/>
|
| 1096 |
<text x="${right?p.x+11:p.x-11}" y="${p.y+4}" text-anchor="${right?'start':'end'}">${label}</text>
|