jupyter-duckdb 1.2.101__tar.gz → 1.2.102__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/PKG-INFO +1 -1
  2. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/setup.py +4 -2
  3. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/kernel.py +72 -54
  4. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/visualization/Plotly.py +8 -18
  5. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/visualization/RATreeDrawer.py +34 -2
  6. jupyter_duckdb-1.2.102/src/duckdb_kernel/visualization/lib/__init__.py +53 -0
  7. jupyter_duckdb-1.2.102/src/duckdb_kernel/visualization/lib/plotly-3.0.1.min.js +3879 -0
  8. jupyter_duckdb-1.2.102/src/duckdb_kernel/visualization/lib/ra.css +3 -0
  9. jupyter_duckdb-1.2.102/src/duckdb_kernel/visualization/lib/ra.js +55 -0
  10. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/jupyter_duckdb.egg-info/PKG-INFO +1 -1
  11. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/jupyter_duckdb.egg-info/SOURCES.txt +4 -0
  12. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/README.md +0 -0
  13. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/setup.cfg +0 -0
  14. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/__init__.py +0 -0
  15. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/__main__.py +0 -0
  16. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/db/Column.py +0 -0
  17. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/db/Connection.py +0 -0
  18. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/db/Constraint.py +0 -0
  19. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/db/DatabaseError.py +0 -0
  20. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/db/ForeignKey.py +0 -0
  21. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/db/Table.py +0 -0
  22. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/db/__init__.py +0 -0
  23. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/db/error/EmptyResultError.py +0 -0
  24. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/db/error/__init__.py +0 -0
  25. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/db/implementation/duckdb/Connection.py +0 -0
  26. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/db/implementation/duckdb/__init__.py +0 -0
  27. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/db/implementation/postgres/Connection.py +0 -0
  28. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/db/implementation/postgres/__init__.py +0 -0
  29. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/db/implementation/postgres/util.py +0 -0
  30. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/db/implementation/sqlite/Connection.py +0 -0
  31. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/db/implementation/sqlite/__init__.py +0 -0
  32. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/kernel.json +0 -0
  33. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/magics/MagicCommand.py +0 -0
  34. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/magics/MagicCommandCallback.py +0 -0
  35. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/magics/MagicCommandException.py +0 -0
  36. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/magics/MagicCommandHandler.py +0 -0
  37. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/magics/StringWrapper.py +0 -0
  38. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/magics/__init__.py +0 -0
  39. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/DCParser.py +0 -0
  40. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/LogicParser.py +0 -0
  41. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/ParserError.py +0 -0
  42. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/RAParser.py +0 -0
  43. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/__init__.py +0 -0
  44. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/DCOperand.py +0 -0
  45. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/LogicElement.py +0 -0
  46. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/LogicOperand.py +0 -0
  47. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/LogicOperator.py +0 -0
  48. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/RABinaryOperator.py +0 -0
  49. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/RAElement.py +0 -0
  50. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/RAOperand.py +0 -0
  51. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/RAOperator.py +0 -0
  52. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/RAUnaryOperator.py +0 -0
  53. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/__init__.py +0 -0
  54. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/Add.py +0 -0
  55. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/And.py +0 -0
  56. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/ArrowLeft.py +0 -0
  57. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/ConditionalSet.py +0 -0
  58. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/Cross.py +0 -0
  59. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/Difference.py +0 -0
  60. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/Divide.py +0 -0
  61. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/Division.py +0 -0
  62. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/Equal.py +0 -0
  63. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/FullOuterJoin.py +0 -0
  64. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/GreaterThan.py +0 -0
  65. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/GreaterThanEqual.py +0 -0
  66. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/Intersection.py +0 -0
  67. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/Join.py +0 -0
  68. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/LeftOuterJoin.py +0 -0
  69. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/LessThan.py +0 -0
  70. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/LessThanEqual.py +0 -0
  71. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/Minus.py +0 -0
  72. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/Multiply.py +0 -0
  73. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/Or.py +0 -0
  74. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/RightOuterJoin.py +0 -0
  75. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/Unequal.py +0 -0
  76. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/Union.py +0 -0
  77. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/binary/__init__.py +0 -0
  78. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/unary/Not.py +0 -0
  79. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/unary/Projection.py +0 -0
  80. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/unary/Rename.py +0 -0
  81. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/unary/Selection.py +0 -0
  82. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/elements/unary/__init__.py +0 -0
  83. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/tokenizer/Token.py +0 -0
  84. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/tokenizer/Tokenizer.py +0 -0
  85. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/tokenizer/__init__.py +0 -0
  86. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/util/RenamableColumn.py +0 -0
  87. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/util/RenamableColumnList.py +0 -0
  88. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/parser/util/__init__.py +0 -0
  89. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/tests/__init__.py +0 -0
  90. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/tests/test_dc.py +0 -0
  91. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/tests/test_ra.py +0 -0
  92. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/tests/test_result_comparison.py +0 -0
  93. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/tests/test_sql.py +0 -0
  94. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/util/ResultSetComparator.py +0 -0
  95. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/util/SQL.py +0 -0
  96. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/util/TestError.py +0 -0
  97. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/util/__init__.py +0 -0
  98. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/util/formatting.py +0 -0
  99. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/visualization/Drawer.py +0 -0
  100. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/visualization/SchemaDrawer.py +0 -0
  101. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/duckdb_kernel/visualization/__init__.py +0 -0
  102. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/jupyter_duckdb.egg-info/dependency_links.txt +0 -0
  103. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/jupyter_duckdb.egg-info/requires.txt +0 -0
  104. {jupyter_duckdb-1.2.101 → jupyter_duckdb-1.2.102}/src/jupyter_duckdb.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: jupyter-duckdb
