Mercurial > code > home > repos > streamed-graph
diff src/graph_view.ts @ 84:067d66a45a51
enable more code. factor out setGraphView
author | drewp@bigasterisk.com |
---|---|
date | Wed, 17 Nov 2021 16:45:10 -0800 |
parents | 0c188ed3bcd8 |
children | ac7ad087d474 |
line wrap: on
line diff
--- a/src/graph_view.ts Wed Nov 17 16:42:59 2021 -0800 +++ b/src/graph_view.ts Wed Nov 17 16:45:10 2021 -0800 @@ -1,327 +1,327 @@ -// import { html, TemplateResult } from 'lit-html'; -// import { DataFactory, Literal, N3Store, NamedNode, Quad, Quad_Object, Term, Util } from 'n3'; +import { html, TemplateResult } from 'lit'; +import { DataFactory, Literal, Store, NamedNode, Quad, Quad_Object, Term, Util } from 'n3'; -// import { SuffixLabels } from './suffixLabels'; +import { SuffixLabels } from './suffixLabels'; -// const { namedNode } = DataFactory; +const { namedNode } = DataFactory; // // import ns from 'n3/src/IRIs'; // // const { rdf } = ns; -// const rdf = { -// type: namedNode("http://www.w3.org/1999/02/22-rdf-syntax-ns#type") -// }; +const rdf = { + type: namedNode("http://www.w3.org/1999/02/22-rdf-syntax-ns#type") +}; -// type TypeToSubjs = Map<NamedNode, Set<NamedNode>>; -// // When there are multiple types, an arbitrary one is used. -// function groupByRdfType( -// graph: N3Store -// ): { byType: TypeToSubjs; untyped: Set<NamedNode> } { -// const rdfType = rdf.type; -// const byType: TypeToSubjs = new Map(); -// const untyped: Set<NamedNode> = new Set(); // subjs -// const internSubjs = new Map<string, NamedNode>(); -// graph.forEach( -// q => { -// if (!Util.isNamedNode(q.subject)) { -// throw new Error("unsupported " + q.subject.value); -// } -// const subj = q.subject as NamedNode; +type TypeToSubjs = Map<NamedNode, Set<NamedNode>>; +// When there are multiple types, an arbitrary one is used. +function groupByRdfType( + graph: Store +): { byType: TypeToSubjs; untyped: Set<NamedNode> } { + const rdfType = rdf.type; + const byType: TypeToSubjs = new Map(); + const untyped: Set<NamedNode> = new Set(); // subjs + const internSubjs = new Map<string, NamedNode>(); + graph.forEach( + q => { + if (!Util.isNamedNode(q.subject)) { + throw new Error("unsupported " + q.subject.value); + } + const subj = q.subject as NamedNode; -// let subjType: NamedNode | null = null; + let subjType: NamedNode | null = null; -// graph.forObjects( -// (o: Quad_Object) => { -// subjType = o as NamedNode; -// }, -// subj, -// rdfType, -// null -// ); + graph.forObjects( + (o: Quad_Object) => { + subjType = o as NamedNode; + }, + subj, + rdfType, + null + ); -// if (subjType !== null) { -// // (subj, rdf:type, subjType) in graph -// if (!byType.has(subjType)) { -// byType.set(subjType, new Set()); -// } -// (byType.get(subjType) as Set<NamedNode>).add(subj); -// } else { -// // no rdf:type stmt in graph -// if (!internSubjs.has(subj.value)) { -// internSubjs.set(subj.value, subj); -// } -// const intSubj: NamedNode = internSubjs.get( -// subj.value as string -// ) as NamedNode; -// untyped.add(intSubj); -// } -// }, -// null, -// null, -// null, -// null -// ); -// return { byType: byType, untyped: untyped }; -// } + if (subjType !== null) { + // (subj, rdf:type, subjType) in graph + if (!byType.has(subjType)) { + byType.set(subjType, new Set()); + } + (byType.get(subjType) as Set<NamedNode>).add(subj); + } else { + // no rdf:type stmt in graph + if (!internSubjs.has(subj.value)) { + internSubjs.set(subj.value, subj); + } + const intSubj: NamedNode = internSubjs.get( + subj.value as string + ) as NamedNode; + untyped.add(intSubj); + } + }, + null, + null, + null, + null + ); + return { byType: byType, untyped: untyped }; +} -// class NodeDisplay { -// labels: SuffixLabels; -// constructor(labels: SuffixLabels) { -// this.labels = labels; -// } -// getHtml(n: Term | NamedNode): TemplateResult { -// if (Util.isLiteral(n)) { -// n = n as Literal; -// let dtPart: any = ""; -// if (n.datatype) { -// dtPart = html` -// ^^<span class="literalType"> -// ${this.getHtml(n.datatype)} -// </span> -// `; -// } -// return html` -// <span class="literal">${n.value}${dtPart}</span> -// `; -// } +class NodeDisplay { + labels: SuffixLabels; + constructor(labels: SuffixLabels) { + this.labels = labels; + } + getHtml(n: Term | NamedNode): TemplateResult { + if (Util.isLiteral(n)) { + n = n as Literal; + let dtPart: any = ""; + if (n.datatype) { + dtPart = html` + ^^<span class="literalType"> + ${this.getHtml(n.datatype)} + </span> + `; + } + return html` + <span class="literal">${n.value}${dtPart}</span> + `; + } -// if (Util.isNamedNode(n)) { -// n = n as NamedNode; -// let shortened = false; -// let uriValue: string = n.value; -// for (let [long, short] of [ -// ["http://www.w3.org/1999/02/22-rdf-syntax-ns#", "rdf:"], -// ["http://www.w3.org/2000/01/rdf-schema#", "rdfs:"], -// ["http://purl.org/dc/elements/1.1/", "dc:"], -// ["http://www.w3.org/2001/XMLSchema#", "xsd:"] -// ]) { -// if (uriValue.startsWith(long)) { -// uriValue = short + uriValue.substr(long.length); -// shortened = true; -// break; -// } -// } -// if (!shortened) { -// let dn: string | undefined = this.labels.getLabelForNode(uriValue); -// if (dn === undefined) { -// throw new Error(`dn=${dn}`); -// } -// uriValue = dn; -// } + if (Util.isNamedNode(n)) { + n = n as NamedNode; + let shortened = false; + let uriValue: string = n.value; + for (let [long, short] of [ + ["http://www.w3.org/1999/02/22-rdf-syntax-ns#", "rdf:"], + ["http://www.w3.org/2000/01/rdf-schema#", "rdfs:"], + ["http://purl.org/dc/elements/1.1/", "dc:"], + ["http://www.w3.org/2001/XMLSchema#", "xsd:"] + ]) { + if (uriValue.startsWith(long)) { + uriValue = short + uriValue.substr(long.length); + shortened = true; + break; + } + } + if (!shortened) { + let dn: string | undefined = this.labels.getLabelForNode(uriValue); + if (dn === undefined) { + throw new Error(`dn=${dn}`); + } + uriValue = dn; + } -// return html` -// <a class="graphUri" href="${n.value}">${uriValue}</a> -// `; -// } + return html` + <a class="graphUri" href="${n.value}">${uriValue}</a> + `; + } -// return html` -// [${n.termType} ${n.value}] -// `; -// } -// } + return html` + [${n.termType} ${n.value}] + `; + } +} -// export class GraphView { -// url: string; -// graph: N3Store; -// nodeDisplay: NodeDisplay; -// constructor(url: string, graph: N3Store) { -// this.url = url; -// this.graph = graph; +export class GraphView { + url: string; + graph: Store; + nodeDisplay: NodeDisplay; + constructor(url: string, graph: Store) { + this.url = url; + this.graph = graph; -// const labels = new SuffixLabels(); -// this._addLabelsForAllTerms(labels); -// this.nodeDisplay = new NodeDisplay(labels); -// } + const labels = new SuffixLabels(); + this._addLabelsForAllTerms(labels); + this.nodeDisplay = new NodeDisplay(labels); + } -// _addLabelsForAllTerms(labels: SuffixLabels) { -// return this.graph.forEach( -// (q: Quad) => { -// if (q.subject.termType === "NamedNode") { -// labels.planDisplayForNode(q.subject); -// } -// if (q.predicate.termType === "NamedNode") { -// labels.planDisplayForNode(q.predicate); -// } -// if (q.object.termType === "NamedNode") { -// labels.planDisplayForNode(q.object); -// } -// if (q.object.termType === "Literal" && q.object.datatype) { -// labels.planDisplayForNode(q.object.datatype); -// } -// }, -// null, -// null, -// null, -// null -// ); -// } + _addLabelsForAllTerms(labels: SuffixLabels) { + return this.graph.forEach( + (q: Quad) => { + if (q.subject.termType === "NamedNode") { + labels.planDisplayForNode(q.subject); + } + if (q.predicate.termType === "NamedNode") { + labels.planDisplayForNode(q.predicate); + } + if (q.object.termType === "NamedNode") { + labels.planDisplayForNode(q.object); + } + if (q.object.termType === "Literal" && q.object.datatype) { + labels.planDisplayForNode(q.object.datatype); + } + }, + null, + null, + null, + null + ); + } -// _subjBlock(subj: NamedNode) { -// const predsSet: Set<NamedNode> = new Set(); -// this.graph.forEach( -// (q: Quad) => { -// predsSet.add(q.predicate as NamedNode); -// }, -// subj, -// null, -// null, -// null -// ); -// const preds = Array.from(predsSet.values()); -// preds.sort(); -// return html` -// <div class="subject"> -// ${this.nodeDisplay.getHtml(subj)} -// <!-- todo: special section for uri/type-and-icon/label/comment --> -// <div> -// ${preds.map(p => { -// return this._predBlock(subj, p); -// })} -// </div> -// </div> -// `; -// } + _subjBlock(subj: NamedNode) { + const predsSet: Set<NamedNode> = new Set(); + this.graph.forEach( + (q: Quad) => { + predsSet.add(q.predicate as NamedNode); + }, + subj, + null, + null, + null + ); + const preds = Array.from(predsSet.values()); + preds.sort(); + return html` + <div class="subject"> + ${this.nodeDisplay.getHtml(subj)} + <!-- todo: special section for uri/type-and-icon/label/comment --> + <div> + ${preds.map(p => { + return this._predBlock(subj, p); + })} + </div> + </div> + `; + } -// _objBlock(obj: Term) { -// return html` -// <div class="object"> -// ${this.nodeDisplay.getHtml(obj)} -// <!-- indicate what source or graph said this stmt --> -// </div> -// `; -// } + _objBlock(obj: Term) { + return html` + <div class="object"> + ${this.nodeDisplay.getHtml(obj)} + <!-- indicate what source or graph said this stmt --> + </div> + `; + } -// _predBlock(subj: NamedNode, pred: NamedNode) { -// const objsSet = new Set<Term>(); -// this.graph.forEach( -// (q: Quad) => { -// objsSet.add(q.object); -// }, -// subj, -// pred, -// null, -// null -// ); -// const objs = Array.from(objsSet.values()); -// objs.sort(); -// return html` -// <div class="predicate"> -// ${this.nodeDisplay.getHtml(pred)} -// <div> -// ${objs.map(this._objBlock.bind(this))} -// </div> -// </div> -// `; -// } + _predBlock(subj: NamedNode, pred: NamedNode) { + const objsSet = new Set<Term>(); + this.graph.forEach( + (q: Quad) => { + objsSet.add(q.object); + }, + subj, + pred, + null, + null + ); + const objs = Array.from(objsSet.values()); + objs.sort(); + return html` + <div class="predicate"> + ${this.nodeDisplay.getHtml(pred)} + <div> + ${objs.map(this._objBlock.bind(this))} + </div> + </div> + `; + } -// byTypeBlock(byType: TypeToSubjs, typeUri: NamedNode) { -// const subjSet = byType.get(typeUri); -// const subjs: Array<NamedNode> = subjSet ? Array.from(subjSet) : []; -// subjs.sort(); + byTypeBlock(byType: TypeToSubjs, typeUri: NamedNode) { + const subjSet = byType.get(typeUri); + const subjs: Array<NamedNode> = subjSet ? Array.from(subjSet) : []; + subjs.sort(); -// const graphCells = new Map<string, Set<Term>>(); // [subj, pred] : objs -// const makeCellKey = (subj: NamedNode, pred: NamedNode) => -// subj.value + "|||" + pred.value; -// const preds = new Set<NamedNode>(); + const graphCells = new Map<string, Set<Term>>(); // [subj, pred] : objs + const makeCellKey = (subj: NamedNode, pred: NamedNode) => + subj.value + "|||" + pred.value; + const preds = new Set<NamedNode>(); -// subjs.forEach((subj: NamedNode) => { -// this.graph.forEach( -// (q: Quad) => { -// if (!Util.isNamedNode(q.predicate)) { -// throw new Error(); -// } -// preds.add(q.predicate as NamedNode); -// const cellKey = makeCellKey(subj, q.predicate as NamedNode); -// if (!graphCells.has(cellKey)) { -// graphCells.set(cellKey, new Set<Term>()); -// } -// graphCells.get(cellKey)!.add(q.object); -// }, -// subj, -// null, -// null, -// null -// ); -// }); -// const predsList = Array.from(preds); -// predsList.splice(predsList.indexOf(rdf.type), 1); -// // also pull out label, which should be used on 1st column -// predsList.sort(); + subjs.forEach((subj: NamedNode) => { + this.graph.forEach( + (q: Quad) => { + if (!Util.isNamedNode(q.predicate)) { + throw new Error(); + } + preds.add(q.predicate as NamedNode); + const cellKey = makeCellKey(subj, q.predicate as NamedNode); + if (!graphCells.has(cellKey)) { + graphCells.set(cellKey, new Set<Term>()); + } + graphCells.get(cellKey)!.add(q.object); + }, + subj, + null, + null, + null + ); + }); + const predsList = Array.from(preds); + predsList.splice(predsList.indexOf(rdf.type), 1); + // also pull out label, which should be used on 1st column + predsList.sort(); -// const thead = () => { -// const predColumnHead = (pred: NamedNode) => { -// return html` -// <th>${this.nodeDisplay.getHtml(pred)}</th> -// `; -// }; -// return html` -// <thead> -// <tr> -// <th></th> -// ${predsList.map(predColumnHead)} -// </tr> -// </thead> -// `; -// }; + const thead = () => { + const predColumnHead = (pred: NamedNode) => { + return html` + <th>${this.nodeDisplay.getHtml(pred)}</th> + `; + }; + return html` + <thead> + <tr> + <th></th> + ${predsList.map(predColumnHead)} + </tr> + </thead> + `; + }; -// const instanceRow = (subj: NamedNode) => { -// const cell = (pred: NamedNode) => { -// const objs = graphCells.get(subj + "|||" + pred); -// if (!objs) { -// return html` -// <td></td> -// `; -// } -// const objsList = Array.from(objs); -// objsList.sort(); -// const draw = (obj: Term) => { -// return html` -// <div>${this.nodeDisplay.getHtml(obj)}</div> -// `; -// }; -// return html` -// <td>${objsList.map(draw)}</td> -// `; -// }; + const instanceRow = (subj: NamedNode) => { + const cell = (pred: NamedNode) => { + const objs = graphCells.get(subj + "|||" + pred); + if (!objs) { + return html` + <td></td> + `; + } + const objsList = Array.from(objs); + objsList.sort(); + const draw = (obj: Term) => { + return html` + <div>${this.nodeDisplay.getHtml(obj)}</div> + `; + }; + return html` + <td>${objsList.map(draw)}</td> + `; + }; -// return html` -// <tr> -// <td>${this.nodeDisplay.getHtml(subj)}</td> -// ${predsList.map(cell)} -// </tr> -// `; -// }; + return html` + <tr> + <td>${this.nodeDisplay.getHtml(subj)}</td> + ${predsList.map(cell)} + </tr> + `; + }; -// return html` -// <div>[icon] ${this.nodeDisplay.getHtml(typeUri)} resources</div> -// <div class="typeBlockScroll"> -// <table class="typeBlock"> -// ${thead()} ${subjs.map(instanceRow)} -// </table> -// </div> -// `; -// } + return html` + <div>[icon] ${this.nodeDisplay.getHtml(typeUri)} resources</div> + <div class="typeBlockScroll"> + <table class="typeBlock"> + ${thead()} ${subjs.map(instanceRow)} + </table> + </div> + `; + } -// makeTemplate(): TemplateResult { -// const { byType, untyped } = groupByRdfType(this.graph); -// const typedSubjs = Array.from(byType.keys()); -// typedSubjs.sort(); + makeTemplate(): TemplateResult { + const { byType, untyped } = groupByRdfType(this.graph); + const typedSubjs = Array.from(byType.keys()); + typedSubjs.sort(); -// const untypedSubjs = Array.from(untyped.values()); -// untypedSubjs.sort(); + const untypedSubjs = Array.from(untyped.values()); + untypedSubjs.sort(); -// return html` -// <section> -// <h2>Current graph (<a href="${this.url}">${this.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((t: NamedNode) => this.byTypeBlock(byType, t))} -// <div class="spoGrid"> -// ${untypedSubjs.map(this._subjBlock.bind(this))} -// </div> -// </section> -// `; -// } -// } + return html` + <section> + <h2>Current graph (<a href="${this.url}">${this.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((t: NamedNode) => this.byTypeBlock(byType, t))} + <div class="spoGrid"> + ${untypedSubjs.map(this._subjBlock.bind(this))} + </div> + </section> + `; + } +}