diff src/graph_view.ts @ 8:6fefd287aff9

closer- element now holds a changing graph, but can't draw it yet
author drewp@bigasterisk.com
date Thu, 05 Dec 2019 01:32:13 -0800
parents
children 26d3e4860adc
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/graph_view.ts	Thu Dec 05 01:32:13 2019 -0800
@@ -0,0 +1,206 @@
+// from /my/site/homepage/www/rdf/browse/graphView.js
+
+import { html } from 'lit-html';
+import { SuffixLabels } from './suffixLabels.js';
+import { Store, Quad, DataFactory } from "n3"
+const { namedNode, literal, quad } = DataFactory;
+
+const groupByRdfType = (graph: Store) => {
+  const env = graph.store.rdf;
+  const rdfType = env.createNamedNode('rdf:type');
+  const byType = new Map(); // type : subjs
+  const untyped = new Set(); // subjs
+  graph.quadStore.quads({}, (q) => {
+    let subjType = null;
+    graph.quadStore.quads({
+      subject: q.subject,
+      predicate: rdfType
+    },
+      (q2) => { subjType = q2.object; });
+    if (subjType) {
+      subjType = subjType.toString();
+      if (!byType.has(subjType)) {
+        byType.set(subjType, new Set());
+      }
+      byType.get(subjType).add(q.subject.toString());
+    } else {
+      untyped.add(q.subject.toString());
+    }
+
+  });
+  return { byType: byType, untyped: untyped };
+};
+
+const graphView = (graph: Store) => {
+  const env = graph.store.rdf;
+
+  const labels = new SuffixLabels();
+  graph.quadStore.quads({}, (q) => {
+    if (q.subject.interfaceName == "NamedNode") { labels.planDisplayForNode(q.subject); }
+    if (q.predicate.interfaceName == "NamedNode") { labels.planDisplayForNode(q.predicate); }
+    if (q.object.interfaceName == "NamedNode") { labels.planDisplayForNode(q.object); }
+    if (q.object.interfaceName == "Literal" && q.object.datatype) { labels.planDisplayForNode(env.createNamedNode(q.object.datatype)); }
+  });
+
+  const rdfNode = (n) => {
+    if (n.interfaceName == "Literal") {
+      let dtPart: any = "";
+      if (n.datatype) {
+        dtPart = html`
+        ^^<span class="literalType">
+          ${rdfNode(env.createNamedNode(n.datatype))}
+        </span>`;
+      }
+      return html`<span class="literal">${n.nominalValue}${dtPart}</span>`;
+    }
+    if (n.interfaceName == "NamedNode") {
+      let dn = labels.getLabelForNode(n);
+      if (dn.match(/XMLSchema#.*/)) { dn = dn.replace('XMLSchema#', 'xsd:'); }
+      if (dn.match(/rdf-schema#.*/)) { dn = dn.replace('rdf-schema#', 'rdfs:'); }
+      return html`<a class="graphUri" href="${n.toString()}">${dn}</a>`;
+    }
+
+    return html`[${n.interfaceName} ${n.toNT()}]`;
+  }
+
+  const objBlock = (obj) => {
+    return html`
+        <div class="object">
+          ${rdfNode(obj)} <!-- indicate what source or graph said this stmt -->
+        </div>
+    `;
+  };
+
+  /// bunch of table rows
+  const predBlock = (subj, pred) => {
+    const objsSet = new Set();
+    graph.quadStore.quads({ subject: subj, predicate: pred }, (q) => {
+
+      if (q.object.length) {
+        console.log(q.object)
+      }
+      objsSet.add(q.object);
+    });
+    const objs = Array.from(objsSet.values());
+    objs.sort();
+    return html`
+      <div class="predicate">${rdfNode(pred)}
+        <div>
+          ${objs.map(objBlock)}
+        </div>
+      </div>
+    `;
+  };
+
+  const { byType, untyped } = groupByRdfType(graph);
+  const typedSubjs = Array.from(byType.keys());
+  typedSubjs.sort();
+
+  const untypedSubjs = Array.from(untyped.values());
+  untypedSubjs.sort();
+
+  const subjBlock = (subj) => {
+    const subjNode = env.createNamedNode(subj);
+    const predsSet = new Set();
+    graph.quadStore.quads({ subject: subjNode }, (q) => {
+      predsSet.add(q.predicate);
+    });
+    const preds = Array.from(predsSet.values());
+    preds.sort();
+    return html`
+      <div class="subject">${rdfNode(subjNode)}
+        <!-- todo: special section for uri/type-and-icon/label/comment -->
+        <div>
+          ${preds.map((p) => { return predBlock(subjNode, p); })}
+        </div>
+      </div>
+    `;
+  };
+  const byTypeBlock = (typeUri) => {
+    const subjs = Array.from(byType.get(typeUri));
+    subjs.sort();
+
+    const graphCells = new Map(); // [subj, pred] : objs
+    const preds = new Set();
+
+    subjs.forEach((subj) => {
+      graph.quadStore.quads({ subject: env.createNamedNode(subj) }, (q) => {
+        preds.add(q.predicate.toString());
+        const cellKey = subj + '|||' + q.predicate.toString();
+        if (!graphCells.has(cellKey)) {
+          graphCells.set(cellKey, new Set());
+        }
+        graphCells.get(cellKey).add(q.object);
+      });
+    });
+    const predsList = Array.from(preds);
+    predsList.splice(predsList.indexOf('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), 1);
+    // also pull out label, which should be used on 1st column
+    predsList.sort();
+
+    const thead = () => {
+      const predColumnHead = (pred) => {
+        return html`<th>${rdfNode(env.createNamedNode(pred))}</th>`;
+      };
+      return html`
+              <thead>
+                <tr>
+                  <th></th>
+                  ${predsList.map(predColumnHead)}
+                </tr>
+              </thead>`;
+    };
+
+    const instanceRow = (subj) => {
+      const cell = (pred) => {
+        const objs = graphCells.get(subj + '|||' + pred);
+        if (!objs) {
+          return html`<td></td>`;
+        }
+        const objsList = Array.from(objs);
+        objsList.sort();
+        const draw = (obj) => {
+          return html`<div>${rdfNode(obj)}</div>`
+        };
+        return html`<td>${objsList.map(draw)}</td>`;
+      };
+
+      return html`
+              <tr>
+                <td>${rdfNode(env.createNamedNode(subj))}</td>
+                ${predsList.map(cell)}
+              </tr>
+            `;
+    };
+
+    return html`
+          <div>[icon] ${rdfNode(env.createNamedNode(typeUri))} resources</div>
+<div class="typeBlockScroll">
+          <table class="typeBlock">
+            ${thead()}
+            ${subjs.map(instanceRow)}
+          </table>
+</div>
+        `;
+  };
+
+  return html`
+      <link rel="stylesheet" href="/rdf/browse/style.css">
+
+      <section>
+        <h2>
+          Current graph (<a href="${graph.events.url}">${graph.events.url}</a>)
+        </h2>
+        <div>
+         <!-- todo: graphs and provenance.
+          These statements are all in the
+          <span data-bind="html: $root.createCurie(graphUri())">...</span> graph.-->
+        </div>
+        ${typedSubjs.map(byTypeBlock)}
+        <div class="spoGrid">
+          ${untypedSubjs.map(subjBlock)}
+        </div>
+      </section>
+    `;
+}
+export { graphView }