DocUA commited on
Commit
5a3f318
·
1 Parent(s): 04fe8ef

fix(graph): visible edges + measurement values in node labels

Browse files
Files changed (2) hide show
  1. livemedcard/api.py +18 -10
  2. 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
- g = state.card.graph.g
387
- nodes = [
388
- {
389
- "id": n,
390
- "type": d.get("type", "?"),
391
- # у вузла-документа мітки немає — показуємо його id
392
- "label": d.get("label") or n.split("/", 1)[-1],
393
- }
394
- for n, d in g.nodes(data=True)
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(--line);stroke-width:1.2;fill:none;transition:.16s}
334
- .gwrap .edge.prov{stroke-dasharray:3 3}
 
 
 
335
  .gwrap .node{cursor:pointer}
336
- .gwrap .node text{font:11px/1 var(--mono);fill:var(--muted);transition:.16s}
337
  .gwrap .node:hover text{fill:var(--ink)}
338
- .gwrap.sel .edge{opacity:.12}
339
  .gwrap.sel .edge.on{opacity:1;stroke:var(--accent);stroke-width:2}
340
- .gwrap.sel .node{opacity:.3}
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>26?n.label.slice(0,25)+'…':n.label;
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>