Mercurial > code > home > repos > light9
annotate web/patch.ts @ 2408:7e7874fed2e3
buttons to add panels to the layout
author | drewp@bigasterisk.com |
---|---|
date | Sat, 18 May 2024 21:02:32 -0700 |
parents | 4556eebe5d73 |
children |
rev | line source |
---|---|
2091
9324fc8285ad
Effect repairs duplicate :settings edges when it finds them
drewp@bigasterisk.com
parents:
2071
diff
changeset
|
1 import * as async from "async"; |
2071 | 2 import debug from "debug"; |
2258 | 3 import * as N3 from "n3"; |
2091
9324fc8285ad
Effect repairs duplicate :settings edges when it finds them
drewp@bigasterisk.com
parents:
2071
diff
changeset
|
4 import { NamedNode, Parser, Quad, Writer } from "n3"; |
2274 | 5 import * as Immutable from "immutable"; |
2260 | 6 export interface QuadPattern { |
7 subject: N3.Quad_Subject | null; | |
8 predicate: N3.Quad_Predicate | null; | |
9 object: N3.Quad_Object | null; // literals allowed? needs review. probably 'yes'. | |
10 graph: N3.Quad_Graph | null; | |
11 } | |
12 | |
2071 | 13 const log = debug("patch"); |
14 | |
2258 | 15 export class Patch { |
16 // immutable | |
2274 | 17 private dels: Immutable.Set<Quad>; |
18 private adds: Immutable.Set<Quad>; | |
19 private _allPredsCache?: Immutable.Set<string>; | |
20 private _allSubjsCache?: Immutable.Set<string>; | |
21 constructor(dels: Iterable<Quad>, adds: Iterable<Quad>) { | |
22 this.dels = Immutable.Set(dels); | |
23 this.adds = Immutable.Set(adds); | |
2258 | 24 this.validate(); |
25 } | |
2274 | 26 |
2258 | 27 private validate() { |
28 // todo: finish porting this from coffeescript | |
2274 | 29 this.adds.union(this.dels).forEach((q: Quad) => { |
30 if (!q.equals) { | |
31 throw new Error("doesn't look like a proper Quad"); | |
32 } | |
33 if (!q.subject.id || q.graph.id == null || q.predicate.id == null) { | |
34 throw new Error(`corrupt patch: ${JSON.stringify(q)}`); | |
35 } | |
36 if ( | |
37 q.object.termType == "Literal" && | |
38 (q.object.datatypeString == "http://www.w3.org/2001/XMLSchema#float" || q.object.datatypeString == "http://www.w3.org/2001/XMLSchema#double") | |
39 ) { | |
40 throw new Error(`${JSON.stringify(q)} is using non-decimal for numbers, which is going to break some comparisons`); | |
41 } | |
42 }); | |
2258 | 43 } |
44 | |
2260 | 45 matches(pat: QuadPattern): boolean { |
46 const allQuads = this.dels.concat(this.adds); | |
47 return allQuads.some((quad) => { | |
48 return ( | |
49 (pat.subject === null || pat.subject.equals(quad.subject)) && // | |
50 (pat.predicate === null || pat.predicate.equals(quad.predicate)) && // | |
51 (pat.object === null || pat.object.equals(quad.object)) && // | |
52 (pat.graph === null || pat.graph.equals(quad.graph)) | |
53 ); | |
54 }); | |
55 } | |
56 | |
2258 | 57 isEmpty() { |
2274 | 58 return Immutable.is(this.dels, this.adds); |
2258 | 59 } |
60 | |
61 applyToGraph(g: N3.Store) { | |
2274 | 62 for (let quad of this.dels) { |
2258 | 63 g.removeQuad(quad); |
64 } | |
2274 | 65 for (let quad of this.adds) { |
2258 | 66 g.addQuad(quad); |
67 } | |
68 } | |
69 | |
70 update(other: Patch): Patch { | |
2274 | 71 // this is approx, since it doesnt handle cancelling existing quads. |
72 return new Patch(this.dels.union(other.dels), this.adds.union(other.adds)); | |
2258 | 73 } |
74 | |
75 summary(): string { | |
2274 | 76 return "-" + this.dels.size + " +" + this.adds.size; |
2258 | 77 } |
78 | |
2268 | 79 dump(): string { |
2322 | 80 if (this.dels.size + this.adds.size > 20) { |
81 return this.summary(); | |
82 } | |
2268 | 83 const lines: string[] = []; |
84 const s = (term: N3.Term): string => { | |
2273 | 85 if (term.termType == "Literal") return term.value; |
86 if (term.termType == "NamedNode") | |
87 return term.value | |
88 .replace("http://light9.bigasterisk.com/effect/", "effect:") | |
89 .replace("http://light9.bigasterisk.com/", ":") | |
90 .replace("http://www.w3.org/2000/01/rdf-schema#", "rdfs:") | |
91 .replace("http://www.w3.org/1999/02/22-rdf-syntax-ns#", "rdf:"); | |
92 if (term.termType == "BlankNode") return "_:" + term.value; | |
2268 | 93 return term.id; |
94 }; | |
2273 | 95 const delPrefix = "- ", |
96 addPrefix = "\u200B+ "; // dels to sort before adds | |
97 this.dels.forEach((d) => lines.push(delPrefix + s(d.subject) + " " + s(d.predicate) + " " + s(d.object))); | |
98 this.adds.forEach((d) => lines.push(addPrefix + s(d.subject) + " " + s(d.predicate) + " " + s(d.object))); | |
2268 | 99 lines.sort(); |
2277 | 100 return lines.join("\n") + "\n" + (this.isEmpty() ? "(empty)" : "(nonempty)"); |
2268 | 101 } |
102 | |
2258 | 103 async toJsonPatch(): Promise<string> { |
104 return new Promise((res, rej) => { | |
105 const out: SyncgraphPatchMessage = { patch: { adds: "", deletes: "" } }; | |
106 | |
107 const writeDels = (cb1: () => void) => { | |
108 const writer = new Writer({ format: "N-Quads" }); | |
2274 | 109 writer.addQuads(this.dels.toArray()); |
2258 | 110 writer.end(function (err: any, result: string) { |
111 out.patch.deletes = result; | |
112 cb1(); | |
113 }); | |
114 }; | |
115 | |
116 const writeAdds = (cb2: () => void) => { | |
117 const writer = new Writer({ format: "N-Quads" }); | |
2274 | 118 writer.addQuads(this.adds.toArray()); |
2258 | 119 writer.end(function (err: any, result: string) { |
120 out.patch.adds = result; | |
121 cb2(); | |
122 }); | |
123 }; | |
124 | |
125 async.parallel([writeDels, writeAdds], (err: any) => res(JSON.stringify(out))); | |
126 }); | |
127 } | |
128 | |
2274 | 129 containsAnyPreds(preds: Iterable<NamedNode>): boolean { |
2258 | 130 if (this._allPredsCache === undefined) { |
2274 | 131 this._allPredsCache = Immutable.Set(); |
132 this._allPredsCache.withMutations((cache) => { | |
133 for (let qq of [this.adds, this.dels]) { | |
134 for (let q of Array.from(qq)) { | |
135 cache.add(q.predicate.value); | |
136 } | |
2258 | 137 } |
2274 | 138 }); |
2258 | 139 } |
140 | |
2274 | 141 for (let p of preds) { |
2258 | 142 if (this._allPredsCache.has(p.value)) { |
143 return true; | |
144 } | |
145 } | |
146 return false; | |
147 } | |
148 | |
2274 | 149 allSubjs(): Immutable.Set<string> { |
2258 | 150 // returns subjs as Set of strings |
151 if (this._allSubjsCache === undefined) { | |
2274 | 152 this._allSubjsCache = Immutable.Set(); |
153 this._allSubjsCache.withMutations((cache) => { | |
154 for (let qq of [this.adds, this.dels]) { | |
155 for (let q of Array.from(qq)) { | |
156 cache.add(q.subject.value); | |
157 } | |
2258 | 158 } |
2274 | 159 }); |
2258 | 160 } |
161 | |
162 return this._allSubjsCache; | |
163 } | |
2267 | 164 |
2274 | 165 allPreds(): Immutable.Set<NamedNode> { |
166 // todo: this could cache | |
167 const ret = Immutable.Set<NamedNode>(); | |
168 ret.withMutations((r) => { | |
169 for (let qq of [this.adds, this.dels]) { | |
170 for (let q of Array.from(qq)) { | |
171 if (q.predicate.termType == "Variable") throw "unsupported"; | |
172 r.add(q.predicate); | |
173 } | |
2258 | 174 } |
2274 | 175 }); |
2258 | 176 return ret; |
177 } | |
2071 | 178 } |
179 | |
2267 | 180 // The schema of the json sent from graph server. |
2258 | 181 export interface SyncgraphPatchMessage { |
2071 | 182 patch: { adds: string; deletes: string }; |
183 } | |
184 | |
2258 | 185 export function patchToDeleteEntireGraph(g: N3.Store) { |
186 return new Patch(g.getQuads(null, null, null, null), []); | |
2071 | 187 } |
188 | |
2108
e92db17f3e7e
effectSequencer can now also process some note-like values coming from the fade/ ui
drewp@bigasterisk.com
parents:
2091
diff
changeset
|
189 export function parseJsonPatch(input: SyncgraphPatchMessage, cb: (p: Patch) => void): void { |
2071 | 190 // note response cb doesn't have an error arg. |
2258 | 191 const dels: Quad[] = []; |
192 const adds: Quad[] = []; | |
2071 | 193 |
2258 | 194 const parseAdds = (cb2: () => any) => { |
2071 | 195 const parser = new Parser(); |
196 return parser.parse(input.patch.adds, (error: any, quad: Quad, prefixes: any) => { | |
197 if (quad) { | |
2258 | 198 return adds.push(quad); |
2071 | 199 } else { |
2258 | 200 return cb2(); |
2071 | 201 } |
202 }); | |
203 }; | |
2258 | 204 const parseDels = (cb3: () => any) => { |
2071 | 205 const parser = new Parser(); |
206 return parser.parse(input.patch.deletes, (error: any, quad: any, prefixes: any) => { | |
207 if (quad) { | |
2258 | 208 return dels.push(quad); |
2071 | 209 } else { |
2258 | 210 return cb3(); |
2071 | 211 } |
212 }); | |
213 }; | |
214 | |
2091
9324fc8285ad
Effect repairs duplicate :settings edges when it finds them
drewp@bigasterisk.com
parents:
2071
diff
changeset
|
215 // todo: is it faster to run them in series? might be |
2258 | 216 async.parallel([parseAdds, parseDels], (err: any) => cb(new Patch(dels, adds))); |
2071 | 217 } |