3
- Version: 1.2.101
3
+ Version: 1.2.102
4
4
  Summary: a basic wrapper kernel for DuckDB
5
5
  Home-page: https://github.com/erictroebs/jupyter-duckdb
6
6
  Author: Eric Tröbs
@@ -1,7 +1,7 @@
1
1
  import os
2
2
 
3
3
  # configuration
4
- PACKAGE_VERSION = '1.2.101'
4
+ PACKAGE_VERSION = '1.2.102'
5
5
  DUCKDB_VERSION = '1.2.1'
6
6
 
7
7
  DEPENDENCIES = [
@@ -46,7 +46,9 @@ if __name__ == '__main__':
46
46
  include_package_data=True,
47
47
  package_data={
48
48
  'duckdb_kernel': [
49
- 'kernel.json'
49
+ 'kernel.json',
50
+ 'visualization/lib/*.css',
51
+ 'visualization/lib/*.js',
50
52
  ]
51
53
  }
52
54
  )
@@ -10,6 +10,7 @@ from typing import Optional, Dict, List, Tuple
10
10
  from ipykernel.kernelbase import Kernel
11
11
 
12
12
  from .db import Connection, DatabaseError, Table
13
+ from .visualization.lib import *
13
14
  from .db.error import *
14
15
  from .magics import *
15
16
  from .parser import RAParser, DCParser, ParserError
@@ -118,6 +119,7 @@ class DuckDBKernel(Kernel):
118
119
  self._db = Postgres(host, port, username, password, database_name)
119
120
  except ImportError:
120
121
  self.print('psycopg could not be found', name='stderr')
122
+ return False
121
123
 
122
124
  # Otherwise the provided path is used to create an
123
125
  # in-process instance.
@@ -147,7 +149,7 @@ class DuckDBKernel(Kernel):
147
149
  else:
148
150
  return False
149
151
 
