Format.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. import math
  2. from datetime import date
  3. def joinit(iterable, delimiter):
  4. it = iter(iterable)
  5. yield next(it)
  6. for x in it:
  7. yield delimiter
  8. yield x
  9. # To format, in HTML, the cores in the right order.
  10. # First we order tje categories
  11. # Then we order the cores in each category
  12. # The final ORDEREDCORES is what is used
  13. # to order tjhe values
  14. # Since some cores may be missing, each atble display
  15. # is computing a rstricted ordered core list with only the available cores.
  16. CORTEXCATEGORIES=["Cortex-M","Cortex-R","Cortex-A"]
  17. CORECATEGORIES={"Cortex-M":["m0","m4", "m7", "m33" , "m55 scalar", "m55 mve"],
  18. "Cortex-R":["r8","r52"],
  19. "Cortex-A":["a32"]
  20. }
  21. ORDEREDCORES=[]
  22. for cat in CORTEXCATEGORIES:
  23. cores=[]
  24. if cat in CORECATEGORIES:
  25. for core in CORECATEGORIES[cat]:
  26. cores.append(core)
  27. else:
  28. print("Error core %s not found" % cat)
  29. quit()
  30. ORDEREDCORES += cores
  31. class Markdown:
  32. def __init__(self,output):
  33. self._id=0
  34. self._output = output
  35. def visitBarChart(self,data):
  36. pass
  37. def visitHistory(self,data):
  38. pass
  39. def visitText(self,text):
  40. self._output.write(text)
  41. # Write columns in markdown format
  42. def writeColumns(self,cols):
  43. colStr = "".join(joinit(cols,"|"))
  44. self._output.write("|")
  45. self._output.write(colStr)
  46. self._output.write("|\n")
  47. sepStr="".join(joinit([":-:" for x in cols],"|"))
  48. self._output.write("|")
  49. self._output.write(sepStr)
  50. self._output.write("|\n")
  51. # Write row in markdown format
  52. def writeRow(self,row):
  53. row=[str(x) for x in row]
  54. rowStr = "".join(joinit(row,"|"))
  55. self._output.write("|")
  56. self._output.write(rowStr)
  57. self._output.write("|\n")
  58. def visitTable(self,table):
  59. self.writeColumns(table.columns)
  60. for row in table.rows:
  61. self.writeRow(row)
  62. def visitSection(self,section):
  63. self._id = self._id + 1
  64. header = "".join(["#" for i in range(self._id)])
  65. self._output.write("%s %s\n" % (header,section.name))
  66. def leaveSection(self,section):
  67. self._id = self._id - 1
  68. def visitDocument(self,document):
  69. self._output.write("Document generated frun run ids : %s\n" % document.runidHeader)
  70. def leaveDocument(self,document):
  71. pass
  72. styleSheet="""
  73. <style type='text/css'>
  74. #TOC {
  75. position: fixed;
  76. left: 0;
  77. top: 0;
  78. width: 290px;
  79. height: 100%;
  80. overflow:auto;
  81. margin-top:5px;
  82. margin-bottom:10px;
  83. }
  84. html {
  85. font-size: 16px;
  86. }
  87. html, body {
  88. background-color: #f3f2ee;
  89. font-family: "PT Serif", 'Times New Roman', Times, serif;
  90. color: #1f0909;
  91. line-height: 1.5em;
  92. }
  93. body {
  94. margin: auto;
  95. margin-top:0px;
  96. margin-left:290px;
  97. }
  98. h1,
  99. h2,
  100. h3,
  101. h4,
  102. h5,
  103. h6 {
  104. font-weight: bold;
  105. }
  106. h1 {
  107. font-size: 1.875em;
  108. margin-top:5px;
  109. }
  110. h2 {
  111. font-size: 1.3125em;
  112. }
  113. h3 {
  114. font-size: 1.3125em;
  115. margin-left:1em;
  116. }
  117. h4 {
  118. font-size: 1.125em;
  119. margin-left:1em;
  120. }
  121. h5,
  122. h6 {
  123. font-size: 1em;
  124. margin-left:1em;
  125. }
  126. #TOC h1 {
  127. margin-top:0em;
  128. margin-left:0.5em;
  129. }
  130. table {
  131. margin-bottom: 1.5em;
  132. font-size: 1em;
  133. width: 100%;
  134. border-collapse: collapse;
  135. border-spacing: 0;
  136. width: 100%;
  137. margin-left:1em;
  138. }
  139. thead th,
  140. tfoot th {
  141. padding: .25em .25em .25em .4em;
  142. text-transform: uppercase;
  143. }
  144. th {
  145. text-align: left;
  146. }
  147. td {
  148. vertical-align: top;
  149. padding: .25em .25em .25em .4em;
  150. }
  151. .ty-table-edit {
  152. background-color: transparent;
  153. }
  154. thead {
  155. background-color: #dadada;
  156. }
  157. tr:nth-child(even) {
  158. background: #e8e7e7;
  159. }
  160. ul, #myUL {
  161. list-style-type: none;
  162. padding-inline-start:10px;
  163. }
  164. /* Remove margins and padding from the parent ul */
  165. #myUL {
  166. margin: 0;
  167. padding: 0;
  168. }
  169. /* Style the caret/arrow */
  170. .caret {
  171. cursor: pointer;
  172. user-select: none; /* Prevent text selection */
  173. }
  174. /* Create the caret/arrow with a unicode, and style it */
  175. .caret::before {
  176. content: "\\25B6";
  177. color: black;
  178. display: inline-block;
  179. margin-right: 6px;
  180. }
  181. /* Rotate the caret/arrow icon when clicked on (using JavaScript) */
  182. .caret-down::before {
  183. transform: rotate(90deg);
  184. }
  185. /* Hide the nested list */
  186. .nested {
  187. display: none;
  188. }
  189. /* Show the nested list when the user clicks on the caret/arrow (with JavaScript) */
  190. .active {
  191. display: block;
  192. }
  193. .firstcore {
  194. border-left-color: black;
  195. border-left-style: solid;
  196. border-left-width: 1px;
  197. }
  198. </style>
  199. """
  200. script="""<script type="text/javascript">
  201. var toggler = document.getElementsByClassName("caret");
  202. var i;
  203. for (i = 0; i < toggler.length; i++) {
  204. toggler[i].addEventListener("click", function() {
  205. this.parentElement.querySelector(".nested").classList.toggle("active");
  206. this.classList.toggle("caret-down");
  207. });
  208. }</script>"""
  209. barscript=""" <script src="https://d3js.org/d3.v5.js"></script>
  210. <script type="text/javascript">
  211. histwidth=400;
  212. histheight=200;
  213. histmargin={left:40,right:100,bottom:40,top:10};
  214. function legend(color,svg)
  215. {
  216. const g = svg
  217. .attr("transform", `translate(${histwidth},0)`)
  218. .attr("text-anchor", "end")
  219. .attr("font-family", "sans-serif")
  220. .attr("font-size", 9)
  221. .selectAll("g")
  222. .data(color.domain().slice().reverse())
  223. .join("g")
  224. .attr("transform", (d, i) => `translate(0,${i * 20})`);
  225. g.append("rect")
  226. .attr("x", -19)
  227. .attr("width", 19)
  228. .attr("height", 19)
  229. .attr("fill", color);
  230. g.append("text")
  231. .attr("x", -24)
  232. .attr("y", 9.5)
  233. .attr("dy", "0.35em")
  234. .text(d => d);
  235. }
  236. function myhist(data,theid)
  237. {
  238. var x,y,xAxis,yAxis,svg,color;
  239. color = d3.scaleOrdinal()
  240. .domain(data.series.map(d => d['name']))
  241. .range(["#FF6B00",
  242. "#FFC700",
  243. "#95D600",
  244. "#00C1DE",
  245. "#0091BD",
  246. "#002B49",
  247. "#333E48",
  248. "#7D868C",
  249. "#E5ECEB"]);
  250. svg = d3.select(theid).insert("svg")
  251. .attr("viewBox", [0, 0, histwidth, histheight]);
  252. sx = d3.scaleLinear()
  253. .domain(d3.extent(data.dates))
  254. .range([histmargin.left,histwidth - histmargin.right]);
  255. sy = d3.scaleLinear()
  256. .domain([0, d3.max(data.series, d => d3.max(d.values,q => q[1]))]).nice()
  257. .range([histheight - histmargin.bottom, histmargin.top]);
  258. xAxis = g => g
  259. .attr("transform", `translate(0,${histheight - histmargin.bottom})`)
  260. .call(d3.axisBottom(sx).tickValues(data.dates).ticks(histwidth / 80,"d").
  261. tickSizeOuter(0));
  262. svg.append("text")
  263. .attr("class", "x label")
  264. .attr("text-anchor", "end")
  265. .attr("x", histwidth/2.0)
  266. .attr("y", histheight - 6)
  267. .text("RUN ID");
  268. yAxis = g => g
  269. .attr("transform", `translate(${histmargin.left},0)`)
  270. .call(d3.axisLeft(sy))
  271. .call(g => g.select(".domain").remove());
  272. line = d3.line()
  273. .x(d => sx(data.dates[d[0]])
  274. )
  275. .y(d => sy(d[1]));
  276. svg.append("g")
  277. .call(xAxis);
  278. svg.append("g")
  279. .call(yAxis);
  280. const path = svg.append("g")
  281. .attr("fill", "none")
  282. .attr("stroke", "steelblue")
  283. .attr("stroke-width", 1.5)
  284. .attr("stroke-linejoin", "round")
  285. .attr("stroke-linecap", "round")
  286. .selectAll("path")
  287. .data(data.series)
  288. .join("path")
  289. .style("mix-blend-mode", "multiply")
  290. .attr("stroke", d => color(d.name))
  291. .attr("d", d => line(d.values));
  292. // Legend
  293. svg.append("g")
  294. .call(d => legend(color,d));
  295. //svg.call(hover, path);
  296. }
  297. function mybar(data,theid)
  298. {
  299. var width,height,margin,x,y,xAxis,yAxis,svg,color;
  300. width=400;
  301. height=100;
  302. margin={left:40,right:10,bottom:40,top:10};
  303. svg = d3.select(theid).insert("svg")
  304. .attr("viewBox", [0, 0, width, height]);;
  305. x = d3.scaleBand()
  306. .domain(d3.range(data.length))
  307. .range([margin.left, width - margin.right])
  308. .padding(0.1);
  309. y = d3.scaleLinear()
  310. .domain([0, d3.max(data, d => d.value)]).nice()
  311. .range([height - margin.bottom, margin.top]);
  312. xAxis = g => g
  313. .attr("transform", `translate(0,${height - margin.bottom})`)
  314. .call(d3.axisBottom(x).tickFormat(i => data[i].name).tickSizeOuter(0));
  315. yAxis = g => g
  316. .attr("transform", `translate(${margin.left},0)`)
  317. .call(d3.axisLeft(y).ticks(4, data.format))
  318. .call(g => g.select(".domain").remove())
  319. .call(g => g.append("text")
  320. .attr("x", -margin.left)
  321. .attr("y", 10)
  322. .attr("fill", "currentColor")
  323. .attr("text-anchor", "start")
  324. .text(data.y));
  325. color = "steelblue"
  326. svg.append("g")
  327. .attr("fill", color)
  328. .selectAll("rect")
  329. .data(data)
  330. .join("rect")
  331. .attr("x", (d, i) => x(i))
  332. .attr("y", d => y(d.value))
  333. .attr("height", d => y(0) - y(d.value))
  334. .attr("width", x.bandwidth());
  335. svg.append("g")
  336. .call(xAxis);
  337. svg.append("g")
  338. .call(yAxis);
  339. }
  340. </script>"""
  341. class HTMLToc:
  342. def __init__(self,output):
  343. self._id=0
  344. self._sectionID = 0
  345. self._output = output
  346. def visitTable(self,table):
  347. pass
  348. def visitBarChart(self,data):
  349. pass
  350. def visitHistory(self,data):
  351. pass
  352. def visitText(self,text):
  353. pass
  354. def visitSection(self,section):
  355. self._id = self._id + 1
  356. self._sectionID = self._sectionID + 1
  357. if section.hasChildren:
  358. self._output.write("<li><span class=\"caret\"><a href=\"#section%d\">%s</a></span>\n" % (self._sectionID,section.name))
  359. self._output.write("<ul class=\"nested\">\n")
  360. else:
  361. self._output.write("<li><span><a href=\"#section%d\">%s</a></span>\n" % (self._sectionID,section.name))
  362. def leaveSection(self,section):
  363. if section.hasChildren:
  364. self._output.write("</ul></li>\n")
  365. self._id = self._id - 1
  366. def visitDocument(self,document):
  367. self._output.write("<div id=\"TOC\"><h1>Table of content</h1><ul id=\"myUL\">\n")
  368. def leaveDocument(self,document):
  369. self._output.write("</ul></div>%s\n" % script)
  370. def permutation(ordered,unordered):
  371. result=[]
  372. restricted=[]
  373. for c in ORDEREDCORES:
  374. if c in unordered:
  375. restricted.append(c)
  376. for c in unordered:
  377. result.append(restricted.index(c))
  378. return(result,restricted)
  379. def reorder(p,v):
  380. result=[0 for x in v]
  381. for val,i in zip(v,p):
  382. result[i]=val
  383. return(result)
  384. class HTML:
  385. def __init__(self,output,regMode):
  386. self._id=0
  387. self._sectionID = 0
  388. self._barID = 0
  389. self._histID = 0
  390. self._output = output
  391. self._regMode = regMode
  392. def visitBarChart(self,bar):
  393. data=bar.data
  394. datastr = "".join(joinit(["{name:'%s',value:%s}" % x for x in data],","))
  395. #print(datastr)
  396. self._output.write("<p id=\"g%d\"></p>\n" % self._barID)
  397. self._output.write("""<script type="text/javascript">
  398. thedata%d=[%s];
  399. mybar(thedata%d,"#g%d");
  400. </script>""" % (self._barID,datastr,self._barID,self._barID))
  401. self._barID = self._barID + 1
  402. def _getIndex(self,runids,data):
  403. return([[runids.index(x[0]),x[1]] for x in data])
  404. def visitHistory(self,hist):
  405. data=hist.data
  406. runidstr = "".join(joinit([str(x) for x in hist.runids],","))
  407. serieelems=[]
  408. for core in data:
  409. serieelems.append("{name: '%s',values: %s}" % (core,self._getIndex(hist.runids,data[core])))
  410. seriestr = "".join(joinit(serieelems,","))
  411. datastr="""{
  412. series: [%s],
  413. dates: [%s]
  414. };""" %(seriestr,runidstr);
  415. #print(datastr)
  416. self._output.write("<p id=\"hi%d\"></p>\n" % self._histID)
  417. self._output.write("""<script type="text/javascript">
  418. thehdata%d=%s
  419. myhist(thehdata%d,"#hi%d");
  420. </script>""" % (self._histID,datastr,self._histID,self._histID))
  421. self._histID = self._histID + 1
  422. def visitText(self,text):
  423. self._output.write("<p>\n")
  424. self._output.write(text.text)
  425. self._output.write("</p>\n")
  426. def visitTable(self,table):
  427. self._output.write("<table>\n")
  428. self._output.write("<thead>\n")
  429. self._output.write("<tr>\n")
  430. firstCore = False
  431. for col in table.params:
  432. firstCore = True
  433. self._output.write("<th class=\"param\">")
  434. self._output.write(str(col))
  435. self._output.write("</th>\n")
  436. perm,restricted=permutation(ORDEREDCORES,table.cores)
  437. for col in restricted:
  438. if firstCore:
  439. self._output.write("<th class=\"firstcore\">")
  440. else:
  441. self._output.write("<th class=\"core\">")
  442. self._output.write(str(col))
  443. self._output.write("</th>\n")
  444. firstCore = False
  445. self._output.write("</tr>\n")
  446. self._output.write("</thead>\n")
  447. nbParams = len(table.params)
  448. for row in table.rows:
  449. self._output.write("<tr>\n")
  450. i = 0
  451. row=list(row)
  452. #print(row)
  453. params=row[0:nbParams]
  454. values=row[nbParams:]
  455. row = params + reorder(perm,values)
  456. for elem in row:
  457. if i < nbParams:
  458. self._output.write("<td class=\"param\">")
  459. self._output.write(str(elem))
  460. self._output.write("</td>\n")
  461. elif i == nbParams and nbParams != 0:
  462. self._output.write("<td class=\"firstcore\">")
  463. self._output.write(str(elem))
  464. self._output.write("</td>\n")
  465. else:
  466. self._output.write("<td class=\"core\">")
  467. self._output.write(str(elem))
  468. self._output.write("</td>\n")
  469. i = i + 1
  470. self._output.write("</tr>\n")
  471. self._output.write("</table>\n")
  472. def visitSection(self,section):
  473. self._id = self._id + 1
  474. self._sectionID = self._sectionID + 1
  475. self._output.write("<h%d id=\"section%d\">%s</h%d>\n" % (self._id,self._sectionID,section.name,self._id))
  476. def leaveSection(self,section):
  477. self._id = self._id - 1
  478. def visitDocument(self,document):
  479. self._output.write("""<!doctype html>
  480. <html>
  481. <head>
  482. <meta charset='UTF-8'><meta name='viewport' content='width=device-width initial-scale=1'>
  483. <title>Benchmarks</title>%s</head><body>\n""" % styleSheet)
  484. if self._regMode:
  485. self._output.write("<h1>ECPS Benchmark Regressions</h1>\n")
  486. else:
  487. self._output.write("<h1>ECPS Benchmark Summary</h1>\n")
  488. self._output.write("<p>Document generated for run ids : %s</p>\n" % document.runidHeader)
  489. today = date.today()
  490. d2 = today.strftime("%B %d, %Y")
  491. self._output.write("<p>Document generated on %s</p>\n" % d2)
  492. self._output.write(barscript)
  493. def leaveDocument(self,document):
  494. document.accept(HTMLToc(self._output))
  495. self._output.write("</body></html>\n")