comparison src/layout/Layout.ts @ 110:3cdbbd913f1d

table displays now just barely
author drewp@bigasterisk.com
date Fri, 18 Mar 2022 23:41:24 -0700
parents 5e6840229a05
children 4b33a479dc2f
comparison
equal deleted inserted replaced
109:cbcd82d21356 110:3cdbbd913f1d
1 // Organize graph data into tables (column orders, etc) for the view layer. 1 // Organize graph data into tables (column orders, etc) for the view layer.
2 2
3 import Immutable from "immutable"; // mostly using this for the builtin equals() testing, since NamedNode(x)!=NamedNode(x) 3 import Immutable from "immutable"; // mostly using this for the builtin equals() testing, since NamedNode(x)!=NamedNode(x)
4 import { NamedNode, Quad, Store, Term } from "n3"; 4 import { NamedNode, Quad, Store, Term } from "n3";
5 import { rdf } from "./namespaces"; 5 import { rdf, rdfs } from "./namespaces";
6 import { uniqueSortedTerms } from "./rdf_value"; 6 import { uniqueSortedTerms } from "./rdf_value";
7 import { TableDesc, ViewConfig } from "./ViewConfig"; 7 import { TableDesc, ViewConfig } from "./ViewConfig";
8 8
9 type UriSet = Immutable.Set<NamedNode>; 9 type UriSet = Immutable.Set<NamedNode>;
10 export type TypeToSubjs = Immutable.Map<NamedNode, UriSet>; 10 export type TypeToSubjs = Immutable.Map<NamedNode, UriSet>;
14 pred: NamedNode; 14 pred: NamedNode;
15 } 15 }
16 16
17 export interface AlignedTable { 17 export interface AlignedTable {
18 columnHeaders: ColumnHeader[]; 18 columnHeaders: ColumnHeader[];
19 rows: (Term | null)[][]; // each row is 1 wider than columnHeaders since the 1st element is the subject for that row 19 rowHeaders: NamedNode[];
20 rows: Term[][][];
20 } 21 }
21 22
22 export interface PredRow { 23 export interface PredRow {
23 pred: NamedNode; 24 pred: NamedNode;
24 objs: Term[]; 25 objs: Term[];
35 36
36 export interface LayoutResult { 37 export interface LayoutResult {
37 sections: (AlignedTable | FreeStatements)[]; 38 sections: (AlignedTable | FreeStatements)[];
38 } 39 }
39 40
41 interface ISP {
42 subj: NamedNode;
43 pred: NamedNode;
44 }
45 const makeSP = Immutable.Record<ISP>({
46 subj: new NamedNode(""),
47 pred: new NamedNode(""),
48 });
49
40 class AlignedTableBuilder { 50 class AlignedTableBuilder {
41 columnPreds = Immutable.List<NamedNode>(); 51 subjSet = Immutable.Set<NamedNode>();
42 subjRowIndices = Immutable.Map<NamedNode, number>(); 52 predSet = Immutable.Set<NamedNode>();
43 rows: (Term | null)[][] = []; 53 cell = Immutable.Map<string, Immutable.Set<Term>>();
44 constructor( 54 constructor(
45 public rdfType: NamedNode /* plus join types, sort instructions */ 55 public rdfType: NamedNode /* plus join types, sort instructions */
46 ) {} 56 ) {}
47 57
48 addQuad(q: Quad) { 58 addQuad(q: Quad) {
59 const subj = q.subject as NamedNode;
49 const pred = q.predicate as NamedNode; 60 const pred = q.predicate as NamedNode;
50 const omittedColumn = pred.equals(rdf.type); 61 this.subjSet = this.subjSet.add(subj);
51 if (!this.columnPreds.contains(pred) && !omittedColumn) { 62 this.predSet = this.predSet.add(pred);
52 this.columnPreds = this.columnPreds.push(pred); // this is putting cols in random order 63
53 this.rows.forEach((r) => r.push(null)); 64 const key =subj.id+pred.id//makeSP({ subj, pred });
65 const cur = this.cell.get(key, undefined);
66 const newval =
67 cur === undefined ? Immutable.Set([q.object]) : cur.add(q.object);
68
69 this.cell = this.cell.set(key, newval);
70 }
71
72 value(): AlignedTable {
73 let preds = uniqueSortedTerms(this.predSet);
74 const tagged = preds.map((p, i)=>{
75 if (p.equals(rdf.type)) {
76 i=999;
77 }
78 if (p.equals(rdfs.label)) {
79 i=-1
80 }
81 return {sort:i, val: p}
82 })
83 tagged.sort((a,b)=>{
84 return a.sort - b.sort;
85 });
86 preds = tagged.map((e)=>e.val);
87
88 // const omittedColumn = pred.equals(rdf.type);
89 const subjs = uniqueSortedTerms(this.subjSet);
90 const outputGrid: Term[][][] = [];
91 for (let subj of subjs) {
92 const row: Term[][] = [];
93 preds.forEach((pred) => {
94 const key = subj.id+pred.id;//makeSP({ subj, pred });
95 const objs = this.cell.get(key, Immutable.Set<Term>([]));
96 const uniq = uniqueSortedTerms(objs);
97 console.log("cell objs", objs.size, uniq.length);
98 row.push(uniq);
99 });
100 outputGrid.push(row);
54 } 101 }
55 102
56 const predIndex = omittedColumn ? null : this.columnPreds.indexOf(pred); 103 const headers = preds.map((pred) => {
57 let rowIndex = this.subjRowIndices.get(q.subject as NamedNode); 104 return { rdfType: this.rdfType, pred: pred };
58 if (rowIndex === undefined) {
59 const newRow = new Array(1 + this.columnPreds.size).fill(null);
60 newRow[0] = q.subject;
61 this.rows.push(newRow);
62 rowIndex = this.rows.length - 1;
63 this.subjRowIndices = this.subjRowIndices.set(
64 q.subject as NamedNode,
65 rowIndex
66 );
67 }
68 if (predIndex !== null) {
69 this.rows[rowIndex][1 + predIndex] = q.object;
70 }
71 }
72
73 value(): AlignedTable {
74 this.rows.sort((a, b) => {
75 const uriA = (a[0] as NamedNode).value,
76 uriB = (b[0] as NamedNode).value;
77 return uriA.localeCompare(uriB);
78 }); 105 });
79 const headers = this.columnPreds 106 return { columnHeaders: headers, rowHeaders: subjs, rows: outputGrid };
80 .map((pred) => {
81 return { rdfType: this.rdfType, pred: pred };
82 })
83 .toArray();
84 return { columnHeaders: headers, rows: this.rows };
85 } 107 }
86 } 108 }
87 109
88 function findTypesNeededForTables(viewConfig?: ViewConfig): UriSet { 110 function findTypesNeededForTables(viewConfig?: ViewConfig): UriSet {
89 const typesToGather: NamedNode[] = []; 111 const typesToGather: NamedNode[] = [];
93 }); 115 });
94 } 116 }
95 return Immutable.Set(typesToGather); 117 return Immutable.Set(typesToGather);
96 } 118 }
97 119
98 function findSubjectsWithTypes(graph: Store, typesToGatherSet: UriSet): UriSet { 120 function findSubjectsWithTypes(graph: Store, typesToGather: UriSet): UriSet {
99 const subjectsToGather: NamedNode[] = []; 121 const subjectsToGather: NamedNode[] = [];
122 const ft = typesToGather.toArray()[0];
100 graph.forEach( 123 graph.forEach(
101 (q: Quad) => { 124 (q: Quad) => {
102 if (typesToGatherSet.contains(q.object as NamedNode)) { 125 if (q.object.equals(ft)) {
126 //typesToGather.has(q.object as NamedNode)) {
103 subjectsToGather.push(q.subject as NamedNode); 127 subjectsToGather.push(q.subject as NamedNode);
104 } 128 }
105 }, 129 },
106 null, 130 null,
107 rdf.type, 131 rdf.type,
140 164
141 // The description of how this page should look: sections, tables, etc. 165 // The description of how this page should look: sections, tables, etc.
142 export class Layout { 166 export class Layout {
143 constructor(public viewConfig?: ViewConfig) {} 167 constructor(public viewConfig?: ViewConfig) {}
144 plan(graph: Store): LayoutResult { 168 plan(graph: Store): LayoutResult {
145 const typesToGatherSet = findTypesNeededForTables(this.viewConfig); 169 const typesToTable = findTypesNeededForTables(this.viewConfig);
146 170
147 const subjectsToGatherSet = findSubjectsWithTypes(graph, typesToGatherSet); 171 const subjectsToTable = findSubjectsWithTypes(graph, typesToTable);
148 const ungrouped: Quad[] = []; 172 const ungrouped: Quad[] = [];
149 const vc = this.viewConfig; 173 const vc = this.viewConfig;
150 const table = 174 const table =
151 vc && vc.tables.length > 0 175 vc && vc.tables.length > 0
152 ? new AlignedTableBuilder(vc.tables[0].primary) 176 ? new AlignedTableBuilder(vc.tables[0].primary) //todo multiple tables
153 : null; 177 : null;
154 178
155 graph.forEach( 179 graph.forEach(
156 (q: Quad) => { 180 (q: Quad) => {
157 if (!subjectsToGatherSet.contains(q.subject as NamedNode) || !table) { 181 let contains = false;
182 subjectsToTable.forEach((s) => {
183 if (s.equals(q.subject)) {
184 contains = true;
185 }
186 });
187
188 // if (subjectsToTable.has(q.subject as NamedNode) && table) { // not working
189 if (contains && table) {
190 table.addQuad(q);
191 } else {
158 ungrouped.push(q); 192 ungrouped.push(q);
159 } else {
160 table.addQuad(q);
161 } 193 }
162 }, 194 },
163 null, 195 null,
164 null, 196 null,
165 null, 197 null,
166 null 198 null
167 ); 199 );
168 const res: LayoutResult = { sections: [] }; 200 const res: LayoutResult = { sections: [] };
169 if (table) { 201 if (table) {
202 console.log("table value");
170 res.sections.push(table.value()); 203 res.sections.push(table.value());
171 } 204 }
172 res.sections.push(freeStatmentsSection(ungrouped)); 205 res.sections.push(freeStatmentsSection(ungrouped));
173 return res; 206 return res;
174 } 207 }
175 } 208 }
176
177 // interface ISP {
178 // subj: NamedNode;
179 // pred: NamedNode;
180 // }
181 // const SP = Immutable.Record<ISP>({
182 // subj: new NamedNode(""),
183 // pred: new NamedNode(""),
184 // });
185 209
186 // // One table of rows with a common rdf:type. 210 // // One table of rows with a common rdf:type.
187 // export class MultiSubjsTypeBlockLayout { 211 // export class MultiSubjsTypeBlockLayout {
188 // subjs: NamedNode[]; 212 // subjs: NamedNode[];
189 // preds: NamedNode[]; 213 // preds: NamedNode[];