150
- def _execute_stmt(self, query: str, silent: bool,
152
+ def _execute_stmt(self, name: str, query: str, silent: bool,
151
153
  column_name_mapping: Dict[str, str],
152
154
  max_rows: Optional[int]) -> Tuple[Optional[List[str]], Optional[List[List]]]:
153
155
  if self._db is None:
@@ -163,52 +165,57 @@ class DuckDBKernel(Kernel):
163
165
 
164
166
  et = time.time()
165
167
 
166
- # return result if silent
167
- if silent:
168
- return columns, rows
168
+ # print result if not silent
169
+ if not silent:
170
+ # print EXPLAIN queries as raw text if using DuckDB
171
+ if query.strip().startswith('EXPLAIN') and self._db.plain_explain():
172
+ for ekey, evalue in rows:
173
+ html = f'<b>{ekey}</b><br><pre>{evalue}</pre>'
174
+ break
169
175
 
170
- # print EXPLAIN queries as raw text if using DuckDB
171
- if query.strip().startswith('EXPLAIN') and self._db.plain_explain():
172
- for ekey, evalue in rows:
173
- self.print_data(f'<b>{ekey}</b><br><pre>{evalue}</pre>')
176
+ return None, None
174
177
 
175
- return None, None
178
+ # print every other query as a table
179
+ else:
180
+ if columns is not None:
181
+ # table header
182
+ mapped_columns = (column_name_mapping.get(c, c) for c in columns)
183
+ table_header = ''.join(f'<th>{c}</th>' for c in mapped_columns)
184
+
185
+ # table data
186
+ if max_rows is not None and len(rows) > max_rows:
187
+ table_data = f'''
188
+ {rows_table(rows[:math.ceil(max_rows / 2)])}
189
+ <tr>
190
+ <td colspan="{len(columns)}"
191
+ style="text-align: center"
192
+ title="{row_count(len(rows) - max_rows)} omitted">
193
+ ...
194
+ </td>
195
+ </tr>
196
+ {rows_table(rows[-math.floor(max_rows // 2):])}
197
+ '''
198
+ else:
199
+ table_data = rows_table(rows)
200
+
201
+ # send to client
202
+ html = (f'''
203
+ <table class="duckdb-query-result-table">
204
+ {table_header}
205
+ {table_data}
206
+ </table>
207
+
208
+ {row_count(len(rows))} in {et - st:.3f}s
209
+ ''')
176
210
 
177
- # print every other query as a table
178
- else:
179
- if columns is not None:
180
- # table header
181
- mapped_columns = (column_name_mapping.get(c, c) for c in columns)
182
- table_header = ''.join(f'<th>{c}</th>' for c in mapped_columns)
183
-
184
- # table data
185
- if max_rows is not None and len(rows) > max_rows:
186
- table_data = f'''
187
- {rows_table(rows[:math.ceil(max_rows / 2)])}
188
- <tr>
189
- <td colspan="{len(columns)}"
190
- style="text-align: center"
191
- title="{row_count(len(rows) - max_rows)} omitted">
192
- ...
193
- </td>
194
- </tr>
195
- {rows_table(rows[-math.floor(max_rows // 2):])}
196
- '''
197
211
  else:
198
- table_data = rows_table(rows)
212
+ html = f'statement executed without result in {et - st:.3f}s'
199
213
 
200
- # send to client
201
- self.print_data(f'''
202
- <table class="duckdb-query-result">
203
- {table_header}
204
- {table_data}
205
- </table>
206
- ''')
207
-
208
- self.print_data(f'{row_count(len(rows))} in {et - st:.3f}s')
209
-
210
- else:
211
- self.print_data(f'statement executed without result in {et - st:.3f}s')
214
+ self.print_data(f'''
215
+ <div class="duckdb-query-result {name}">
216
+ {html}
217
+ </div>
218
+ ''')
212
219
 
213
220
  return columns, rows
214
221
 
@@ -498,16 +505,23 @@ class DuckDBKernel(Kernel):
498
505
  # create and show visualization
499
506
  if analyze:
500
507
  vd = RATreeDrawer(self._db, root_node, tables)
501
- svg = vd.to_svg(True)
502
-
503
- self.print_data(svg)
508
+ svg = vd.to_interactive_svg()
509
+ data = {
510
+ 'generated_code': {
511
+ node_id: node.to_sql_with_renamed_columns(tables)
512
+ for node_id, node in vd.nodes.items()
513
+ }
514
+ }
504
515
 
505
- # generate sql
506
- sql = root_node.to_sql_with_renamed_columns(tables)
516
+ else:
517
+ svg = ''
518
+ data = {
519
+ 'generated_code': root_node.to_sql_with_renamed_columns(tables)
520
+ }
507
521
 
508
- return {
509
- 'generated_code': sql
510
- }
522
+ # return data
523
+ self.print_data(svg)
524
+ return data
511
525
 
512
526
  def _all_ra_magic(self, silent: bool, value: str):
513
527
  if value.lower() in ('1', 'on', 'true'):
@@ -689,10 +703,14 @@ class DuckDBKernel(Kernel):
689
703
  execution_args['column_name_mapping'] = {}
690
704
 
691
705
  # execute statement if needed
692
- if clean_code.strip():
693
- cols, rows = self._execute_stmt(clean_code, silent, **execution_args)
694
- else:
695
- cols, rows = None, None
706
+ cols, rows = None, None
707
+
708
+ if not isinstance(clean_code, dict):
709
+ clean_code = {'default': clean_code}
710
+
711
+ for name, code in clean_code.items():
712
+ if code.strip():
713
+ cols, rows = self._execute_stmt(name, code, silent, **execution_args)
696
714
 
697
715
  # execute magic command here if it does depend on query results
698
716
  for callback in post_query_callbacks:
@@ -1,22 +1,9 @@
1
- from decimal import Decimal
2
-
3
- PLOTLY_VERSION = '3.0.1'
4
-
5
1
  import json
6
- from uuid import uuid4
2
+ from decimal import Decimal
7
3
  from typing import Dict, List, Optional
4
+ from uuid import uuid4
8
5
 
9
- __PLOTLY_INITIALIZED = False
10
-
11
-
12
- def __init() -> str:
13
- global __PLOTLY_INITIALIZED
14
-
15
- if not __PLOTLY_INITIALIZED:
16
- __PLOTLY_INITIALIZED = True
17
- return f'<script src="https://cdn.plot.ly/plotly-{PLOTLY_VERSION}.min.js"></script>'
18
- else:
19
- return ''
6
+ from .lib import init_plotly
20
7
 
21
8
 
22
9
  def __div_id() -> str:
@@ -60,7 +47,7 @@ def __fix_decimal(x: List):
60
47
 
61
48
 
62
49
  def draw_chart(title: Optional[str], traces: List[Dict] | Dict) -> str:
63
- init = __init()
50
+ init = init_plotly()
64
51
  div_id = __div_id()
65
52
  layout = __layout(title)
66
53
  config = __config()
@@ -69,7 +56,10 @@ def draw_chart(title: Optional[str], traces: List[Dict] | Dict) -> str:
69
56
  traces = json.dumps(traces)
70
57
 
71
58
  return f'''
72
- {init}
59
+ <script type="text/javascript">
60
+ {init}
61
+ </script>
62
+
73
63
  <div id="{div_id}"></div>
74
64
  <script type="text/javascript">
75
65
  Plotly.newPlot('{div_id}', {traces}, {json.dumps(layout)}, {json.dumps(config)});
@@ -1,12 +1,14 @@
1
- from typing import Dict
1
+ from typing import Dict, Optional
2
2
 
3
3
  from graphviz import Digraph
4
+ from uuid import uuid4
4
5
 
5
6
  from duckdb_kernel.db import Table
6
7
  from duckdb_kernel.parser.elements import RAElement
7
8
  from duckdb_kernel.util.formatting import row_count
8
9
  from .Drawer import Drawer
9
10
  from ..db import Connection
11
+ from .lib import *
10
12
 
11
13
 
12
14
  class RATreeDrawer(Drawer):
@@ -15,6 +17,9 @@ class RATreeDrawer(Drawer):
15
17
  self.root_node: RAElement = root_node
16
18
  self.tables: Dict[str, Table] = tables
17
19
 
20
+ self.nodes: Dict[str, RAElement] = {}
21
+ self.root_node_id: Optional[str] = None
22
+
18
23
  def to_graph(self) -> Digraph:
19
24
  # create graph
20
25
  ps = Digraph('Schema',
@@ -31,7 +36,11 @@ class RATreeDrawer(Drawer):
31
36
 
32
37
  def __add_node(self, ps: Digraph, node: RAElement) -> str:
33
38
  # use id of node object as identifier
34
- node_id = f'node_{id(node)}'
39
+ node_id = f'node_{str(uuid4()).replace("-", "_")}'
40
+
41
+ self.nodes[node_id] = node
42
+ if node == self.root_node:
43
+ self.root_node_id = node_id
35
44
 
36
45
  # generate child nodes
37
46
  child_ids = [self.__add_node(ps, child) for child in node.children]
@@ -69,3 +78,26 @@ class RATreeDrawer(Drawer):
69
78
 
70
79
  # return node identifier to generate edges
71
80
  return node_id
81
+
82
+ def to_interactive_svg(self) -> str:
83
+ div_id = f'div-{str(uuid4())}'
84
+
85
+ css = init_css()
86
+ ra = init_ra()
87
+ svg = self.to_svg(True)
88
+
89
+ return f'''
90
+ <style type="text/css">
91
+ {css}
92
+ </style>
93
+
94
+ <div id="{div_id}">
95
+ {svg}
96
+ </div>
97
+
98
+ <script type="text/javascript">
99
+ {ra}
100
+
101
+ animate_ra('{div_id}', '{self.root_node_id}')
102
+ </script>
103
+ '''
@@ -0,0 +1,53 @@
1
+ import os
2
+
3
+ __CSS_INITIALIZED = False
4
+ __RA_INITIALIZED = False
5
+ __PLOTLY_INITIALIZED = False
6
+
7
+ __location = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
8
+
9
+
10
+ def init_css() -> str:
11
+ global __CSS_INITIALIZED
12
+
13
+ if not __CSS_INITIALIZED:
14
+ with open(os.path.join(__location, 'ra.css')) as ra_file:
15
+ css = ra_file.read()
16
+ else:
17
+ css = ''
18
+
19
+ __CSS_INITIALIZED = True
20
+ return css
21
+
22
+
23
+ def init_ra() -> str:
24
+ global __RA_INITIALIZED
25
+
26
+ if not __RA_INITIALIZED:
27
+ with open(os.path.join(__location, 'ra.js')) as ra_file:
28
+ ra = ra_file.read()
29
+ else:
30
+ ra = ''
31
+
32
+ __RA_INITIALIZED = True
33
+ return ra
34
+
35
+
36
+ def init_plotly() -> str:
37
+ global __PLOTLY_INITIALIZED
38
+
39
+ if not __PLOTLY_INITIALIZED:
40
+ with open(os.path.join(__location, 'plotly-3.0.1.min.js')) as plotly_file:
41
+ plotly = plotly_file.read()
42
+ else:
43
+ plotly = ''
44
+
45
+ __PLOTLY_INITIALIZED = True
46
+ return plotly
47
+
48
+
49
+ __all__ = [
50
+ 'init_css',
51
+ 'init_ra',
52
+ 'init_plotly',
53
+ ]