_macros.html 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. {% macro show_tree(assets, current_asset_name) %}
  2. <div class="container">
  3. <div id="view"></div>
  4. </div>
  5. <script type="text/javascript">
  6. window.onload = function(){
  7. let assets = {{ assets | safe }};
  8. let currentAssetName = '{{ current_asset_name | safe}}';
  9. {# Vega Spec adapted from https://stackoverflow.com/a/76300309 #}
  10. treeSpecs = {
  11. "$schema": "https://vega.github.io/schema/vega/v5.json",
  12. "width": 1000,
  13. "height": 650,
  14. "padding": 0,
  15. "autosize": {"type": "fit", "resize": false},
  16. "signals": [
  17. {"name": "nodeWidth", "value": 190},
  18. {"name": "nodeHeight", "value": 40},
  19. {"name": "verticalNodeGap", "value": 10},
  20. {"name": "horizontalNodeGap", "value": 60},
  21. {"name": "currentAssetName", "value": currentAssetName},
  22. {
  23. "name": "scaledNodeHeight",
  24. "update": "abs(nodeHeight/ max(span(ydom),height))*height"
  25. },
  26. {"name": "scaledNodeWidth", "update": "(nodeWidth/ span(xdom))*width"},
  27. {"name": "xrange", "update": "[0, width]"},
  28. {"name": "yrange", "update": "[0, height]"},
  29. {"name": "scaledFont13", "update": "(30/ span(xdom))*width"},
  30. {"name": "scaledLimit", "update": "(30/ span(xdom))*width"},
  31. {
  32. "name": "xdom",
  33. "update": "slice(xext)",
  34. },
  35. {
  36. "name": "ydom",
  37. "update": "slice(yext)",
  38. },
  39. ],
  40. "data": [
  41. {
  42. "name": "tree",
  43. "values": assets,
  44. "transform": [
  45. {"type": "stratify", "key": "id", "parentKey": "parent"},
  46. {
  47. "type": "tree",
  48. "method": "tidy",
  49. "size": [{"signal": "nodeHeight *0.1"},
  50. {"signal": "width"}
  51. ],
  52. "separation": {"signal": "true"},
  53. "nodeSize" : [
  54. {"signal": "nodeHeight+verticalNodeGap"},
  55. {"signal": "nodeWidth+horizontalNodeGap"}
  56. ],
  57. "as": ["y", "x", "depth", "children"],
  58. },
  59. {"type": "extent", "field": "x", "signal": "xext"},
  60. {"type": "extent", "field": "y", "signal": "yext"}
  61. ]
  62. },
  63. {
  64. "name": "links",
  65. "source": "tree",
  66. "transform": [
  67. {"type": "treelinks", "signal": "upstream"},
  68. {
  69. "type": "linkpath",
  70. "orient": "horizontal",
  71. "shape": {"signal": "'diagonal'"},
  72. "sourceY": {"expr": "scale('yscale', datum.source.y)"},
  73. "sourceX": {"expr": "scale('xscale', datum.source.x)"},
  74. "targetY": {"expr": "scale('yscale', datum.target.y)"},
  75. "targetX": {"expr": "scale('xscale', datum.target.x)"}
  76. }
  77. ]
  78. }
  79. ],
  80. "scales": [
  81. {
  82. "name": "xscale",
  83. "zero": false,
  84. "domain": {"signal": "xdom"},
  85. "range": {"signal": "xrange"}
  86. },
  87. {
  88. "name": "yscale",
  89. "zero": false,
  90. "domain": {"signal": "ydom"},
  91. "range": {"signal": "yrange"}
  92. }
  93. ],
  94. "marks": [
  95. {
  96. "type": "path",
  97. "from": {"data": "links"},
  98. "encode": {
  99. "update": {"path": {"field": "path"}, "stroke": {"value": "#aaa"}}
  100. }
  101. },
  102. {
  103. "name": "node",
  104. "description": "The Parent Node",
  105. "type": "group",
  106. "clip": false,
  107. "from": {"data": "tree"},
  108. "encode": {
  109. "update": {
  110. "x": {"field": "x", "scale": "xscale"},
  111. "width": {
  112. "signal": "datum.name === currentAssetName ? scaledNodeWidth * 1.1 : scaledNodeWidth"
  113. },
  114. "yc": {"field": "y", "scale": "yscale"},
  115. "height": {
  116. "signal": "datum.name === currentAssetName ? scaledNodeHeight * 1.1 : scaledNodeHeight"
  117. },
  118. "fill": {
  119. "signal": "datum.name === currentAssetName ? 'gold' : datum.name === 'Add Child Asset' ? 'green' : 'lightblue'"
  120. },
  121. "stroke": {
  122. "signal": "datum.name === currentAssetName ? 'darkorange' : 'black'"
  123. },
  124. "strokeWidth": {
  125. "signal": "datum.name === currentAssetName ? 4 : 1"
  126. },
  127. "cornerRadius": {"value": 5},
  128. "href": {"signal": "datum.link"},
  129. "tooltip": {"signal": "datum.tooltip"}
  130. }
  131. },
  132. "marks": [
  133. {
  134. "type": "text",
  135. "interactive": false,
  136. "name": "name",
  137. "encode": {
  138. "update": {
  139. "x": {"signal": "(5/ span(xdom))*width + (scaledNodeWidth * 0.15)"},
  140. "y": {"signal": "(5/ span(xdom))*width + (scaledNodeHeight * 0.15)"},
  141. "fontWeight": {"value": "bold"},
  142. "baseline": {"value": "top"},
  143. "text": {"signal": "parent.name"},
  144. "fontSize": {
  145. "signal": "datum.name === currentAssetName ? scaledFont13 * 1.5 : scaledFont13"
  146. },
  147. "fill": {
  148. "signal": "parent.name === currentAssetName ? 'black' : parent.name === 'Add Child Asset' ? 'white' : 'darkblue'"
  149. },
  150. "limit": {"signal": "scaledNodeWidth-scaledLimit"},
  151. "font": {"value": "Calibri"},
  152. "href": {"signal": "datum.link"},
  153. "tooltip": {"signal": "parent.tooltip"}
  154. }
  155. }
  156. },
  157. {
  158. "type": "image",
  159. "encode": {
  160. "update": {
  161. "x": {"signal": "-(scaledNodeWidth * 0.001)"},
  162. "y": {"signal": "-(scaledNodeHeight * 0.001)"},
  163. "width": {"signal": "scaledNodeWidth * 0.002"},
  164. "height": {"signal": "scaledNodeHeight * 0.002"},
  165. "url": {"signal": "parent.icon"}
  166. }
  167. }
  168. },
  169. ]
  170. }
  171. ]
  172. }
  173. vegaEmbed("#view", treeSpecs, { renderer: "svg" })
  174. }
  175. </script>
  176. {% endmacro %}
  177. {% macro render_attributes(attributes) %}
  178. <table class="table table-striped table-responsive">
  179. <thead>
  180. <tr>
  181. <th scope="col">Attribute</th>
  182. <th scope="col">Value</th>
  183. </tr>
  184. </thead>
  185. <tbody>
  186. {% for key, value in attributes.items() %}
  187. {% if not key.endswith("_id") %}
  188. <tr>
  189. <td scope="row">{{ key }}</td>
  190. <td scope="row">{{ value }}</td>
  191. </tr>
  192. {% endif %}
  193. {% endfor %}
  194. </tbody>
  195. </table>
  196. {% endmacro %}