Mercurial > code > home > repos > homeauto
comparison service/mqtt_to_rdf/structured_log.py @ 1694:73abfd4cf5d0
new html log and other refactoring as i work on the advanceTheStack problems
https://bigasterisk.com/post/inference/2021-09-27_11-11.png
author | drewp@bigasterisk.com |
---|---|
date | Mon, 27 Sep 2021 11:22:09 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
1693:0455a1e18e4f | 1694:73abfd4cf5d0 |
---|---|
1 import re | |
2 from pathlib import Path | |
3 from typing import List, Optional, Union, cast | |
4 | |
5 import simple_html.render | |
6 from rdflib import Graph | |
7 from rdflib.term import Node | |
8 from simple_html.nodes import (SafeString, body, div, head, hr, html, span, | |
9 style, table, td, tr) | |
10 | |
11 from candidate_binding import CandidateBinding | |
12 from inference_types import Triple | |
13 from stmt_chunk import Chunk, ChunkedGraph | |
14 | |
15 CSS = SafeString(''' | |
16 @import url('https://fonts.googleapis.com/css2?family=Oxygen+Mono&display=swap'); | |
17 | |
18 * { | |
19 font-family: 'Oxygen Mono', monospace; | |
20 font-size: 10px; | |
21 } | |
22 .arrow { | |
23 font-size: 350%; | |
24 vertical-align: middle; | |
25 } | |
26 table { | |
27 border-collapse: collapse; | |
28 } | |
29 td { | |
30 vertical-align: top; | |
31 border: 1px solid gray; | |
32 padding: 2px; | |
33 } | |
34 .consider.isNew-False { | |
35 opacity: .3; | |
36 } | |
37 .iteration { | |
38 font-size: 150%; | |
39 padding: 20px 0; | |
40 background: #c7dec7; | |
41 } | |
42 .timetospin { | |
43 font-size: 150%; | |
44 margin-top: 20 px ; | |
45 padding: 10 px 0; | |
46 background: #86a886; | |
47 } | |
48 .looper { | |
49 background: #e2e2e2; | |
50 } | |
51 .alignedWorkingSetChunk tr:nth-child(even) { | |
52 background: #bddcbd; | |
53 } | |
54 .alignedWorkingSetChunk tr:nth-child(odd) { | |
55 background: hsl(120deg 31% 72%); | |
56 } | |
57 .highlight, .alignedWorkingSetChunk tr.highlight { | |
58 background: #ffffd6; | |
59 outline: 1px solid #d2d200; | |
60 padding: 2px; | |
61 line-height: 17px; | |
62 } | |
63 .node { padding: 0 2px; } | |
64 .URIRef { background: #e0b3b3; } | |
65 .Literal { background: #eecc95 } | |
66 .Variable { background: #aaaae8; } | |
67 .BNode { background: orange; } | |
68 .say { | |
69 white-space: pre-wrap; | |
70 } | |
71 .say.now.past.end { | |
72 color: #b82c08; | |
73 } | |
74 .say.restarts { | |
75 color: #51600f; | |
76 } | |
77 .say.advance.start { | |
78 margin-top: 5px; | |
79 } | |
80 .say.finished { | |
81 border-bottom: 1px solid gray; | |
82 display: inline-block; | |
83 margin-bottom: 8px; | |
84 } | |
85 ''') | |
86 | |
87 | |
88 class StructuredLog: | |
89 workingSet: Graph | |
90 | |
91 def __init__(self, output: Path): | |
92 self.output = output | |
93 self.steps = [] | |
94 | |
95 def say(self, line): | |
96 classes = ['say'] + [c for c in re.split(r'\s+|\W|(\d+)', line) if c] | |
97 cssList = ' '.join(classes) | |
98 self.steps.append(div.attrs(('class', cssList))(line)) | |
99 | |
100 def startIteration(self, num: int): | |
101 self.steps.extend([hr(), div.attrs(('class', 'iteration'))(f"iteration {num}")]) | |
102 | |
103 def rule(self, workingSet: Graph, i: int, rule): | |
104 self.steps.append(htmlGraph('working set', self.workingSet)) | |
105 self.steps.append(f"try rule {i}") | |
106 self.steps.append(htmlRule(rule)) | |
107 | |
108 def foundBinding(self, bound): | |
109 self.steps.append(div('foundBinding', htmlBinding(bound.binding))) | |
110 | |
111 def looperConsider(self, looper, newBinding, fullBinding, isNew): | |
112 self.steps.append( | |
113 table.attrs(('class', f'consider isNew-{isNew}'))(tr( | |
114 td(htmlChunkLooper(looper, showBindings=False)), | |
115 td(div('newBinding', htmlBinding(newBinding))), | |
116 td(div('fullBinding', htmlBinding(fullBinding))), | |
117 td(f'{isNew=}'), | |
118 ))) | |
119 | |
120 def odometer(self, chunkStack): | |
121 self.steps.append( | |
122 table( | |
123 tr(*[ | |
124 td( | |
125 table.attrs(('class', 'looper'))( | |
126 tr( | |
127 td(htmlChunkLooper(looper, showBindings=False)), # | |
128 td(div('newBinding'), | |
129 htmlBinding(looper.localBinding()) if not looper.pastEnd() else '(pastEnd)'), # | |
130 td(div('fullBinding'), | |
131 htmlBinding(looper.currentBinding()) if not looper.pastEnd() else ''), # | |
132 ))) for looper in chunkStack | |
133 ]))) | |
134 | |
135 def render(self): | |
136 | |
137 with open(self.output, 'w') as out: | |
138 out.write(simple_html.render.render(html(head(style(CSS)), body(div(*self.steps))))) | |
139 | |
140 | |
141 def htmlRule(r): | |
142 return table(tr(td(htmlGraph('lhsGraph', r.lhsGraph)), td(htmlGraph('rhsGraph', r.rhsGraph)))) | |
143 | |
144 | |
145 def htmlGraph(label: str, g: Graph): | |
146 return div(label, table(*[htmlStmtRow(s) for s in sorted(g)])) | |
147 | |
148 | |
149 def htmlStmtRow(s: Triple): | |
150 return tr(td(htmlTerm(s[0])), td(htmlTerm(s[1])), td(htmlTerm(s[2]))) | |
151 | |
152 | |
153 def htmlTerm(t: Union[Node, List[Node]]): | |
154 if isinstance(t, list): | |
155 return span('( ', *[htmlTerm(x) for x in t], ')') | |
156 return span.attrs(('class', 'node ' + t.__class__.__name__))(repr(t)) | |
157 | |
158 | |
159 def htmlBinding(b: CandidateBinding): | |
160 return table(*[tr(td(htmlTerm(k)), td(htmlTerm(v))) for k, v in sorted(b.binding.items())]) | |
161 | |
162 | |
163 def htmlChunkLooper(looper, showBindings=True): | |
164 alignedMatches = [] | |
165 for i, arc in enumerate(looper._alignedMatches): | |
166 hi = arc.workingSetChunk == looper.currentSourceChunk | |
167 alignedMatches.append( | |
168 tr.attrs(('class', 'highlight' if hi else ''))( | |
169 td(span.attrs(('class', 'arrow'))('➢' if hi else ''), str(i)), # | |
170 td(htmlChunk(arc.workingSetChunk)))) | |
171 return table( | |
172 tr( | |
173 td(div(repr(looper)), div(f"prev = {looper.prev}")), | |
174 td( | |
175 div('lhsChunk'), | |
176 htmlChunk(looper.lhsChunk), # | |
177 div('alignedMatches'), | |
178 table.attrs(('class', 'alignedWorkingSetChunk'))(*alignedMatches) # | |
179 ), | |
180 td('localBinding', htmlBinding(looper.localBinding())) if showBindings else '', | |
181 td('currentBinding', htmlBinding(looper.currentBinding())) if showBindings else '', | |
182 )) | |
183 | |
184 | |
185 def htmlChunkedGraph(g: ChunkedGraph, highlightChunk: Optional[Chunk] = None): | |
186 return table( | |
187 tr(td('staticChunks'), td(*[div(htmlChunk(ch, ch == highlightChunk)) for ch in sorted(g.staticChunks)])), | |
188 tr(td('patternChunks'), td(*[div(htmlChunk(ch, ch == highlightChunk)) for ch in sorted(g.patternChunks)])), | |
189 tr(td('chunksUsedByFuncs'), td(*[div(htmlChunk(ch, ch == highlightChunk)) for ch in sorted(g.chunksUsedByFuncs)])), | |
190 ) | |
191 | |
192 | |
193 def htmlChunk(ch: Chunk, highlight=False): | |
194 return span.attrs(('class', 'highlight' if highlight else ''))( | |
195 'subj=', | |
196 htmlTerm(ch.primary[0] if ch.primary[0] is not None else cast(List[Node], ch.subjList)), # | |
197 ' pred=', | |
198 htmlTerm(ch.predicate), # | |
199 ' obj=', | |
200 htmlTerm(ch.primary[2] if ch.primary[2] is not None else cast(List[Node], ch.objList))) |