# HG changeset patch
# User drewp@bigasterisk.com
# Date 1647735784 25200
# Node ID dd3325cc023e606168c6455fa1d524406f1f3e80
# Parent 84551452a9c9f6e18998d1ef02b6041396aaa880
multiple table support in Layout
diff -r 84551452a9c9 -r dd3325cc023e src/layout/Layout.test.ts
--- a/src/layout/Layout.test.ts Sat Mar 19 16:37:29 2022 -0700
+++ b/src/layout/Layout.test.ts Sat Mar 19 17:23:04 2022 -0700
@@ -41,6 +41,12 @@
const layout = new Layout(vc);
const lr = layout.plan(await twoStatements());
});
+ it("returns no sections for empty graph", () => {
+ const vc = new ViewConfig();
+ const layout = new Layout(vc);
+ const lr = layout.plan(new Store());
+ expect(lr.sections).toHaveLength(0);
+ });
it("defaults to putting all triples in the ungrouped list", async () => {
const layout = new Layout();
const lr = layout.plan(await twoStatements());
@@ -106,71 +112,38 @@
});
});
});
+ it("makes two tables", async () => {
+ const vc = new ViewConfig();
+ await vc.readFromGraph(`
+ @prefix ex: .
+ @prefix rdfs: .
+ @prefix : .
+
+ <> a :View; :table [ :primaryType :T1 ], [ :primaryType :T2 ] .`);
+ const layout = new Layout(vc);
+ const lr = layout.plan(await typedStatements());
+ expect(lr.sections).toHaveLength(2);
+ expect(lr.sections[0]).toEqual({
+ columnHeaders: [
+ { rdfType: EX("T1"), pred: EX("color") },
+ { rdfType: EX("T1"), pred: EX("size") },
+ ],
+ rowHeaders: [EX("a"), EX("b"), EX("c"), EX("e")],
+ rows: [
+ [[EX("red")], []],
+ [[EX("blue")], []],
+ [[], []],
+ [[], [EX("small")]],
+ ],
+ });
+ expect(lr.sections[1]).toEqual({
+ columnHeaders: [{ rdfType: EX("T2"), pred: EX("size") }],
+ rowHeaders: [EX("d"), EX("e")],
+ rows: [
+ [[EX("big")]], //
+ [[EX("small")]],
+ ],
+ });
+ });
it.skip("makes a table out of ungrouped triples with the same type", async () => {});
});
-
-// describe("equality", () => {
-// test("investigation of https://github.com/rdfjs/N3.js/issues/265", () => {
-// const x = namedNode("x");
-// const x2 = namedNode("x");
-// // (NamedNode.prototype as any).hashCode = () => 0;
-// // expect((x as any).hashCode()).toEqual((x2 as any).hashCode())
-// expect(x === x2).toBeFalsy();
-// expect(x == x2).toBeFalsy();
-// expect(x.equals(x2)).toBeTruthy();
-// let imap = Immutable.Map();
-// imap = imap.set(x, 11);
-// imap = imap.set(x, 22);
-// imap = imap.set(x2, 33);
-// expect(imap.has(x)).toBeTruthy();
-// expect(imap.has(x2)).toBeTruthy();
-// expect(imap.size).toEqual(1);
-// });
-// });
-
-// describe("groupByRdfType", () => {
-// test("finds multiple graphs", () => {});
-// test("works", async () => {
-// const store = new Store();
-
-// const parser = new Parser();
-// await new Promise((res, rej) => {
-// parser.parse(
-// `PREFIX :
-// :rs1 a :Foo; :pred1 "obj1" .
-// :rs2 a :Foo; :pred1 "obj2" .
-// :rs3 a :Bar .
-// :rs4 :pred1 "obj4" .
-// `,
-// (error, quad: Quad, prefixes: Prefixes) => {
-// if (quad) {
-// store.addQuad(quad);
-// } else {
-// res(undefined);
-// }
-// }
-// );
-// });
-// const grouped = groupByRdfType(store);
-// expect(Array.from(grouped.byType.keys())).toHaveLength(2);
-// expect(grouped.byType.get(namedNode("urn:Foo"))).toEqual(
-// Immutable.Set([namedNode("urn:rs1"), namedNode("urn:rs2")])
-// );
-// expect(grouped.byType.get(namedNode("urn:Bar"))).toEqual(
-// Immutable.Set([namedNode("urn:rs3")])
-// );
-// expect(grouped.untypedSubjs).toEqual([namedNode("urn:rs4")]);
-// });
-
-// describe("MultiSubjsTypeBlockLayout", () => {
-// test("gathers subjs", () => {
-
-// });
-// test("gathers preds", () => {
-
-// });
-// test("cells reports filled cells", () => {
-
-// });
-// });
-// });
diff -r 84551452a9c9 -r dd3325cc023e src/layout/Layout.ts
--- a/src/layout/Layout.ts Sat Mar 19 16:37:29 2022 -0700
+++ b/src/layout/Layout.ts Sat Mar 19 17:23:04 2022 -0700
@@ -4,7 +4,7 @@
import { NamedNode, Quad, Store, Term } from "n3";
import { rdf, rdfs } from "./namespaces";
import { uniqueSortedTerms, UriPairMap } from "./rdf_value";
-import { TableDesc, ViewConfig } from "./ViewConfig";
+import { ViewConfig } from "./ViewConfig";
type UriSet = Immutable.Set;
export type TypeToSubjs = Immutable.Map;
@@ -71,7 +71,9 @@
preds = tagged.map((e) => e.val);
return preds;
}
-
+ gotStatements(): boolean {
+ return !this.subjSet.isEmpty();
+ }
value(): AlignedTable {
const subjs = uniqueSortedTerms(this.subjSet);
const preds = this._displayedPreds();
@@ -93,32 +95,36 @@
}
}
-function findTypesNeededForTables(viewConfig?: ViewConfig): UriSet {
- const typesToGather: NamedNode[] = [];
- if (viewConfig) {
- viewConfig.tables.forEach((t: TableDesc) => {
- typesToGather.push(t.primary);
- });
- }
- return Immutable.Set(typesToGather);
-}
+type SubjectTableBuilders = Immutable.Map<
+ NamedNode,
+ AlignedTableBuilder[]
+>;
-function findSubjectsWithTypes(graph: Store, typesToGather: UriSet): UriSet {
- const subjectsToGather: NamedNode[] = [];
- const ft = typesToGather.toArray()[0];
+function subjectsToTablesMap(
+ graph: Store,
+ tableBuilders: AlignedTableBuilder[]
+): SubjectTableBuilders {
+ let out: SubjectTableBuilders = Immutable.Map();
graph.forEach(
(q: Quad) => {
- if (q.object.equals(ft)) {
- //typesToGather.has(q.object as NamedNode)) {
- subjectsToGather.push(q.subject as NamedNode);
- }
+ const s = q.subject as NamedNode;
+ tableBuilders.forEach((tb) => {
+ if (tb.rdfType.equals(q.object)) {
+ let cur = out.get(s);
+ if (cur === undefined) {
+ cur = [];
+ out = out.set(s, cur);
+ }
+ cur.push(tb);
+ }
+ });
},
null,
rdf.type,
null,
null
);
- return Immutable.Set(subjectsToGather);
+ return out;
}
function freeStatmentsSection(stmts: Quad[]): FreeStatements {
@@ -151,29 +157,16 @@
// The description of how this page should look: sections, tables, etc.
export class Layout {
constructor(public viewConfig?: ViewConfig) {}
- plan(graph: Store): LayoutResult {
- const typesToTable = findTypesNeededForTables(this.viewConfig);
-
- const subjectsToTable = findSubjectsWithTypes(graph, typesToTable);
- const ungrouped: Quad[] = [];
- const vc = this.viewConfig;
- const table =
- vc && vc.tables.length > 0
- ? new AlignedTableBuilder(vc.tables[0].primary) //todo multiple tables
- : null;
-
+ _groupAllStatements(
+ graph: Store,
+ tablesWantingSubject: SubjectTableBuilders,
+ ungrouped: Quad[]
+ ) {
graph.forEach(
(q: Quad) => {
- let contains = false;
- subjectsToTable.forEach((s) => {
- if (s.equals(q.subject)) {
- contains = true;
- }
- });
-
- // if (subjectsToTable.has(q.subject as NamedNode) && table) { // not working
- if (contains && table) {
- table.addQuad(q);
+ const tables = tablesWantingSubject.get(q.subject as NamedNode);
+ if (tables && tables.length) {
+ tables.forEach((t: AlignedTableBuilder) => t.addQuad(q));
} else {
ungrouped.push(q);
}
@@ -183,12 +176,25 @@
null,
null
);
+ }
+ plan(graph: Store): LayoutResult {
+ const ungrouped: Quad[] = [];
+
+ const tableBuilders = this.viewConfig
+ ? this.viewConfig.tables.map((t) => new AlignedTableBuilder(t.primary))
+ : [];
+
+ const tablesWantingSubject = subjectsToTablesMap(graph, tableBuilders);
+ this._groupAllStatements(graph, tablesWantingSubject, ungrouped);
const res: LayoutResult = { sections: [] };
- if (table) {
- console.log("table value");
- res.sections.push(table.value());
+ for (const t of tableBuilders) {
+ if (t.gotStatements()) {
+ res.sections.push(t.value());
+ }
}
- res.sections.push(freeStatmentsSection(ungrouped));
+ if (ungrouped.length) {
+ res.sections.push(freeStatmentsSection(ungrouped));
+ }
return res;
}
}