Mercurial > code > home > repos > streamed-graph
comparison src/graph_view.ts @ 79:0c188ed3bcd8
starting lit upgrade. total mess right now
author | drewp@bigasterisk.com |
---|---|
date | Wed, 17 Nov 2021 13:01:08 -0800 |
parents | 58676ebdce0f |
children | 067d66a45a51 |
comparison
equal
deleted
inserted
replaced
78:ea9c9db282d6 | 79:0c188ed3bcd8 |
---|---|
1 import { html, TemplateResult } from 'lit-html'; | 1 // import { html, TemplateResult } from 'lit-html'; |
2 import { DataFactory, Literal, N3Store, NamedNode, Quad, Quad_Object, Term, Util } from 'n3'; | 2 // import { DataFactory, Literal, N3Store, NamedNode, Quad, Quad_Object, Term, Util } from 'n3'; |
3 | 3 |
4 import { SuffixLabels } from './suffixLabels'; | 4 // import { SuffixLabels } from './suffixLabels'; |
5 | 5 |
6 const { namedNode } = DataFactory; | 6 // const { namedNode } = DataFactory; |
7 | 7 |
8 // import ns from 'n3/src/IRIs'; | 8 // // import ns from 'n3/src/IRIs'; |
9 // const { rdf } = ns; | 9 // // const { rdf } = ns; |
10 const rdf = { | 10 // const rdf = { |
11 type: namedNode("http://www.w3.org/1999/02/22-rdf-syntax-ns#type") | 11 // type: namedNode("http://www.w3.org/1999/02/22-rdf-syntax-ns#type") |
12 }; | 12 // }; |
13 | 13 |
14 type TypeToSubjs = Map<NamedNode, Set<NamedNode>>; | 14 // type TypeToSubjs = Map<NamedNode, Set<NamedNode>>; |
15 // When there are multiple types, an arbitrary one is used. | 15 // // When there are multiple types, an arbitrary one is used. |
16 function groupByRdfType( | 16 // function groupByRdfType( |
17 graph: N3Store | 17 // graph: N3Store |
18 ): { byType: TypeToSubjs; untyped: Set<NamedNode> } { | 18 // ): { byType: TypeToSubjs; untyped: Set<NamedNode> } { |
19 const rdfType = rdf.type; | 19 // const rdfType = rdf.type; |
20 const byType: TypeToSubjs = new Map(); | 20 // const byType: TypeToSubjs = new Map(); |
21 const untyped: Set<NamedNode> = new Set(); // subjs | 21 // const untyped: Set<NamedNode> = new Set(); // subjs |
22 const internSubjs = new Map<string, NamedNode>(); | 22 // const internSubjs = new Map<string, NamedNode>(); |
23 graph.forEach( | 23 // graph.forEach( |
24 q => { | 24 // q => { |
25 if (!Util.isNamedNode(q.subject)) { | 25 // if (!Util.isNamedNode(q.subject)) { |
26 throw new Error("unsupported " + q.subject.value); | 26 // throw new Error("unsupported " + q.subject.value); |
27 } | 27 // } |
28 const subj = q.subject as NamedNode; | 28 // const subj = q.subject as NamedNode; |
29 | 29 |
30 let subjType: NamedNode | null = null; | 30 // let subjType: NamedNode | null = null; |
31 | 31 |
32 graph.forObjects( | 32 // graph.forObjects( |
33 (o: Quad_Object) => { | 33 // (o: Quad_Object) => { |
34 subjType = o as NamedNode; | 34 // subjType = o as NamedNode; |
35 }, | 35 // }, |
36 subj, | 36 // subj, |
37 rdfType, | 37 // rdfType, |
38 null | 38 // null |
39 ); | 39 // ); |
40 | 40 |
41 if (subjType !== null) { | 41 // if (subjType !== null) { |
42 // (subj, rdf:type, subjType) in graph | 42 // // (subj, rdf:type, subjType) in graph |
43 if (!byType.has(subjType)) { | 43 // if (!byType.has(subjType)) { |
44 byType.set(subjType, new Set()); | 44 // byType.set(subjType, new Set()); |
45 } | 45 // } |
46 (byType.get(subjType) as Set<NamedNode>).add(subj); | 46 // (byType.get(subjType) as Set<NamedNode>).add(subj); |
47 } else { | 47 // } else { |
48 // no rdf:type stmt in graph | 48 // // no rdf:type stmt in graph |
49 if (!internSubjs.has(subj.value)) { | 49 // if (!internSubjs.has(subj.value)) { |
50 internSubjs.set(subj.value, subj); | 50 // internSubjs.set(subj.value, subj); |
51 } | 51 // } |
52 const intSubj: NamedNode = internSubjs.get( | 52 // const intSubj: NamedNode = internSubjs.get( |
53 subj.value as string | 53 // subj.value as string |
54 ) as NamedNode; | 54 // ) as NamedNode; |
55 untyped.add(intSubj); | 55 // untyped.add(intSubj); |
56 } | 56 // } |
57 }, | 57 // }, |
58 null, | 58 // null, |
59 null, | 59 // null, |
60 null, | 60 // null, |
61 null | 61 // null |
62 ); | 62 // ); |
63 return { byType: byType, untyped: untyped }; | 63 // return { byType: byType, untyped: untyped }; |
64 } | 64 // } |
65 | 65 |
66 class NodeDisplay { | 66 // class NodeDisplay { |
67 labels: SuffixLabels; | 67 // labels: SuffixLabels; |
68 constructor(labels: SuffixLabels) { | 68 // constructor(labels: SuffixLabels) { |
69 this.labels = labels; | 69 // this.labels = labels; |
70 } | 70 // } |
71 getHtml(n: Term | NamedNode): TemplateResult { | 71 // getHtml(n: Term | NamedNode): TemplateResult { |
72 if (Util.isLiteral(n)) { | 72 // if (Util.isLiteral(n)) { |
73 n = n as Literal; | 73 // n = n as Literal; |
74 let dtPart: any = ""; | 74 // let dtPart: any = ""; |
75 if (n.datatype) { | 75 // if (n.datatype) { |
76 dtPart = html` | 76 // dtPart = html` |
77 ^^<span class="literalType"> | 77 // ^^<span class="literalType"> |
78 ${this.getHtml(n.datatype)} | 78 // ${this.getHtml(n.datatype)} |
79 </span> | 79 // </span> |
80 `; | 80 // `; |
81 } | 81 // } |
82 return html` | 82 // return html` |
83 <span class="literal">${n.value}${dtPart}</span> | 83 // <span class="literal">${n.value}${dtPart}</span> |
84 `; | 84 // `; |
85 } | 85 // } |
86 | 86 |
87 if (Util.isNamedNode(n)) { | 87 // if (Util.isNamedNode(n)) { |
88 n = n as NamedNode; | 88 // n = n as NamedNode; |
89 let shortened = false; | 89 // let shortened = false; |
90 let uriValue: string = n.value; | 90 // let uriValue: string = n.value; |
91 for (let [long, short] of [ | 91 // for (let [long, short] of [ |
92 ["http://www.w3.org/1999/02/22-rdf-syntax-ns#", "rdf:"], | 92 // ["http://www.w3.org/1999/02/22-rdf-syntax-ns#", "rdf:"], |
93 ["http://www.w3.org/2000/01/rdf-schema#", "rdfs:"], | 93 // ["http://www.w3.org/2000/01/rdf-schema#", "rdfs:"], |
94 ["http://purl.org/dc/elements/1.1/", "dc:"], | 94 // ["http://purl.org/dc/elements/1.1/", "dc:"], |
95 ["http://www.w3.org/2001/XMLSchema#", "xsd:"] | 95 // ["http://www.w3.org/2001/XMLSchema#", "xsd:"] |
96 ]) { | 96 // ]) { |
97 if (uriValue.startsWith(long)) { | 97 // if (uriValue.startsWith(long)) { |
98 uriValue = short + uriValue.substr(long.length); | 98 // uriValue = short + uriValue.substr(long.length); |
99 shortened = true; | 99 // shortened = true; |
100 break; | 100 // break; |
101 } | 101 // } |
102 } | 102 // } |
103 if (!shortened) { | 103 // if (!shortened) { |
104 let dn: string | undefined = this.labels.getLabelForNode(uriValue); | 104 // let dn: string | undefined = this.labels.getLabelForNode(uriValue); |
105 if (dn === undefined) { | 105 // if (dn === undefined) { |
106 throw new Error(`dn=${dn}`); | 106 // throw new Error(`dn=${dn}`); |
107 } | 107 // } |
108 uriValue = dn; | 108 // uriValue = dn; |
109 } | 109 // } |
110 | 110 |
111 return html` | 111 // return html` |
112 <a class="graphUri" href="${n.value}">${uriValue}</a> | 112 // <a class="graphUri" href="${n.value}">${uriValue}</a> |
113 `; | 113 // `; |
114 } | 114 // } |
115 | 115 |
116 return html` | 116 // return html` |
117 [${n.termType} ${n.value}] | 117 // [${n.termType} ${n.value}] |
118 `; | 118 // `; |
119 } | 119 // } |
120 } | 120 // } |
121 | 121 |
122 export class GraphView { | 122 // export class GraphView { |
123 url: string; | 123 // url: string; |
124 graph: N3Store; | 124 // graph: N3Store; |
125 nodeDisplay: NodeDisplay; | 125 // nodeDisplay: NodeDisplay; |
126 constructor(url: string, graph: N3Store) { | 126 // constructor(url: string, graph: N3Store) { |
127 this.url = url; | 127 // this.url = url; |
128 this.graph = graph; | 128 // this.graph = graph; |
129 | 129 |
130 const labels = new SuffixLabels(); | 130 // const labels = new SuffixLabels(); |
131 this._addLabelsForAllTerms(labels); | 131 // this._addLabelsForAllTerms(labels); |
132 this.nodeDisplay = new NodeDisplay(labels); | 132 // this.nodeDisplay = new NodeDisplay(labels); |
133 } | 133 // } |
134 | 134 |
135 _addLabelsForAllTerms(labels: SuffixLabels) { | 135 // _addLabelsForAllTerms(labels: SuffixLabels) { |
136 return this.graph.forEach( | 136 // return this.graph.forEach( |
137 (q: Quad) => { | 137 // (q: Quad) => { |
138 if (q.subject.termType === "NamedNode") { | 138 // if (q.subject.termType === "NamedNode") { |
139 labels.planDisplayForNode(q.subject); | 139 // labels.planDisplayForNode(q.subject); |
140 } | 140 // } |
141 if (q.predicate.termType === "NamedNode") { | 141 // if (q.predicate.termType === "NamedNode") { |
142 labels.planDisplayForNode(q.predicate); | 142 // labels.planDisplayForNode(q.predicate); |
143 } | 143 // } |
144 if (q.object.termType === "NamedNode") { | 144 // if (q.object.termType === "NamedNode") { |
145 labels.planDisplayForNode(q.object); | 145 // labels.planDisplayForNode(q.object); |
146 } | 146 // } |
147 if (q.object.termType === "Literal" && q.object.datatype) { | 147 // if (q.object.termType === "Literal" && q.object.datatype) { |
148 labels.planDisplayForNode(q.object.datatype); | 148 // labels.planDisplayForNode(q.object.datatype); |
149 } | 149 // } |
150 }, | 150 // }, |
151 null, | 151 // null, |
152 null, | 152 // null, |
153 null, | 153 // null, |
154 null | 154 // null |
155 ); | 155 // ); |
156 } | 156 // } |
157 | 157 |
158 _subjBlock(subj: NamedNode) { | 158 // _subjBlock(subj: NamedNode) { |
159 const predsSet: Set<NamedNode> = new Set(); | 159 // const predsSet: Set<NamedNode> = new Set(); |
160 this.graph.forEach( | 160 // this.graph.forEach( |
161 (q: Quad) => { | 161 // (q: Quad) => { |
162 predsSet.add(q.predicate as NamedNode); | 162 // predsSet.add(q.predicate as NamedNode); |
163 }, | 163 // }, |
164 subj, | 164 // subj, |
165 null, | 165 // null, |
166 null, | 166 // null, |
167 null | 167 // null |
168 ); | 168 // ); |
169 const preds = Array.from(predsSet.values()); | 169 // const preds = Array.from(predsSet.values()); |
170 preds.sort(); | 170 // preds.sort(); |
171 return html` | 171 // return html` |
172 <div class="subject"> | 172 // <div class="subject"> |
173 ${this.nodeDisplay.getHtml(subj)} | 173 // ${this.nodeDisplay.getHtml(subj)} |
174 <!-- todo: special section for uri/type-and-icon/label/comment --> | 174 // <!-- todo: special section for uri/type-and-icon/label/comment --> |
175 <div> | 175 // <div> |
176 ${preds.map(p => { | 176 // ${preds.map(p => { |
177 return this._predBlock(subj, p); | 177 // return this._predBlock(subj, p); |
178 })} | 178 // })} |
179 </div> | 179 // </div> |
180 </div> | 180 // </div> |
181 `; | 181 // `; |
182 } | 182 // } |
183 | 183 |
184 _objBlock(obj: Term) { | 184 // _objBlock(obj: Term) { |
185 return html` | 185 // return html` |
186 <div class="object"> | 186 // <div class="object"> |
187 ${this.nodeDisplay.getHtml(obj)} | 187 // ${this.nodeDisplay.getHtml(obj)} |
188 <!-- indicate what source or graph said this stmt --> | 188 // <!-- indicate what source or graph said this stmt --> |
189 </div> | 189 // </div> |
190 `; | 190 // `; |
191 } | 191 // } |
192 | 192 |
193 _predBlock(subj: NamedNode, pred: NamedNode) { | 193 // _predBlock(subj: NamedNode, pred: NamedNode) { |
194 const objsSet = new Set<Term>(); | 194 // const objsSet = new Set<Term>(); |
195 this.graph.forEach( | 195 // this.graph.forEach( |
196 (q: Quad) => { | 196 // (q: Quad) => { |
197 objsSet.add(q.object); | 197 // objsSet.add(q.object); |
198 }, | 198 // }, |
199 subj, | 199 // subj, |
200 pred, | 200 // pred, |
201 null, | 201 // null, |
202 null | 202 // null |
203 ); | 203 // ); |
204 const objs = Array.from(objsSet.values()); | 204 // const objs = Array.from(objsSet.values()); |
205 objs.sort(); | 205 // objs.sort(); |
206 return html` | 206 // return html` |
207 <div class="predicate"> | 207 // <div class="predicate"> |
208 ${this.nodeDisplay.getHtml(pred)} | 208 // ${this.nodeDisplay.getHtml(pred)} |
209 <div> | 209 // <div> |
210 ${objs.map(this._objBlock.bind(this))} | 210 // ${objs.map(this._objBlock.bind(this))} |
211 </div> | 211 // </div> |
212 </div> | 212 // </div> |
213 `; | 213 // `; |
214 } | 214 // } |
215 | 215 |
216 byTypeBlock(byType: TypeToSubjs, typeUri: NamedNode) { | 216 // byTypeBlock(byType: TypeToSubjs, typeUri: NamedNode) { |
217 const subjSet = byType.get(typeUri); | 217 // const subjSet = byType.get(typeUri); |
218 const subjs: Array<NamedNode> = subjSet ? Array.from(subjSet) : []; | 218 // const subjs: Array<NamedNode> = subjSet ? Array.from(subjSet) : []; |
219 subjs.sort(); | 219 // subjs.sort(); |
220 | 220 |
221 const graphCells = new Map<string, Set<Term>>(); // [subj, pred] : objs | 221 // const graphCells = new Map<string, Set<Term>>(); // [subj, pred] : objs |
222 const makeCellKey = (subj: NamedNode, pred: NamedNode) => | 222 // const makeCellKey = (subj: NamedNode, pred: NamedNode) => |
223 subj.value + "|||" + pred.value; | 223 // subj.value + "|||" + pred.value; |
224 const preds = new Set<NamedNode>(); | 224 // const preds = new Set<NamedNode>(); |
225 | 225 |
226 subjs.forEach((subj: NamedNode) => { | 226 // subjs.forEach((subj: NamedNode) => { |
227 this.graph.forEach( | 227 // this.graph.forEach( |
228 (q: Quad) => { | 228 // (q: Quad) => { |
229 if (!Util.isNamedNode(q.predicate)) { | 229 // if (!Util.isNamedNode(q.predicate)) { |
230 throw new Error(); | 230 // throw new Error(); |
231 } | 231 // } |
232 preds.add(q.predicate as NamedNode); | 232 // preds.add(q.predicate as NamedNode); |
233 const cellKey = makeCellKey(subj, q.predicate as NamedNode); | 233 // const cellKey = makeCellKey(subj, q.predicate as NamedNode); |
234 if (!graphCells.has(cellKey)) { | 234 // if (!graphCells.has(cellKey)) { |
235 graphCells.set(cellKey, new Set<Term>()); | 235 // graphCells.set(cellKey, new Set<Term>()); |
236 } | 236 // } |
237 graphCells.get(cellKey)!.add(q.object); | 237 // graphCells.get(cellKey)!.add(q.object); |
238 }, | 238 // }, |
239 subj, | 239 // subj, |
240 null, | 240 // null, |
241 null, | 241 // null, |
242 null | 242 // null |
243 ); | 243 // ); |
244 }); | 244 // }); |
245 const predsList = Array.from(preds); | 245 // const predsList = Array.from(preds); |
246 predsList.splice(predsList.indexOf(rdf.type), 1); | 246 // predsList.splice(predsList.indexOf(rdf.type), 1); |
247 // also pull out label, which should be used on 1st column | 247 // // also pull out label, which should be used on 1st column |
248 predsList.sort(); | 248 // predsList.sort(); |
249 | 249 |
250 const thead = () => { | 250 // const thead = () => { |
251 const predColumnHead = (pred: NamedNode) => { | 251 // const predColumnHead = (pred: NamedNode) => { |
252 return html` | 252 // return html` |
253 <th>${this.nodeDisplay.getHtml(pred)}</th> | 253 // <th>${this.nodeDisplay.getHtml(pred)}</th> |
254 `; | 254 // `; |
255 }; | 255 // }; |
256 return html` | 256 // return html` |
257 <thead> | 257 // <thead> |
258 <tr> | 258 // <tr> |
259 <th></th> | 259 // <th></th> |
260 ${predsList.map(predColumnHead)} | 260 // ${predsList.map(predColumnHead)} |
261 </tr> | 261 // </tr> |
262 </thead> | 262 // </thead> |
263 `; | 263 // `; |
264 }; | 264 // }; |
265 | 265 |
266 const instanceRow = (subj: NamedNode) => { | 266 // const instanceRow = (subj: NamedNode) => { |
267 const cell = (pred: NamedNode) => { | 267 // const cell = (pred: NamedNode) => { |
268 const objs = graphCells.get(subj + "|||" + pred); | 268 // const objs = graphCells.get(subj + "|||" + pred); |
269 if (!objs) { | 269 // if (!objs) { |
270 return html` | 270 // return html` |
271 <td></td> | 271 // <td></td> |
272 `; | 272 // `; |
273 } | 273 // } |
274 const objsList = Array.from(objs); | 274 // const objsList = Array.from(objs); |
275 objsList.sort(); | 275 // objsList.sort(); |
276 const draw = (obj: Term) => { | 276 // const draw = (obj: Term) => { |
277 return html` | 277 // return html` |
278 <div>${this.nodeDisplay.getHtml(obj)}</div> | 278 // <div>${this.nodeDisplay.getHtml(obj)}</div> |
279 `; | 279 // `; |
280 }; | 280 // }; |
281 return html` | 281 // return html` |
282 <td>${objsList.map(draw)}</td> | 282 // <td>${objsList.map(draw)}</td> |
283 `; | 283 // `; |
284 }; | 284 // }; |
285 | 285 |
286 return html` | 286 // return html` |
287 <tr> | 287 // <tr> |
288 <td>${this.nodeDisplay.getHtml(subj)}</td> | 288 // <td>${this.nodeDisplay.getHtml(subj)}</td> |
289 ${predsList.map(cell)} | 289 // ${predsList.map(cell)} |
290 </tr> | 290 // </tr> |
291 `; | 291 // `; |
292 }; | 292 // }; |
293 | 293 |
294 return html` | 294 // return html` |
295 <div>[icon] ${this.nodeDisplay.getHtml(typeUri)} resources</div> | 295 // <div>[icon] ${this.nodeDisplay.getHtml(typeUri)} resources</div> |
296 <div class="typeBlockScroll"> | 296 // <div class="typeBlockScroll"> |
297 <table class="typeBlock"> | 297 // <table class="typeBlock"> |
298 ${thead()} ${subjs.map(instanceRow)} | 298 // ${thead()} ${subjs.map(instanceRow)} |
299 </table> | 299 // </table> |
300 </div> | 300 // </div> |
301 `; | 301 // `; |
302 } | 302 // } |
303 | 303 |
304 makeTemplate(): TemplateResult { | 304 // makeTemplate(): TemplateResult { |
305 const { byType, untyped } = groupByRdfType(this.graph); | 305 // const { byType, untyped } = groupByRdfType(this.graph); |
306 const typedSubjs = Array.from(byType.keys()); | 306 // const typedSubjs = Array.from(byType.keys()); |
307 typedSubjs.sort(); | 307 // typedSubjs.sort(); |
308 | 308 |
309 const untypedSubjs = Array.from(untyped.values()); | 309 // const untypedSubjs = Array.from(untyped.values()); |
310 untypedSubjs.sort(); | 310 // untypedSubjs.sort(); |
311 | 311 |
312 return html` | 312 // return html` |
313 <section> | 313 // <section> |
314 <h2>Current graph (<a href="${this.url}">${this.url}</a>)</h2> | 314 // <h2>Current graph (<a href="${this.url}">${this.url}</a>)</h2> |
315 <div> | 315 // <div> |
316 <!-- todo: graphs and provenance. | 316 // <!-- todo: graphs and provenance. |
317 These statements are all in the | 317 // These statements are all in the |
318 <span data-bind="html: $root.createCurie(graphUri())">...</span> graph.--> | 318 // <span data-bind="html: $root.createCurie(graphUri())">...</span> graph.--> |
319 </div> | 319 // </div> |
320 ${typedSubjs.map((t: NamedNode) => this.byTypeBlock(byType, t))} | 320 // ${typedSubjs.map((t: NamedNode) => this.byTypeBlock(byType, t))} |
321 <div class="spoGrid"> | 321 // <div class="spoGrid"> |
322 ${untypedSubjs.map(this._subjBlock.bind(this))} | 322 // ${untypedSubjs.map(this._subjBlock.bind(this))} |
323 </div> | 323 // </div> |
324 </section> | 324 // </section> |
325 `; | 325 // `; |
326 } | 326 // } |
327 } | 327 // } |