diff --git a/ILegend-JupyterMultiKernel/Demo.ipynb b/ILegend-JupyterMultiKernel/Demo.ipynb
new file mode 100644
index 000000000..7b2f6589e
--- /dev/null
+++ b/ILegend-JupyterMultiKernel/Demo.ipynb
@@ -0,0 +1,317 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "5469a760-15dd-4de3-9142-6ed6a8e2ba7a",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
Total Time Taken - 5.84s\n",
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Loaded into table: 'test3'. RowCount: 100"
+ ]
+ }
+ ],
+ "source": [
+ "load ~/Documents/CSV/startups.csv local::DuckDuckConnection"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "f0495a36-3743-456f-a108-b8258deb69e0",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "Total Time Taken - 3.59s\n",
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "Database: local::DB
\n",
+ "
Table: sample__sport
\n",
+ "
\n",
+ "| Column Name | Type |
\n",
+ "| Athlete | VARCHAR(0) |
\n",
+ "| Age | BIGINT |
\n",
+ "| Country | VARCHAR(0) |
\n",
+ "| Year | BIGINT |
\n",
+ "| Date | DATE |
\n",
+ "| Sport | VARCHAR(0) |
\n",
+ "| Gold | BIGINT |
\n",
+ "| Silver | BIGINT |
\n",
+ "| Bronze | BIGINT |
\n",
+ "
\n",
+ "
Table: sample__tree
\n",
+ "
\n",
+ "| Column Name | Type |
\n",
+ "| city | VARCHAR(0) |
\n",
+ "| country | VARCHAR(0) |
\n",
+ "| year | BIGINT |
\n",
+ "| tree | BIGINT |
\n",
+ "
\n",
+ "
Table: test3
\n",
+ "
\n",
+ "| Column Name | Type |
\n",
+ "| Company | VARCHAR(0) |
\n",
+ "| Sector | VARCHAR(0) |
\n",
+ "| Stage | VARCHAR(0) |
\n",
+ "| HeadQuarter | VARCHAR(0) |
\n",
+ "| Description | VARCHAR(0) |
\n",
+ "| "TurnOver($)" | DOUBLE |
\n",
+ "| Year | BIGINT |
\n",
+ "
\n",
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "db local::DuckDuckConnection"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "388a9dcb-02c2-4088-96f2-5706d1704722",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "Total Time Taken - 2.52s\n",
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ " \n",
+ " "
+ ],
+ "text/plain": [
+ " Company Sector ... TurnOver($) Year\n",
+ "0 Eunimart E-Commerce ... 3.380110e+07 2018\n",
+ "1 Hyperdata.IO Marketing ... 4.555556e+06 2018\n",
+ "2 Recordent FinTech ... 6.972143e+08 2020\n",
+ "3 Super Surfaces Design ... 5.000000e+05 2020\n",
+ "4 Freyr Energy Renewable Energy ... 2.000000e+06 2021\n",
+ "5 Eunimart E-Commerce ... 7.000000e+05 2021\n",
+ "6 Sreyas Holistic Remedies Med Tech ... 1.000000e+06 2020\n",
+ "7 Codingal EdTech ... 5.600000e+05 2020\n",
+ "8 Vistaar Finance Financial Services ... 3.000000e+07 2020\n",
+ "9 Quintype Content Marketing ... 3.500000e+06 2020\n",
+ "10 Cityflo Travel/Transport ... 7.000000e+06 2020\n",
+ "\n",
+ "[11 rows x 7 columns]"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "#>{local::DuckDuckDatabase.test3}#->\n",
+ "filter(x|$x.HeadQuarter=='Hyderabad')->from(local::DuckDuckRuntime)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "1cc1070b-de83-4259-b048-e40ab31672d1",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "Total Time Taken - 2.74s\n",
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "DataFrame variable 'my_df' stored in the Python Environment
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "#>{local::DuckDuckDatabase.test3}#->filter(x|$x.HeadQuarter=='Hyderabad')->from(local::DuckDuckRuntime) --var my_df"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "499fbbc7-64d8-4fde-ab43-6641e7120488",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " Company | \n",
+ " Sector | \n",
+ " Stage | \n",
+ " HeadQuarter | \n",
+ " Description | \n",
+ " TurnOver($) | \n",
+ " Year | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " Eunimart | \n",
+ " E-Commerce | \n",
+ " Seed | \n",
+ " Hyderabad | \n",
+ " Eunimart is a one stop solution for merchants ... | \n",
+ " 3.380110e+07 | \n",
+ " 2018 | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " Hyperdata.IO | \n",
+ " Marketing | \n",
+ " Angel | \n",
+ " Hyderabad | \n",
+ " Hyperdata combines advanced machine learning w... | \n",
+ " 4.555556e+06 | \n",
+ " 2018 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " Recordent | \n",
+ " FinTech | \n",
+ " Seed | \n",
+ " Hyderabad | \n",
+ " A technology-enabled platform that helps MSMEs... | \n",
+ " 6.972143e+08 | \n",
+ " 2020 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " Super Surfaces | \n",
+ " Design | \n",
+ " Series A | \n",
+ " Hyderabad | \n",
+ " A design solution based company | \n",
+ " 5.000000e+05 | \n",
+ " 2020 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " Freyr Energy | \n",
+ " Renewable Energy | \n",
+ " Undisclosed | \n",
+ " Hyderabad | \n",
+ " Freyr Energy is a company that provides full s... | \n",
+ " 2.000000e+06 | \n",
+ " 2021 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " Company Sector Stage HeadQuarter \\\n",
+ "0 Eunimart E-Commerce Seed Hyderabad \n",
+ "1 Hyperdata.IO Marketing Angel Hyderabad \n",
+ "2 Recordent FinTech Seed Hyderabad \n",
+ "3 Super Surfaces Design Series A Hyderabad \n",
+ "4 Freyr Energy Renewable Energy Undisclosed Hyderabad \n",
+ "\n",
+ " Description TurnOver($) Year \n",
+ "0 Eunimart is a one stop solution for merchants ... 3.380110e+07 2018 \n",
+ "1 Hyperdata combines advanced machine learning w... 4.555556e+06 2018 \n",
+ "2 A technology-enabled platform that helps MSMEs... 6.972143e+08 2020 \n",
+ "3 A design solution based company 5.000000e+05 2020 \n",
+ "4 Freyr Energy is a company that provides full s... 2.000000e+06 2021 "
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "#Kernel: Python\n",
+ "#Code in Python below. Don't Remove this Header!!\n",
+ "my_df.head()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d5d9af73-ae1e-447c-86d8-cf8448ab0065",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "ILegend",
+ "language": "ilegend",
+ "name": "ilegend_kernel"
+ },
+ "language_info": {
+ "codemirror_mode": "ilegend",
+ "file_extension": ".ilgd",
+ "mimetype": "text/x-ilegend",
+ "name": "ilegend"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/ILegend-JupyterMultiKernel/Kernel/LICENSE b/ILegend-JupyterMultiKernel/Kernel/LICENSE
new file mode 100644
index 000000000..a6eb512fa
--- /dev/null
+++ b/ILegend-JupyterMultiKernel/Kernel/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2025 Shanmukh raj MSS
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/ILegend-JupyterMultiKernel/Kernel/Makefile b/ILegend-JupyterMultiKernel/Kernel/Makefile
new file mode 100644
index 000000000..fc4dd931f
--- /dev/null
+++ b/ILegend-JupyterMultiKernel/Kernel/Makefile
@@ -0,0 +1,10 @@
+
+install:
+ pip install -e .
+ python -m ilegend_kernel.install
+
+
+
+clean:
+ pip uninstall ilegend_kernel
+ jupyter kernelspec remove ilegend_kernel
diff --git a/ILegend-JupyterMultiKernel/Kernel/README.md b/ILegend-JupyterMultiKernel/Kernel/README.md
new file mode 100644
index 000000000..9b99330b7
--- /dev/null
+++ b/ILegend-JupyterMultiKernel/Kernel/README.md
@@ -0,0 +1,26 @@
+
+# Jupyter-ILegend - A Jupyter Kernel for ILegend Services
+
+
+## Installation
+
+### Requirements
+
+- At least **Python** 3.5
+ - Tested with Python 3.12.2
+- **Jupyter** installation with JupyterLab and/or Juypter Notebook
+ - Tested with
+ - jupyter_core: 5.7.2
+ - jupyterlab: 4.x.x
+ - notebook: 7.x.x
+
+
+## Development
+
+### Development Install
+
+1. Download the repository.
+2. Change to the root directory of the repository.
+3. `pip install .`
+4. Install the kernelspec:
+ - `python -m ilegend_kernel.install`
diff --git a/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/__init__.py b/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/__init__.py
new file mode 100644
index 000000000..cec4b2bde
--- /dev/null
+++ b/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/__init__.py
@@ -0,0 +1,3 @@
+
+from .kernel import ILegendRouterKernel
+__version__ = ILegendRouterKernel.implementation_version
\ No newline at end of file
diff --git a/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/__main__.py b/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/__main__.py
new file mode 100644
index 000000000..5ca8b8055
--- /dev/null
+++ b/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/__main__.py
@@ -0,0 +1,5 @@
+
+from ipykernel.kernelapp import IPKernelApp
+from .kernel import ILegendRouterKernel
+
+IPKernelApp.launch_instance(kernel_class=ILegendRouterKernel)
diff --git a/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/ilegend_kernel_config.py b/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/ilegend_kernel_config.py
new file mode 100644
index 000000000..80800c062
--- /dev/null
+++ b/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/ilegend_kernel_config.py
@@ -0,0 +1 @@
+c = get_config()
\ No newline at end of file
diff --git a/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/install.py b/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/install.py
new file mode 100644
index 000000000..019346339
--- /dev/null
+++ b/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/install.py
@@ -0,0 +1,67 @@
+import argparse
+import json
+import logging
+import os
+import shutil
+import sys
+import tempfile
+
+from jupyter_client.kernelspec import KernelSpecManager
+
+logger = logging.getLogger(__name__)
+
+KERNELSPEC_FILES = [
+ 'kernel.js',
+ 'logo-64x64.png'
+]
+
+def get_kernelspec_dir_path():
+ """
+ Get the path of the kernelspec directory where the static files needed for the installation are.
+ This currently only includes the kernel.js file,
+ because the kernelspec (kernel.json) is generated dynamically.
+ """
+ dirname = os.path.dirname(__file__)
+ kernelspec_dir_path = os.path.join(dirname, 'kernelspec')
+ return kernelspec_dir_path
+
+def create_kernelspec(dest_dir):
+ with open(os.path.join(dest_dir, 'kernel.json'), 'w', encoding='utf-8') as f:
+ kernel_json = {
+ 'argv': [sys.executable, '-m', 'ilegend_kernel', '-f', '{connection_file}'],
+ 'display_name': 'ILegend',
+ 'language': 'ilegend',
+ 'metadata': {
+ 'logo-64x64': 'logo-64x64.png' } # or 'logo-svg': 'logo.svg'
+ }
+ json.dump(kernel_json, f, ensure_ascii=False, indent=4)
+
+ kernelspec_dir = get_kernelspec_dir_path()
+ for file in KERNELSPEC_FILES:
+ shutil.copyfile(os.path.join(kernelspec_dir, file), os.path.join(dest_dir, file))
+
+def main(argv=None):
+ logging.basicConfig(
+ format='%(levelname)s: %(message)s',
+ level=logging.INFO,
+ )
+
+ ap = argparse.ArgumentParser()
+ ap.add_argument(
+ '--user',
+ action='store_true',
+ help="install to the per-user kernel registry instead of sys.prefix (use if you get permission errors during installation)")
+ ap.add_argument(
+ '--prefix',
+ help="install to the given prefix: PREFIX/share/jupyter/kernels/")
+ args = ap.parse_args(argv)
+
+ if not args.user and not args.prefix:
+ args.prefix = sys.prefix
+
+ with tempfile.TemporaryDirectory() as temp_dir:
+ create_kernelspec(temp_dir)
+ KernelSpecManager().install_kernel_spec(temp_dir, 'ilegend_kernel', user=args.user, prefix=args.prefix)
+
+if __name__ == '__main__':
+ main()
diff --git a/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/kernel.py b/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/kernel.py
new file mode 100644
index 000000000..188c06a7e
--- /dev/null
+++ b/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/kernel.py
@@ -0,0 +1,2108 @@
+from ipykernel.kernelbase import Kernel
+from jupyter_client import KernelManager
+from queue import Empty
+import traceback
+import re
+import textwrap
+import ast
+import requests
+import datetime
+import json
+from ipykernel.kernelbase import Kernel
+from .magics import CELL_MAGICS, LINE_MAGICS
+from ipykernel.kernelbase import Kernel
+import pandas as pd
+import os
+import re
+from dash import Dash, html, dcc,Output,Input,State
+from dash_ag_grid import AgGrid
+import threading
+import socket
+
+
+
+
+class ILegendRouterKernel(Kernel):
+ implementation = "ilegend"
+ implementation_version = "1.2"
+ banner = "ILegend kernel: route to python3 when #kernel: python is used"
+ language_info = {
+ "name": "ilegend",
+ "file_extension": ".ilgd",
+ "mimetype": "text/x-ilegend",
+ "codemirror_mode": "ilegend",
+ }
+ tables = []
+ details = {}
+ user_ns={}
+ check = False
+
+
+
+ def _launch_python_kernel(self):
+ km = KernelManager(kernel_name="python3")
+ km.start_kernel()
+ kc = km.client()
+ kc.start_channels()
+ return {"manager": km, "client": kc}
+
+
+
+
+
+ def _extract_kernel_choice(self, code: str) -> str:
+ """Return 'python' only when first line starts '#kernel: python'."""
+ lines = code.lstrip().splitlines()
+ if lines and lines[0].lower().startswith("#kernel:"):
+ if lines[0].split(":", 1)[1].strip().lower().startswith("python"):
+ return "python"
+ return ""
+
+
+
+
+ def _strip_first_line(self,code):
+ lines = code.splitlines()
+ return "\n".join(lines[2:]) if (lines and lines[0].lower().startswith("#kernel:")) else code
+
+
+
+
+ def to_json(self,df):
+ return df.to_json(orient="records")
+
+
+
+
+ def get_columns(self):
+ if self.tables == []:
+ return
+ for x in self.tables:
+ response = requests.post("http://127.0.0.1:9095/api/server/execute",json={"line":"get_attributes " + "local::DuckDuckConnection."+x})
+ output = response.json()
+ self.details[x] = [y for y in output["attributes"]]
+
+
+
+
+ def initiate(self):
+ from IPython.display import HTML
+ import threading, time
+ from IPython.display import clear_output
+ stop_event = threading.Event()
+ def show_running_time():
+ start = time.time()
+ while not stop_event.is_set():
+ elapsed = time.time() - start
+ s = HTML(f"Kernel Warming up... {elapsed:.2f} seconds elapsed\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ self.send_response(self.iopub_socket, 'clear_output', {'wait': True})
+ time.sleep(0.01)
+ timer_thread = threading.Thread(target=show_running_time)
+ timer_thread.start()
+ try:
+ response = requests.post("http://127.0.0.1:9095/api/server/execute", json={"line": "get_tables " + "local::DuckDuckConnection"})
+ output = response.json()
+ self.tables = [x for x in output["tables"]]
+ self.get_columns()
+ finally:
+ stop_event.set()
+ timer_thread.join()
+
+
+
+
+ def parse_db_output(self, text: str):
+ lines = [line.strip() for line in text.strip().splitlines() if line.strip()]
+ result = {
+ 'database': None,
+ 'tables': []
+ }
+ current_table = None
+ inside_table = False
+ for line in lines:
+ if line.startswith("Database"):
+ result['database'] = line.split("Database", 1)[1].strip()
+ elif line.startswith("Table"):
+ table_name = line.split("Table", 1)[1].strip()
+ current_table = {'name': table_name, 'columns': []}
+ inside_table = True
+ elif line == ')':
+ if inside_table and current_table:
+ result['tables'].append(current_table)
+ current_table = None
+ inside_table = False
+ elif inside_table:
+ if ',' in line:
+ line = line[:-1] # remove trailing comma
+ if ' ' in line:
+ col_name, col_type = line.split(None, 1)
+ current_table['columns'].append({'name': col_name, 'type': col_type})
+ return result
+
+
+
+
+
+
+
+
+
+ def render_database_ui(self,data):
+ import html
+ db_name = data.get("database", "Unknown")
+ tables = data.get("tables", [])
+ html_parts = [f"Database: {html.escape(db_name)}
"]
+ for table in tables:
+ table_name = table.get("name", "Unnamed Table")
+ columns = table.get("columns", [])
+ html_parts.append(f"
Table: {html.escape(table_name)}
")
+ # Table of columns
+ html_parts.append("
")
+ html_parts.append("| Column Name | Type |
")
+ for col in columns:
+ col_name = html.escape(col.get("name", ""))
+ col_type = html.escape(col.get("type", ""))
+ html_parts.append(f"| {col_name} | {col_type} |
")
+ html_parts.append("
")
+ html_parts.append("
")
+ return "\n".join(html_parts)
+
+
+
+
+
+
+ def start_aggrid_dash(self, df, port=None):
+ if port is None:
+ try:
+ port = self.find_free_port()
+ except Exception:
+ return "No port Found"
+ column_defs = [
+ {"field": c, "headerName": c.capitalize(), "headerClass": "custom-header"}
+ for c in df.columns
+ ]
+ app = Dash(__name__)
+ app.index_string = """
+
+
+ {%metas%}
+ Data Viewer
+ {%favicon%}
+ {%css%}
+
+
+
+ {%app_entry%}
+
+
+ """
+ app.layout = html.Div(
+ [
+ dcc.Store(id="theme-store", data="light"),
+ html.Button("🌙 Dark mode", id="theme-btn", n_clicks=0,
+ className="theme-toggle"),
+ AgGrid(
+ id="grid",
+ rowData=df.to_dict("records"),
+ columnDefs=column_defs,
+ defaultColDef={"sortable": True, "filter": True, "resizable": True},
+ columnSize="sizeToFit",
+ className="ag-theme-balham",
+ ),
+ ],
+ id="page",
+ style={"margin": 0, "padding": 0, "height": "100vh", "width": "100vw"},
+ )
+ @app.callback(
+ Output("theme-store", "data"),
+ Output("theme-btn", "children"),
+ Input("theme-btn", "n_clicks"),
+ State("theme-store", "data"),
+ prevent_initial_call=True,
+ )
+ def toggle_theme(n, current):
+ new_theme = "dark" if current == "light" else "light"
+ btn_label = "☀️ Light mode" if new_theme == "dark" else "🌙 Dark mode"
+ return new_theme, btn_label
+ @app.callback(
+ Output("page", "className"),
+ Input("theme-store", "data"),
+ )
+ def apply_theme(theme):
+ return theme
+ threading.Thread(
+ target=lambda: app.run(port=port, debug=False, use_reloader=False),
+ daemon=True,
+ ).start()
+ return f"http://localhost:{port}"
+
+
+
+
+
+
+
+
+
+
+
+ def find_free_port(self,start=8050):
+ port = start
+ while(True):
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+ try:
+ s.bind(('localhost', port))
+ return port
+ except OSError:
+ port = port + 1
+
+
+
+
+ def do_execute(self, code, silent, store_history=True, user_expressions=None, allow_stdin=False):
+ if code.strip().startswith("%put"):
+ return self._handle_put_command(code)
+ kernel_choice = self._extract_kernel_choice(code)
+ exec_code = self._strip_first_line(code)
+ if kernel_choice == "python":
+ try:
+ if not hasattr(self, "_python"):
+ self._python = self._launch_python_kernel()
+ kc = self._python["client"]
+ msg_id = kc.execute(exec_code)
+ while True:
+ try:
+ msg = kc.get_iopub_msg(timeout=10)
+ except Empty:
+ break
+ if msg["parent_header"].get("msg_id") != msg_id:
+ continue
+ if msg["msg_type"] in {
+ "stream", "display_data", "execute_result",
+ "error", "clear_output"
+ }:
+ if msg["msg_type"] == "execute_result":
+ msg_type = "display_data"
+ content = {
+ "data": msg["content"]["data"],
+ "metadata": msg["content"].get("metadata", {}),
+ }
+ else:
+ msg_type = msg["msg_type"]
+ content = msg["content"]
+ self.session.send(
+ self.iopub_socket, msg_type, content,
+ parent=self._parent_header
+ )
+ elif msg["msg_type"] == "status" and \
+ msg["content"].get("execution_state") == "idle":
+ break
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ except Exception as e:
+ tb = traceback.format_exc()
+ self.send_response(self.iopub_socket, "stream",
+ {"name": "stderr",
+ "text": f"[Router‑Error] {e}\n{tb}"})
+ return {"status": "error",
+ "execution_count": self.execution_count,
+ "ename": type(e).__name__,
+ "evalue": str(e),
+ "traceback": tb.splitlines()}
+
+ else:
+ if(self.check == False):
+ self.initiate()
+ self.check = True
+ magic_line, *cell_lines = code.splitlines()
+ cell_code = "\n".join(cell_lines)
+
+
+ if code.strip().startswith("start_legend"):
+ import threading, time
+ from IPython.display import clear_output
+ from IPython.display import HTML
+ stop_event = threading.Event()
+ def show_running_time():
+ start = time.time()
+ while not stop_event.is_set():
+ elapsed = time.time() - start
+ s = HTML(f"Activating Legend Features... {elapsed:.2f} seconds elapsed\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ self.send_response(self.iopub_socket, 'clear_output', {'wait': True})
+ time.sleep(0.01)
+ s = HTML(f"Legend Features Activated in - {elapsed:.2f}s\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ timer_thread = threading.Thread(target=show_running_time)
+ timer_thread.start()
+ connection_name = "local::DuckDuckConnection"
+ try:
+ response = requests.post("http://127.0.0.1:9095/api/server/execute", json={"line": "get_tables " + connection_name})
+ finally:
+ stop_event.set()
+ timer_thread.join()
+ output = response.json()
+ self.tables = [x for x in output["tables"]]
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+
+ elif code.strip().startswith("%%"):
+ magic_name = magic_line[2:]
+ if magic_name in CELL_MAGICS:
+ output = CELL_MAGICS[magic_name](cell_code)
+ stream_content = {'name': 'stdout', 'text': output}
+ self.send_response(self.iopub_socket, 'stream', stream_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+
+ elif code.strip().startswith("%"):
+ magic_line = code.strip().split()
+ magic_name = magic_line[0][1:].strip()
+ if magic_name == 'date':
+ t = datetime.datetime.now()
+ t = t.strftime("%Y-%m-%d %H:%M:%S")
+ stream_content = {'name': 'stdout', 'text': t}
+ self.send_response(self.iopub_socket, 'stream', stream_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ elif magic_name in LINE_MAGICS:
+ output = LINE_MAGICS[magic_name](magic_line[1])
+ stream_content = {'name': 'stdout', 'text': output}
+ self.send_response(self.iopub_socket, 'stream', stream_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+ elif code.strip().startswith("complete"):
+ from IPython.display import HTML
+ import threading, time
+ from IPython.display import clear_output
+ stop_event = threading.Event()
+ def show_running_time():
+ start = time.time()
+ while not stop_event.is_set():
+ elapsed = time.time() - start
+ s = HTML(f"Getting suggestions... {elapsed:.2f} seconds elapsed\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ self.send_response(self.iopub_socket, 'clear_output', {'wait': True})
+ time.sleep(0.01)
+ s = HTML(f"Suggestions fetched in - {elapsed:.2f}s\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ timer_thread = threading.Thread(target=show_running_time)
+ timer_thread.start()
+ try:
+ response = requests.post("http://127.0.0.1:9095/api/server/execute", json={"line": magic_line})
+ finally:
+ stop_event.set()
+ timer_thread.join()
+ output = response.text
+ if(output.startswith("Invalid")):
+ s = HTML(f"Error: {output}
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ stream_content = {'name': 'stdout', 'text': output}
+ self.send_response(self.iopub_socket, 'stream', stream_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+
+
+
+
+ elif code.strip().startswith("sql_to_json_line"):
+ from IPython.display import HTML
+ headers = {"Content-Type": "text/plain"}
+ response = requests.post("http://127.0.0.1:9095/api/sql/v1/grammar/grammarToJson",data=cell_code, headers=headers)
+ output = response.json()
+ if("code" in output and output["code"]==-1):
+ s = HTML(f"Error: {output["message"]}
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ stream_content = {'name': 'stdout', 'text': json.dumps(output, indent=2)}
+ self.send_response(self.iopub_socket, 'stream', stream_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+
+ elif code.strip().startswith("sql_to_json_batch"):
+ from IPython.display import HTML
+ headers = {"Content-Type": "application/json"}
+ queries = [q.strip() for q in cell_code.split(";") if q.strip()]
+ payload ={
+ f"query{i+1}": {"value": query + ";"}
+ for i, query in enumerate(queries)
+ }
+ response = requests.post(
+ "http://127.0.0.1:9095/api/sql/v1/grammar/grammarToJson/batch",
+ data=json.dumps(payload),
+ headers=headers
+ )
+ output = response.json()
+ if("code" in output and output["code"]==-1):
+ s = HTML(f"Error: {output["message"]}
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ stream_content = {'name': 'stdout', 'text': json.dumps(output, indent=2)}
+ self.send_response(self.iopub_socket, 'stream', stream_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+ elif code.strip().startswith("show_func_activators"):
+ from IPython.display import HTML
+ headers = {"Content-Type": "text/plain"}
+ response = requests.get("http://127.0.0.1:9095/api/functionActivator/list")
+ output = response.json()
+ if("code" in output and output["code"]==-1):
+ s = HTML(f"Error: {output["message"]}
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ stream_content = {'name': 'stdout', 'text': json.dumps(output, indent=2)}
+ self.send_response(self.iopub_socket, 'stream', stream_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+ elif code.strip().startswith("create "):
+ from IPython.display import HTML
+ headers = {"Content-Type": "text/plain"}
+ response = requests.post("http://127.0.0.1:9095/api/data/createtable",data=magic_line, headers=headers)
+ output = response.json()
+ if("error" in output):
+ s = HTML(f"Error: {output["error"]}
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ s='-----------------Table Created-----------------'+'\n' + "-----Ware House: "+output["warehouse"]+"-----"+'\n' + "-----DataBase: "+output["database"]+"-----"+'\n' + "-----Schema: "+output["schema"]+"-----"+'\n'
+ s = s+ "-----Columns-----"+'\n'
+ p = output["columns"]
+ for column in p:
+ s = s + column["name"] + f"[{column["type"]}]"
+ if(column["primaryKey"]==True):
+ s = s + " is a Primary Key"+"\n"
+ else:
+ s = s+'\n'
+ stream_content = {'name': 'stdout', 'text': s}
+ self.send_response(self.iopub_socket, 'stream', stream_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+
+
+
+ elif code.strip().startswith("insertrow"):
+ from IPython.display import HTML
+ headers = {"Content-Type": "application/json"}
+ if '->' not in cell_code:
+ s = HTML("Error: Improper usage of command
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ row_part, path_part = cell_code.split('->', 1)
+ row_part = row_part.strip()
+ path_part = path_part.strip()
+ if not (row_part.startswith('[') and row_part.endswith(']')):
+ s = HTML("Error: Improper usage of command
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ row_part = row_part[1:-1].strip()
+ row_dict = {}
+ for pair in row_part.split(','):
+ if ':' not in pair:
+ s = HTML("Error: Improper usage of command
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ key, val = map(str.strip, pair.split(':', 1))
+ try:
+ parsed_val = ast.literal_eval(val)
+ except Exception:
+ parsed_val = val
+ row_dict[key] = parsed_val
+ payload = {"path": path_part,"row": row_dict}
+ response = requests.post("http://127.0.0.1:9095/api/data/insertrow",data=json.dumps(payload), headers=headers)
+ output = response.json()
+ if "error" in output:
+ s = HTML(f"Error: {output["error"]}
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ else:
+ s = 'Row Added.' + '\n' + '('
+ p = output["row"]
+ for fields in p:
+ s = s + f"{fields}: {p[fields]}" + ', '
+ s = s[0:len(s)-2] + ')'
+ stream_content = {'name': 'stdout', 'text': s}
+ self.send_response(self.iopub_socket, 'stream', stream_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+
+
+
+
+
+
+ elif code.strip().startswith("delete_row"):
+ from IPython.display import HTML
+ headers = {"Content-Type": "text/plain"}
+ response = requests.post("http://127.0.0.1:9095/api/data/deleterow", data=cell_code, headers=headers)
+ output = response.json()
+ if "error" in output:
+ s = HTML(f"Error: {output["error"]}
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ deleted = output["deletedRow"]
+ s = 'Row deleted successfully:\n('
+ for key, val in deleted.items():
+ s += f"{key}: {val}, "
+ s = s.rstrip(", ") + ')'
+ stream_content = {'name': 'stdout', 'text': s}
+ self.send_response(self.iopub_socket, 'stream', stream_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+
+
+
+
+
+
+ elif code.strip().startswith("show_table"):
+ from IPython.display import HTML
+ headers = {"Content-Type": "text/plain"}
+ response = requests.post("http://127.0.0.1:9095/api/data/fetchtable",data=cell_code,headers=headers)
+ output = response.json()
+ if "error" in output:
+ s = HTML(f"Error: {output["error"]}
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ try:
+ df = pd.DataFrame(output)
+ except Exception as e:
+ s = HTML(f"Error: {output["error"]}
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ display_content = {
+ 'data': {
+ 'text/plain': str(df),
+ 'text/html': df.to_html()
+ },
+ 'metadata': {}
+ }
+ self.send_response(self.iopub_socket, 'display_data', display_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+
+
+ elif code.strip().startswith("show_all_tables"):
+ from IPython.display import HTML
+ response = requests.get("http://127.0.0.1:9095/api/data/showtables")
+ output = response.json()
+ if "error" in output:
+ s = HTML(f"Error: {output["error"]}
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ s = (f"Number of tables: {output['count']}") + "\n"
+ s = s + "Tables:" + "\n"
+ for table in output['tables']:
+ s = s + table + '\n'
+ stream_content = {'name': 'stdout', 'text': s}
+ self.send_response(self.iopub_socket, 'stream', stream_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+
+
+
+ elif code.strip().startswith("load duckdb"):
+ from IPython.display import HTML
+ headers = {"Content-Type": "text/plain"}
+ response = requests.post("http://127.0.0.1:9095/api/data/duckdb/load",data=cell_code, headers=headers)
+ output = response.text
+ stream_content = {'name': 'stdout', 'text': output}
+ self.send_response(self.iopub_socket, 'stream', stream_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+
+
+
+
+ elif code.strip().startswith("query duckdb"):
+ from IPython.display import HTML
+ headers = {"Content-Type": "application/json"}
+ magic_line_new = magic_line.split()
+ payload = {
+ "dbPath": f"{magic_line_new[2]}",
+ "query": f"{cell_code}"
+ }
+ response = requests.post("http://127.0.0.1:9095/api/data/duckdb/query", data=json.dumps(payload), headers=headers)
+ output = response.json()
+ try:
+ df = pd.DataFrame(output)
+ except Exception as e:
+ s = HTML(f"Error: {output["error"]}
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ display_content = {
+ 'data': {
+ 'text/plain': str(df),
+ 'text/html': df.to_html()
+ },
+ 'metadata': {}
+ }
+ self.send_response(self.iopub_socket, 'display_data', display_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+
+
+
+
+ elif code.strip().startswith("load "):
+ from IPython.display import HTML
+ import threading, time
+ from IPython.display import clear_output
+ stop_event = threading.Event()
+ def show_running_time():
+ start = time.time()
+ while not stop_event.is_set():
+ elapsed = time.time() - start
+ s = HTML(f"Loading csv data into table in DuckDB connection... {elapsed:.2f} seconds elapsed\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ self.send_response(self.iopub_socket, 'clear_output', {'wait': True})
+ time.sleep(0.01)
+ s = HTML(f"Total Time Taken - {elapsed:.2f}s\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ timer_thread = threading.Thread(target=show_running_time)
+ timer_thread.start()
+ try:
+ response = requests.post("http://127.0.0.1:9095/api/server/execute", json={"line": magic_line})
+ response2 = requests.post("http://127.0.0.1:9095/api/server/execute", json={"line": "get_tables " + "local::DuckDuckConnection"})
+ output2 = response2.json()
+ self.tables = [x for x in output2["tables"]]
+ self.get_columns()
+ finally:
+ stop_event.set()
+ timer_thread.join()
+ if(response.headers.get('Content-Type') == 'application/json'):
+ output = response.json()
+ s = HTML(f"{output["error"]}
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ output = response.text
+ stream_content = {'name': 'stdout', 'text': output}
+ self.send_response(self.iopub_socket, 'stream', stream_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+
+
+
+
+
+
+
+ elif code.strip().startswith("db"):
+ from IPython.display import HTML
+ import threading, time
+ from IPython.display import clear_output
+ stop_event = threading.Event()
+ def show_running_time():
+ start = time.time()
+ while not stop_event.is_set():
+ elapsed = time.time() - start
+ s = HTML(f"Exploring Database... {elapsed:.2f} seconds elapsed\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ self.send_response(self.iopub_socket, 'clear_output', {'wait': True})
+ time.sleep(0.01)
+ s = HTML(f"Total Time Taken - {elapsed:.2f}s\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ timer_thread = threading.Thread(target=show_running_time)
+ timer_thread.start()
+ try:
+ response = requests.post("http://127.0.0.1:9095/api/server/execute", json={"line": magic_line})
+ finally:
+ stop_event.set()
+ timer_thread.join()
+ if(response.headers.get('Content-Type') == 'application/json'):
+ output = response.json()
+ s = HTML(f"Error: {output["error"]}
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ output = response.text
+ try:
+ structured = self.parse_db_output(output)
+ html_str = self.render_database_ui(structured)
+ self.send_response(self.iopub_socket, 'display_data', {
+ 'data': {'text/html': html_str},
+ 'metadata': {}
+ })
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ except Exception as e:
+ from IPython.display import HTML
+ import html
+ s = HTML(f"Parsing/Rendering failed: {html.escape(str(e))}
")
+ self.send_response(self.iopub_socket, 'display_data', {
+ 'data': {'text/html': str(s.data)},
+ 'metadata': {}
+ })
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+ elif code.strip().startswith("drop_all_tables"):
+ from IPython.display import HTML
+ import threading, time
+ from IPython.display import clear_output
+ stop_event = threading.Event()
+ def show_running_time():
+ start = time.time()
+ while not stop_event.is_set():
+ elapsed = time.time() - start
+ s = HTML(f"Dropping tables from Connection... {elapsed:.2f} seconds elapsed\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ self.send_response(self.iopub_socket, 'clear_output', {'wait': True})
+ time.sleep(0.01)
+ s = HTML(f"Total Time Taken - {elapsed:.2f}s\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ timer_thread = threading.Thread(target=show_running_time)
+ timer_thread.start()
+ try:
+ response = requests.post("http://127.0.0.1:9095/api/server/execute", json={"line": magic_line})
+ response2 = requests.post("http://127.0.0.1:9095/api/server/execute", json={"line": "get_tables " + "local::DuckDuckConnection"})
+ output2 = response2.json()
+ self.tables = [x for x in output2["tables"]]
+ self.get_columns()
+ finally:
+ stop_event.set()
+ timer_thread.join()
+ if(response.headers.get('Content-Type') == 'application/json'):
+ output = response.json()
+ s = HTML(f"{output["error"]}
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ output = response.text
+ stream_content = {'name': 'stdout', 'text': output}
+ self.send_response(self.iopub_socket, 'stream', stream_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ elif code.strip().startswith("macro "):
+ from IPython.display import HTML
+ import threading, time
+ from IPython.display import clear_output
+ stop_event = threading.Event()
+ def show_running_time():
+ start = time.time()
+ while not stop_event.is_set():
+ elapsed = time.time() - start
+ s = HTML(f"Establishing Macro... {elapsed:.2f} seconds elapsed\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ self.send_response(self.iopub_socket, 'clear_output', {'wait': True})
+ time.sleep(0.01)
+ s = HTML(f"Macro Established in - {elapsed:.2f}s\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ timer_thread = threading.Thread(target=show_running_time)
+ timer_thread.start()
+ try:
+ response = requests.post("http://127.0.0.1:9095/api/server/execute", json={"line": magic_line})
+ finally:
+ stop_event.set()
+ timer_thread.join()
+ output = response.text
+ if(output.startswith("Invalid")):
+ s = HTML(f"Error: {output}
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ stream_content = {'name': 'stdout', 'text': output}
+ self.send_response(self.iopub_socket, 'stream', stream_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+
+
+
+
+
+
+
+ elif code.strip().startswith("show_macros"):
+ from IPython.display import HTML
+ import threading, time
+ from IPython.display import clear_output
+ stop_event = threading.Event()
+ def show_running_time():
+ start = time.time()
+ while not stop_event.is_set():
+ elapsed = time.time() - start
+ s = HTML(f"Fetching all Macros... {elapsed:.2f} seconds elapsed\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ self.send_response(self.iopub_socket, 'clear_output', {'wait': True})
+ time.sleep(0.01)
+ s = HTML(f"Macros fetched in- {elapsed:.2f}s\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ timer_thread = threading.Thread(target=show_running_time)
+ timer_thread.start()
+ try:
+ response = requests.post("http://127.0.0.1:9095/api/server/execute", json={"line": magic_line})
+ finally:
+ stop_event.set()
+ timer_thread.join()
+ output = response.text
+ stream_content = {'name': 'stdout', 'text': output}
+ self.send_response(self.iopub_socket, 'stream', stream_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ elif code.strip().startswith("clear_macros"):
+ from IPython.display import HTML
+ import threading, time
+ from IPython.display import clear_output
+ stop_event = threading.Event()
+ def show_running_time():
+ start = time.time()
+ while not stop_event.is_set():
+ elapsed = time.time() - start
+ s = HTML(f"Clearing all Macros... {elapsed:.2f} seconds elapsed\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ self.send_response(self.iopub_socket, 'clear_output', {'wait': True})
+ time.sleep(0.01)
+ s = HTML(f"Macros cleared in- {elapsed:.2f}s\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ timer_thread = threading.Thread(target=show_running_time)
+ timer_thread.start()
+ try:
+ response = requests.post("http://127.0.0.1:9095/api/server/execute", json={"line": magic_line})
+ finally:
+ stop_event.set()
+ timer_thread.join()
+ output = response.text
+ stream_content = {'name': 'stdout', 'text': output}
+ self.send_response(self.iopub_socket, 'stream', stream_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+
+ elif code.strip().startswith("get_tables "):
+ from IPython.display import HTML
+ import threading, time
+ from IPython.display import clear_output
+ stop_event = threading.Event()
+ def show_running_time():
+ start = time.time()
+ while not stop_event.is_set():
+ elapsed = time.time() - start
+ s = HTML(f"Fetching tables... {elapsed:.2f} seconds elapsed\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ self.send_response(self.iopub_socket, 'clear_output', {'wait': True})
+ time.sleep(0.01)
+ s = HTML(f"Tables Fetched in - {elapsed:.2f}s\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ timer_thread = threading.Thread(target=show_running_time)
+ timer_thread.start()
+ try:
+ response = requests.post("http://127.0.0.1:9095/api/server/execute", json={"line": magic_line})
+ finally:
+ stop_event.set()
+ timer_thread.join()
+ output = response.json()
+ s = ""
+ for x in output["tables"]:
+ s = s+x+"\n"
+ stream_content = {'name': 'stdout', 'text': s}
+ self.send_response(self.iopub_socket, 'stream',stream_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+
+ elif code.strip().startswith("get_attributes "):
+ from IPython.display import HTML
+ import threading, time
+ from IPython.display import clear_output
+ stop_event = threading.Event()
+ def show_running_time():
+ start = time.time()
+ while not stop_event.is_set():
+ elapsed = time.time() - start
+ s = HTML(f"Fetching attributes... {elapsed:.2f} seconds elapsed\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ self.send_response(self.iopub_socket, 'clear_output', {'wait': True})
+ time.sleep(0.01)
+ s = HTML(f"Attributes Fetched in - {elapsed:.2f}s\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ timer_thread = threading.Thread(target=show_running_time)
+ timer_thread.start()
+ try:
+ response = requests.post("http://127.0.0.1:9095/api/server/execute", json={"line": magic_line})
+ finally:
+ stop_event.set()
+ timer_thread.join()
+ output = response.json()
+ s=""
+ for x in output["attributes"]:
+ s = s + x + "\n"
+ stream_content = {'name': 'stdout', 'text': s}
+ self.send_response(self.iopub_socket, 'stream',stream_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+ elif code.strip().startswith("get_all"):
+ from IPython.display import HTML
+ import threading, time
+ from IPython.display import clear_output
+ stop_event = threading.Event()
+ def show_running_time():
+ start = time.time()
+ while not stop_event.is_set():
+ elapsed = time.time() - start
+ s = HTML(f"Fetching attributes... {elapsed:.2f} seconds elapsed\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ self.send_response(self.iopub_socket, 'clear_output', {'wait': True})
+ time.sleep(0.01)
+ s = HTML(f"Attributes Fetched in - {elapsed:.2f}s\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ timer_thread = threading.Thread(target=show_running_time)
+ timer_thread.start()
+ try:
+ response = requests.post("http://127.0.0.1:9095/api/server/execute", json={"line": magic_line})
+ finally:
+ stop_event.set()
+ timer_thread.join()
+ output = response.json()
+ s=""
+ for x in output["connections"]:
+ s = s + x + "\n"
+ stream_content = {'name': 'stdout', 'text': s}
+ self.send_response(self.iopub_socket, 'stream',stream_content)
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+
+
+
+
+
+ elif code.strip().startswith("#>"):
+ code = code.replace('\n', '')
+ match1 = re.search(r'(.*?)--file\s+(\w+)\s*$', code.strip())
+ match2 = re.search(r'(.*?)--var\s+(\w+)\s*$', code.strip())
+ if match1:
+ query_part = match1.group(1).strip()
+ filename = match1.group(2).strip()
+ variable = None
+ elif match2:
+ query_part = match2.group(1).strip()
+ variable = match2.group(2).strip()
+ filename = None
+ else:
+ query_part = code.strip()
+ variable = None
+ filename = None
+ from IPython.display import HTML
+ import threading, time
+ from IPython.display import clear_output
+ stop_event = threading.Event()
+ def show_running_time():
+ start = time.time()
+ while not stop_event.is_set():
+ elapsed = time.time() - start
+ s = HTML(f"Data Quereying... {elapsed:.2f} seconds elapsed\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ self.send_response(self.iopub_socket, 'clear_output', {'wait': True})
+ time.sleep(0.01)
+ s = HTML(f"Total Time Taken - {elapsed:.2f}s\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ timer_thread = threading.Thread(target=show_running_time)
+ timer_thread.start()
+ try:
+ response = requests.post("http://127.0.0.1:9095/api/server/execute", json={"line": query_part})
+ finally:
+ stop_event.set()
+ timer_thread.join()
+ output = response.json()
+ try:
+ df = pd.DataFrame(output)
+ except Exception:
+ s = HTML(f"Error: {output["error"]}
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ if filename!=None:
+ df.to_csv(filename,index=False)
+ s = HTML(f"DataFrame saved in the file - {filename}\n
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ elif variable!=None:
+ self._python = self._launch_python_kernel()
+ python_kc = self._python["client"]
+ json_string = df.to_json(orient='split') + "\n"
+ escaped_json = repr(json_string)
+ import textwrap
+ escaped_json = repr(json_string)
+ inject_code = textwrap.dedent(f"""
+ import pandas as pd
+ import io
+ {variable} = pd.read_json(io.StringIO({escaped_json}), orient='split')
+ """)
+ msg_id = python_kc.execute(inject_code)
+ # Wait until Python kernel finishes executing
+ while True:
+ msg = python_kc.get_iopub_msg(timeout=5)
+ if msg['parent_header'].get('msg_id') != msg_id:
+ continue
+ if msg['msg_type'] == 'status' and msg['content'].get('execution_state') == 'idle':
+ break
+ s = HTML(f"DataFrame variable '{variable}' stored in the Python Environment
")
+ self.user_ns[variable] = df
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ else:
+ if(self.start_aggrid_dash(df)=="No port Found"):
+ s = HTML(f"AgGrid haven't generated. Please try again
")
+ self.send_response(self.iopub_socket,
+ 'display_data',
+ {
+ 'data': {
+ 'text/html': str(s.data)
+ },
+ 'metadata': {}
+ }
+ )
+ return {
+ 'status': 'error',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+ url = self.start_aggrid_dash(df)
+ html_output = f'''
+
+ '''
+ self.send_response(self.iopub_socket, 'display_data', {
+ 'data': {
+ 'text/html': html_output,
+ 'text/plain': str(df)
+ },
+ 'metadata': {}
+ })
+ return {
+ 'status': 'ok',
+ 'execution_count': self.execution_count,
+ 'payload': [],
+ 'user_expressions': {}
+ }
+
+
+ def do_complete(self, code, cursor_pos):
+ kernel_choice = self._extract_kernel_choice(code)
+ if kernel_choice == "python":
+ code_without_header = self._strip_first_line(code)
+ lines = code.splitlines()
+ adjustment = sum(len(line) + 1 for line in lines[:2]) if (lines and lines[0].lower().startswith("#kernel:")) else 0
+ adjusted_cursor = max(cursor_pos - adjustment, 0)
+ try:
+ if not hasattr(self, "_python"):
+ self._python = self._launch_python_kernel()
+ kc = self._python["client"]
+ except Exception:
+ return {
+ 'status': 'ok',
+ 'matches': [],
+ 'cursor_start': cursor_pos,
+ 'cursor_end': cursor_pos,
+ 'metadata': {},
+ }
+ msg_id = kc.complete(code_without_header, adjusted_cursor)
+ while True:
+ try:
+ msg = kc.get_shell_msg(timeout=5)
+ except Empty:
+ break
+ if msg['parent_header'].get('msg_id') != msg_id:
+ continue
+ if msg['msg_type'] == 'complete_reply':
+ content = msg['content']
+ # Adjust back to original cursor positions
+ content['cursor_start'] += adjustment
+ content['cursor_end'] += adjustment
+ return content
+ return {
+ 'status': 'ok',
+ 'matches': [],
+ 'cursor_start': cursor_pos,
+ 'cursor_end': cursor_pos,
+ 'metadata': {},
+ }
+ else:
+ suggestions = ["load ", "db ", "#>"]
+ if code is None or code.strip() == "":
+ return {
+ 'matches': suggestions,
+ 'cursor_start': 0,
+ 'cursor_end': 0,
+ 'metadata': {},
+ 'status': 'ok'
+ }
+ prefix = code[:cursor_pos]
+ tokens = prefix.strip().split()
+ if tokens and tokens[0] == "load" and prefix.rstrip() == "load":
+ match = "~/"
+ start = len(prefix)
+ return {
+ 'matches': [match],
+ 'cursor_start': start,
+ 'cursor_end': start,
+ 'metadata': {},
+ 'status': 'ok'
+ }
+ if prefix.startswith("load ~/"):
+ path_prefix = prefix[len("load "):]
+ if path_prefix.endswith('/'):
+ dir_path = os.path.expanduser(path_prefix)
+ partial = ''
+ else:
+ dir_path, partial = os.path.split(os.path.expanduser(path_prefix))
+ try:
+ entries = os.listdir(dir_path)
+ except Exception:
+ entries = []
+ matches = [e for e in entries if e.startswith(partial)]
+ matches = [
+ e + '/' if os.path.isdir(os.path.join(dir_path, e)) else e
+ for e in matches
+ ]
+ if not matches:
+ matches = [
+ e + '/' if os.path.isdir(os.path.join(dir_path, e)) else e
+ for e in entries
+ ]
+ completed_file = (
+ len(matches) == 1 and
+ not matches[0].endswith('/') and
+ os.path.isfile(os.path.join(dir_path, matches[0]))
+ )
+ if completed_file:
+ matches = [' local::DuckDuckConnection']
+ cursor_start = cursor_end = cursor_pos
+ else:
+ cursor_start = len("load ") + len(path_prefix) - len(partial)
+ return {
+ 'matches': matches,
+ 'cursor_start': cursor_start,
+ 'cursor_end': cursor_pos,
+ 'metadata': {},
+ 'status': 'ok'
+ }
+ if prefix.endswith("#>"):
+ return {
+ 'matches': ["{local::DuckDuckDatabase."],
+ 'cursor_start': cursor_pos,
+ 'cursor_end': cursor_pos,
+ 'metadata': {},
+ 'status': 'ok'
+ }
+ if prefix.endswith("db "):
+ return {
+ 'matches': ["local::DuckDuckConnection"],
+ 'cursor_start': cursor_pos,
+ 'cursor_end': cursor_pos,
+ 'metadata': {},
+ 'status': 'ok'
+ }
+ if prefix.endswith("]"):
+ return {
+ 'matches': [")"],
+ 'cursor_start': cursor_pos,
+ 'cursor_end': cursor_pos,
+ 'metadata': {},
+ 'status': 'ok'
+ }
+ if prefix.endswith("}#") or prefix.endswith(")"):
+ return {
+ 'matches': ["->"],
+ 'cursor_start': cursor_pos,
+ 'cursor_end': cursor_pos,
+ 'metadata': {},
+ 'status': 'ok'
+ }
+ if prefix.strip().endswith(" ->") or prefix.strip().endswith("->"):
+ return {
+ 'matches': ["filter(", "groupBy(", "select(", "extend(","from(" , "pivot(", "asofjoin(", "join(", "distinct(","rename(", "concatenate(",
+ "sort(","size(","drop("],
+ 'cursor_start': cursor_pos,
+ 'cursor_end': cursor_pos,
+ 'metadata': {},
+ 'status': 'ok'
+ }
+ if prefix.strip().endswith("x|$x."):
+ match = re.search(r"#>\{local::DuckDuckDatabase\.([A-Za-z0-9_]+)}#", prefix)
+ if match:
+ result = match.group(1)
+ p = self.details[result]
+ return {
+ 'matches': p,
+ 'cursor_start': cursor_pos,
+ 'cursor_end': cursor_pos,
+ 'metadata': {},
+ 'status': 'ok'
+ }
+ if prefix.strip().endswith("|"):
+ if(cursor_pos>=2):
+ var = code[cursor_pos-2]
+ return {
+ 'matches': ["$"+var+"."],
+ 'cursor_start': cursor_pos,
+ 'cursor_end': cursor_pos,
+ 'metadata': {},
+ 'status': 'ok'
+ }
+ else:
+ return {
+ 'matches': [],
+ 'cursor_start': cursor_pos,
+ 'cursor_end': cursor_pos,
+ 'metadata': {},
+ 'status': 'ok'
+ }
+ if prefix.strip().endswith("~") or prefix.strip().endswith(",") or prefix.strip().endswith("[") or (prefix.strip().endswith(".") and cursor_pos>=3 and code[cursor_pos-3]=="$"):
+ match = re.search(r"#>\{local::DuckDuckDatabase\.([A-Za-z0-9_]+)}#", prefix)
+ if match:
+ result = match.group(1)
+ p = self.details[result]
+ return {
+ 'matches': p,
+ 'cursor_start': cursor_pos,
+ 'cursor_end': cursor_pos,
+ 'metadata': {},
+ 'status': 'ok'
+ }
+ if prefix.strip().endswith("filter("):
+ return {
+ 'matches': ["x|$x."],
+ 'cursor_start': cursor_pos,
+ 'cursor_end': cursor_pos,
+ 'metadata': {},
+ 'status': 'ok'
+ }
+ if prefix.strip().endswith("from("):
+ return {
+ 'matches': ["local::DuckDuckRuntime)"],
+ 'cursor_start': cursor_pos,
+ 'cursor_end': cursor_pos,
+ 'metadata': {},
+ 'status': 'ok'
+ }
+ if prefix.strip().endswith("--"):
+ return {
+ 'matches': ["file", "var"],
+ 'cursor_start': cursor_pos,
+ 'cursor_end': cursor_pos,
+ 'metadata': {},
+ 'status': 'ok'
+ }
+ if prefix.strip().endswith("select("):
+ return {
+ 'matches': ["~["],
+ 'cursor_start': cursor_pos,
+ 'cursor_end': cursor_pos,
+ 'metadata': {},
+ 'status': 'ok'
+ }
+ if "#>{local::DuckDuckDatabase" in prefix:
+ match = re.search(r"#>\{local::DuckDuckDatabase\.([A-Za-z0-9_]*)$", prefix)
+ if match:
+ typed = match.group(1)
+ matches = [
+ table + "}#"
+ for table in self.tables
+ if table.lower().startswith(typed.lower())
+ ]
+ cursor_start = cursor_pos - len(typed)
+ cursor_end = cursor_pos
+
+ return {
+ 'matches': matches,
+ 'cursor_start': cursor_start,
+ 'cursor_end': cursor_end,
+ 'metadata': {},
+ 'status': 'ok'
+ }
+ matches = [s for s in suggestions if s.startswith(prefix)]
+ # if not matches and cursor_pos==0:
+ # matches = suggestions
+ return {
+ 'matches': matches,
+ 'cursor_start': 0,
+ 'cursor_end': cursor_pos,
+ 'metadata': {},
+ 'status': 'ok'
+ }
+ return {"status": "ok",
+ "matches": [],
+ "cursor_start": cursor_pos,
+ "cursor_end": cursor_pos,
+ "metadata": {}}
+
+
+
+ def do_shutdown(self, restart):
+ if hasattr(self, "_python"):
+ try:
+ self._python["client"].stop_channels()
+ self._python["manager"].shutdown_kernel(now=True)
+ except Exception:
+ pass
+ return super().do_shutdown(restart)
diff --git a/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/kernelspec/kernel.js b/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/kernelspec/kernel.js
new file mode 100644
index 000000000..a1a7106fa
--- /dev/null
+++ b/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/kernelspec/kernel.js
@@ -0,0 +1,89 @@
+define(["codemirror/lib/codemirror"], function(CodeMirror) {
+ "use strict";
+
+ return {
+ onload: function() {
+ console.info("Loading ILegend kernel.js");
+ CodeMirror.defineMode("ilegend", function(config, parserConfig) {
+ return {
+ startState: function() {
+ return {
+ state: "initial",
+ };
+ },
+
+ token: function(stream, state) {
+ switch (state.state) {
+ case "initial":
+ if (stream.match(/^\%/)) {
+ // Line comment -> consume the rest of the line
+ stream.match(/^.+/);
+ return "comment";
+ } else if (stream.match(/^\/\*/)) {
+ // Block comment start -> switch to comment state
+ state.state = "comment";
+ return "comment";
+ } else if (stream.match(/^"(?:[^"])*"/)) {
+ return "string";
+ } else if (stream.match(/^'(?:[^'])*'/)) {
+ return "atom";
+ } else if (stream.match(/^(?:[0-9]+)/)) {
+ return "number";
+ } else if (stream.match(/^(?:!)/)) {
+ return "builtin";
+ } else if (stream.match(/^(?:=:=|:-|@<|@>|@=<|@>=|[-+\\/><=*#$?^])/)) {
+ return "operator";
+ } else if (stream.match(/^[\s()\[\]{},\.|;@]+/)) {
+ return null;
+ } else if (stream.match(/^:+/)) {
+ // ":" needs to be checked on its own
+ // Otherwise, if ":-" occurs after one of the characters above (e.g. ")"), ":" would not be highlighted correctly
+ return null;
+ } else {
+ const atom_or_variable = stream.match(/^[A-Za-z_]+[A-Za-z_0-9]*/);
+ if (atom_or_variable && atom_or_variable.toString() !== "") {
+ var firstCharacter = atom_or_variable.toString().charAt(0);
+ if (firstCharacter !== "_" && firstCharacter === firstCharacter.toLowerCase()) {
+ // If the token starts with a lower case letter, it is an atom
+ if (stream.peek() === "(") {
+ // Atoms which are preceded by "(" are highlighted differently
+ return "builtin";
+ } else {
+ return "atom";
+ }
+ } else {
+ return "variable-2";
+ }
+ } else {
+ // Consume the rest of the line and mark it as an error
+ stream.match(/^.+/);
+ return "error";
+ }
+ }
+
+ case "comment":
+ while (!stream.eol()) {
+ // Consume everything except for "*"
+ stream.match(/^[^\*]+/);
+ if (stream.match(/^\*\//)) {
+ // "*/" -> switch back to initial state
+ state.state = "initial";
+ return "comment";
+ } else {
+ // "*" without "/" -> consume and stay in comment state
+ stream.match(/^\*/);
+ }
+ }
+ return "comment";
+
+ default:
+ throw new Error("Unhandled state: " + state.state);
+ }
+ },
+ };
+ });
+
+ CodeMirror.defineMIME("text/x-ilegend", "ilegend");
+ },
+ };
+});
diff --git a/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/kernelspec/logo-64x64.png b/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/kernelspec/logo-64x64.png
new file mode 100644
index 000000000..8609e0ca0
Binary files /dev/null and b/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/kernelspec/logo-64x64.png differ
diff --git a/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/magics.py b/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/magics.py
new file mode 100644
index 000000000..ce06048a1
--- /dev/null
+++ b/ILegend-JupyterMultiKernel/Kernel/ilegend_kernel/magics.py
@@ -0,0 +1,64 @@
+#Cell Magics
+def magic_uppercase(cell_code, **kwargs):
+ return cell_code.upper()
+
+def magic_lowercase(cell_code, **kwargs):
+ return cell_code.lower()
+
+def evaluate_python(cell_code, **kwargs):
+ import ast
+ import io
+ import contextlib
+ try:
+ tree = ast.parse(cell_code)
+ buf = io.StringIO()
+ with contextlib.redirect_stdout(buf):
+ exec(compile(tree, filename="", mode="exec"))
+ output = buf.getvalue()
+ return f"Python code executed successfully.\n------Output-----\n {output}"
+ except SyntaxError as e:
+ return f"Syntax Error: {str(e)}"
+ except Exception as e:
+ return f"Error executing Python code: {str(e)}"
+
+
+#Line Magics
+def magic_start(line, **kwargs):
+ return f"Hello {line}! Welcome to the Pure Kernel."
+
+def magic_date(line, **kwargs): #If want to add as a line magic
+ import datetime
+ t = datetime.datetime.now()
+ return f"Current date and time: {t.strftime("%Y-%m-%d %H:%M:%S")}"
+
+def load_csv(line, **kwargs):
+ import pandas as pd
+ try:
+ df = pd.read_csv(line)
+ return f"Loaded CSV file {line}"
+ except Exception as e:
+ return f"Error loading CSV: {str(e)}"
+
+def show_stats(line, **kwargs):
+ import pandas as pd
+ try:
+ df = pd.read_csv(line)
+ stats = df.describe().to_string()
+ return f"Statistics for {line}:\n{stats}"
+ except Exception as e:
+ return f"Error showing stats: {str(e)}"
+
+
+# Register magics here
+CELL_MAGICS = {
+ 'uppercase': magic_uppercase,
+ 'lowercase': magic_lowercase,
+ 'python': evaluate_python,
+}
+
+LINE_MAGICS = {
+ 'start': magic_start,
+ 'date': magic_date,
+ 'load_csv':load_csv,
+ 'show_stats':show_stats,
+}
\ No newline at end of file
diff --git a/ILegend-JupyterMultiKernel/Kernel/pyproject.toml b/ILegend-JupyterMultiKernel/Kernel/pyproject.toml
new file mode 100644
index 000000000..50e5ff6f5
--- /dev/null
+++ b/ILegend-JupyterMultiKernel/Kernel/pyproject.toml
@@ -0,0 +1,30 @@
+[build-system]
+build-backend = "setuptools.build_meta"
+requires = [
+ "setuptools>=61.0"
+]
+
+[project]
+name = "ilegend_kernel"
+version = "0.0.9"
+authors = [ { name="Shanmukh raj MSS"} ]
+description = "Extensible Jupyter kernel for ILegend"
+readme = "README.md"
+license = { file="LICENSE" }
+requires-python = ">=3.7"
+classifiers = [
+ "Programming Language :: Python :: 3",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: OS Independent",
+ "Intended Audience :: Developers",
+]
+dependencies = [
+ "jupyter_client",
+ "IPython",
+ "ipykernel",
+ "graphviz",
+ "beautifulsoup4",
+]
+
+[project.urls]
+"Homepage" = "https://github.com/ShanmukhEstrella/Legend-Jupyter"
diff --git a/ILegend-JupyterMultiKernel/Kernel/setup.cfg b/ILegend-JupyterMultiKernel/Kernel/setup.cfg
new file mode 100644
index 000000000..3d2836c03
--- /dev/null
+++ b/ILegend-JupyterMultiKernel/Kernel/setup.cfg
@@ -0,0 +1,10 @@
+[options]
+packages = find_namespace:
+include_package_data = True
+
+[options.packages.find]
+exclude =
+ notebooks*
+
+[options.package_data]
+* = *.js, *.json, *.lgd
diff --git a/ILegend-JupyterMultiKernel/KernelSwitch-UI/README.md b/ILegend-JupyterMultiKernel/KernelSwitch-UI/README.md
new file mode 100644
index 000000000..cd3c93d8b
--- /dev/null
+++ b/ILegend-JupyterMultiKernel/KernelSwitch-UI/README.md
@@ -0,0 +1,15 @@
+### Requirements
+- Jupyter Lab >= 4.x.x
+
+
+### Installation
+- Clone the repository.
+- Run the commands:
+ ```
+ npm install
+ npm run build
+ jupyter labextension install .
+ jupyter lab build
+ ```
+
+
diff --git a/ILegend-JupyterMultiKernel/KernelSwitch-UI/package.json b/ILegend-JupyterMultiKernel/KernelSwitch-UI/package.json
new file mode 100644
index 000000000..cabe6871e
--- /dev/null
+++ b/ILegend-JupyterMultiKernel/KernelSwitch-UI/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "cell-kernel-selector",
+ "version": "0.1.0",
+ "description": "Adds a kernel dropdown to every code cell",
+ "main": "lib/index.js",
+ "scripts": {
+ "build": "tsc",
+ "watch": "tsc -w"
+ },
+ "keywords": ["jupyterlab-extension"],
+ "jupyterlab": {
+ "extension": true
+ },
+ "dependencies": {
+ "@jupyterlab/application": "^4.0.0",
+ "@jupyterlab/notebook": "^4.0.0",
+ "@lumino/widgets": "^2.0.0"
+ },
+ "devDependencies": {
+ "typescript": "~5.0.0"
+ }
+}
diff --git a/ILegend-JupyterMultiKernel/KernelSwitch-UI/src/index.ts b/ILegend-JupyterMultiKernel/KernelSwitch-UI/src/index.ts
new file mode 100644
index 000000000..a315ffdc9
--- /dev/null
+++ b/ILegend-JupyterMultiKernel/KernelSwitch-UI/src/index.ts
@@ -0,0 +1,202 @@
+/**
+ * Inline kernel selector for ILegend notebooks
+ * -------------------------------------------
+ * Adds a dropdown to every code cell so users can switch between the
+ * ILegend kernel and a Python helper kernel.
+ */
+
+import {
+ JupyterFrontEnd,
+ JupyterFrontEndPlugin
+} from '@jupyterlab/application';
+import {
+ INotebookTracker,
+ NotebookPanel
+} from '@jupyterlab/notebook';
+import { CodeCell } from '@jupyterlab/cells';
+
+/* ------------------------------------------------------------------ */
+/* helpers */
+/* ------------------------------------------------------------------ */
+
+const LEGEND_MIME = 'text/x-ilegend';
+const ILegendDisplayName = 'ILegend'; // ← adjust if needed
+
+const hasDropdown = (cell: CodeCell) =>
+ !!cell.node.querySelector('.cell-kernel-selector-wrapper');
+
+/** Is the notebook’s *current* kernel ILegend? */
+const isILegendKernel = (panel: NotebookPanel): boolean =>
+ (panel.sessionContext.kernelDisplayName ?? '').toLowerCase() ===
+ ILegendDisplayName.toLowerCase();
+
+/* ------------------------------------------------------------------ */
+/* extension */
+/* ------------------------------------------------------------------ */
+
+const extension: JupyterFrontEndPlugin = {
+ id: 'cell-kernel-selector',
+ autoStart: true,
+ requires: [INotebookTracker],
+ activate: (_app, tracker) => {
+ console.log('Inline Kernel Selector Activated');
+
+ /* ---------- one‑time font & CSS ----------------------------- */
+ (() => {
+ if (document.getElementById('cell‑kernel‑selector‑css')) return;
+
+ const font = Object.assign(document.createElement('link'), {
+ rel: 'stylesheet',
+ href: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap'
+ });
+ const style = Object.assign(document.createElement('style'), {
+ id: 'cell‑kernel‑selector‑css',
+ textContent: /* css */ `
+ select.cell-kernel-selector-dropdown option[value="Python"]{
+ color:rgb(37,235,37);font-weight:600;
+ }
+ select.cell-kernel-selector-dropdown option[value="Legend"]{
+ color:rgb(21,17,224);font-weight:600;
+ }
+ .jp-InputArea-editor.has-kernel-dropdown{
+ position:relative!important;padding-top:32px!important;
+ }
+ .cell-kernel-selector-wrapper{
+ position:absolute;top:6px;left:8px;z-index:20;
+ background:var(--jp-layout-color2);
+ border:1px solid var(--jp-border-color2);
+ padding:2px 6px;border-radius:4px;
+ display:flex;gap:4px;font:500 11px 'Inter',sans-serif;
+ }
+ select.cell-kernel-selector-dropdown{
+ padding:2px 6px;font:11px 'Inter',sans-serif;
+ border:1px solid var(--jp-border-color2);
+ background:var(--jp-layout-color0);max-width:120px;cursor:pointer;
+ }
+ `
+ });
+ document.head.append(font, style);
+ })();
+
+ /* ---------- dropdown logic (your original code) ------------- */
+ const choices = ['-- Select Kernel --', 'Python', 'Legend'] as const;
+ const pythonHdr =
+ "#Code in Python below. Don't Remove this Header!!";
+
+ const addDropdown = (cell: CodeCell): void => {
+ if (hasDropdown(cell)) return;
+
+ const host = cell.node.querySelector('.jp-InputArea-editor');
+ if (!host) return;
+
+ const sel = document.createElement('select');
+ sel.className = 'cell-kernel-selector-dropdown';
+ choices.forEach(n => {
+ const opt = document.createElement('option');
+ opt.text = n;
+ opt.value = n === '-- Select Kernel --' ? '' : n;
+ sel.appendChild(opt);
+ });
+
+ let prog = false;
+ const apply = (k: 'Python' | 'Legend') => {
+ const srcLines = cell.model.sharedModel.source
+ .split('\n')
+ .filter(l => !l.trim().startsWith('#Kernel:') && l !== pythonHdr);
+ const newLines =
+ k === 'Python'
+ ? ['#Kernel: Python', pythonHdr, ...srcLines]
+ : [...srcLines];
+
+ const newSrc = newLines.join('\n');
+ if (newSrc === cell.model.sharedModel.source) return;
+
+ prog = true;
+ cell.model.sharedModel.source = newSrc;
+ cell.model.mimeType =
+ k === 'Python' ? 'text/x-python' : LEGEND_MIME;
+ prog = false;
+ cell.editor?.setCursorPosition({
+ line: k === 'Python' ? 2 : 0,
+ column: 0
+ });
+ };
+
+ const sync = () => {
+ if (prog) return;
+ const first = cell.model.sharedModel.source.split('\n')[0]?.toLowerCase();
+ sel.value = first === '#kernel: python' ? 'Python' : 'Legend';
+ };
+
+ sel.onchange = () => apply((sel.value || 'Legend') as any);
+ cell.model.contentChanged.connect(sync);
+
+ const wrap = document.createElement('div');
+ wrap.className = 'cell-kernel-selector-wrapper';
+ wrap.innerHTML = '';
+ wrap.appendChild(sel);
+
+ host.appendChild(wrap);
+ host.classList.add('has-kernel-dropdown');
+ sync();
+ };
+
+ const inject = (panel: NotebookPanel) =>
+ panel.content.widgets.forEach(w => {
+ if (w.model.type === 'code') addDropdown(w as CodeCell);
+ });
+
+ const removeAll = (panel: NotebookPanel) =>
+ panel.content.widgets.forEach(w => {
+ if (w.model.type !== 'code') return;
+ w.node
+ .querySelector('.cell-kernel-selector-wrapper')
+ ?.remove();
+ w.node
+ .querySelector('.jp-InputArea-editor')
+ ?.classList.remove('has-kernel-dropdown');
+ });
+
+ /* ---------- tracker hook ----------------------------------- */
+ tracker.widgetAdded.connect((_t: INotebookTracker, panel: NotebookPanel) => {
+ const refresh = () => {
+ if (isILegendKernel(panel)) {
+ inject(panel);
+ } else {
+ removeAll(panel);
+ }
+ };
+
+ /* run once notebook is ready */
+ panel.context.ready.then(refresh);
+
+ /* run on kernel switches */
+ panel.sessionContext.kernelChanged.connect(refresh);
+
+ /* add dropdown to new cells only if ILegend kernel active */
+ panel.content.model?.cells.changed.connect(
+ (_list: any, ch: import('@jupyterlab/observables').IObservableList.IChangedArgs) => {
+ if (!isILegendKernel(panel)) return;
+ ch.newValues?.forEach(m => {
+ if (m.type !== 'code') return;
+ const v = panel.content.widgets.find(
+ (w: import('@jupyterlab/cells').Cell) => w.model === m
+ ) as CodeCell | undefined;
+ v?.ready.then(() =>
+ requestAnimationFrame(() => addDropdown(v))
+ );
+ });
+ }
+ );
+
+ /* ensure pasted / active cell also handled */
+ panel.content.activeCellChanged.connect(() => {
+ if (!isILegendKernel(panel)) return;
+ const c = panel.content.activeCell;
+ if (c?.model.type === 'code') addDropdown(c as CodeCell);
+ });
+ });
+ }
+};
+
+export default extension;
diff --git a/ILegend-JupyterMultiKernel/KernelSwitch-UI/style/index.css b/ILegend-JupyterMultiKernel/KernelSwitch-UI/style/index.css
new file mode 100644
index 000000000..234900b73
--- /dev/null
+++ b/ILegend-JupyterMultiKernel/KernelSwitch-UI/style/index.css
@@ -0,0 +1,5 @@
+.cell-kernel-selector {
+ margin-left: 8px;
+ font-size: 12px;
+ padding: 2px;
+}
diff --git a/ILegend-JupyterMultiKernel/KernelSwitch-UI/tsconfig.json b/ILegend-JupyterMultiKernel/KernelSwitch-UI/tsconfig.json
new file mode 100644
index 000000000..42841dd97
--- /dev/null
+++ b/ILegend-JupyterMultiKernel/KernelSwitch-UI/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "compilerOptions": {
+ "target": "es6",
+ "module": "es6",
+ "lib": ["es2020", "dom"],
+ "moduleResolution": "node",
+ "outDir": "lib",
+ "rootDir": "src",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true
+ },
+ "include": ["src/**/*"]
+}
diff --git a/ILegend-JupyterMultiKernel/README.md b/ILegend-JupyterMultiKernel/README.md
new file mode 100644
index 000000000..5ac849a4f
--- /dev/null
+++ b/ILegend-JupyterMultiKernel/README.md
@@ -0,0 +1,59 @@
+### DESCRIPTION
+- ILegend - Jupyter Kernel for Legend
+
+
+### FEATURES
+- Users can query data using ILegend Kernel by using legend language in jupyter notebooks.
+- It is Python implementation of Legend REPL.
+- Users can load local csv files and operate on them in a similar fashion as remote data sources.
+- Users can get full benefit of legend database pushdown for queries being executed.
+- Integrated IPython Kernel features into ILegend Kernel, giving capability to users to switch between languages in the same notebook session.
+- Query results from legend notebook cells will be avaialbe as pandas dataframes in subsequent python cells to continue further data analysis.
+- Syntax highlighting and auto-completion are availabe for both python and legend code within notebooks. Both light and dark theme are supported.
+
+
+### REQUIREMENTS
+- JupyterLab >= 4.0.0
+
+
+### USER INSTALLATION
+For ILegend-Kernel to install in your virtual environment, follow the steps:
+- Activate your virtual environment
+- ```
+ pip install ilegend-kernel
+ python -m ilegend_kernel.install
+ ```
+For KernelSwitch-UI lab extenstion to install, follow the steps:
+- ```
+ pip install ilegend-cell-ui
+ jupyter lab build
+ ```
+For Legend-SyntaxHighlighting lab extenstion to install, follow the steps:
+- ```
+ pip install ilegend-syntax-coloring
+ jupyter lab build
+ ```
+
+
+To uninstall ILegend Kernel:
+- ```
+ pip uninstall ilegend-kernel
+ jupyter kernelspec remove ilegend-kernel
+ ```
+
+
+
+
+Now you can see the features below!!
+You can start using the ILegend Kernel now!!
+
+
+### HOME INTERFACE
+
+
+
+### NOTEBOOK INTERFACE
+
+
+
+
diff --git a/ILegend-JupyterMultiKernel/Server/DataServer.java b/ILegend-JupyterMultiKernel/Server/DataServer.java
new file mode 100644
index 000000000..399623928
--- /dev/null
+++ b/ILegend-JupyterMultiKernel/Server/DataServer.java
@@ -0,0 +1,314 @@
+package org.finos.legend.pylegend;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.sun.net.httpserver.Authenticator;
+import org.eclipse.collections.api.RichIterable;
+import org.finos.legend.engine.language.pure.compiler.toPureGraph.PureModel;
+import org.finos.legend.engine.language.pure.grammar.from.RelationalGrammarParserExtension;
+import org.finos.legend.engine.language.pure.grammar.to.PureGrammarComposer;
+import org.finos.legend.engine.language.pure.grammar.to.PureGrammarComposerContext;
+import org.finos.legend.engine.plan.execution.api.result.ResultManager;
+import org.finos.legend.engine.plan.execution.result.ConstantResult;
+import org.finos.legend.engine.plan.execution.result.Result;
+import org.finos.legend.engine.plan.execution.result.serialization.SerializationFormat;
+import org.finos.legend.engine.plan.execution.stores.relational.result.RelationalResult;
+import org.finos.legend.engine.plan.generation.PlanGenerator;
+import org.finos.legend.engine.plan.generation.transformers.LegendPlanTransformers;
+import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContextData;
+import org.finos.legend.engine.protocol.pure.v1.model.executionPlan.SingleExecutionPlan;
+import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.store.relational.model.Database;
+import org.finos.legend.engine.pure.code.core.PureCoreExtensionLoader;
+import org.finos.legend.engine.shared.core.identity.Identity;
+import org.finos.legend.engine.shared.core.kerberos.SubjectTools;
+import org.finos.legend.engine.shared.core.operational.logs.LoggingEventType;
+import org.finos.legend.pure.generated.Root_meta_pure_executionPlan_ExecutionPlan;
+import org.finos.legend.pure.generated.Root_meta_pure_extension_Extension;
+import org.jline.reader.Candidate;
+import org.jline.reader.LineReader;
+import org.eclipse.collections.api.factory.Lists;
+import org.eclipse.collections.api.factory.Maps;
+import org.finos.legend.engine.plan.execution.PlanExecutor;
+import org.finos.legend.engine.plan.execution.stores.relational.config.RelationalExecutionConfiguration;
+import org.finos.legend.engine.plan.execution.stores.relational.config.TemporaryTestDbConfiguration;
+import org.finos.legend.engine.plan.execution.stores.relational.connection.authentication.strategy.OAuthProfile;
+import org.finos.legend.engine.plan.execution.stores.relational.connection.manager.ConnectionManagerSelector;
+import org.finos.legend.engine.plan.execution.stores.relational.plugin.Relational;
+import org.finos.legend.engine.plan.execution.stores.relational.plugin.RelationalStoreExecutor;
+import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.store.relational.connection.RelationalDatabaseConnection;
+import java.util.Collections;
+import org.finos.legend.engine.repl.client.jline3.JLine3Completer;
+import org.finos.legend.engine.repl.client.jline3.JLine3Parser;
+import org.finos.legend.engine.repl.core.legend.LegendInterface;
+import org.finos.legend.engine.repl.core.legend.LocalLegendInterface;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.*;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.util.*;
+import org.finos.legend.engine.repl.client.Client;
+import java.sql.*;
+import org.finos.legend.engine.repl.dataCube.DataCubeReplExtension;
+import org.finos.legend.engine.repl.relational.RelationalReplExtension;
+import org.finos.legend.engine.repl.relational.autocomplete.RelationalCompleterExtension;
+import org.finos.legend.engine.repl.relational.shared.ConnectionHelper;
+import org.finos.legend.engine.shared.core.ObjectMapperFactory;
+import org.jline.reader.LineReaderBuilder;
+import java.sql.Statement;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+
+
+
+@Path("/server")
+public class DataServer
+{
+ private static final ObjectMapper OBJECT_MAPPER = ObjectMapperFactory.getNewStandardObjectMapperWithPureProtocolExtensionSupports();
+ private static final LegendInterface REPL_INTERFACE = new LocalLegendInterface();
+ private static PlanExecutor PLAN_EXECUTOR;
+ private static ConnectionManagerSelector CONNECTION_MANAGER;
+ private static Client CLIENT;
+ private static JLine3Parser PARSER ;
+ private static JLine3Completer COMPLETER ;
+ private static LineReader LINE_READER;
+ private static final Map MACROS = new HashMap<>();
+
+ private static PlanExecutor buildPlanExecutor()
+ {
+ RelationalExecutionConfiguration relationalExecutionConfiguration = RelationalExecutionConfiguration.newInstance()
+ .withTempPath(System.getProperty("TMP","/tmp/"))
+ .withTemporaryTestDbConfiguration(new TemporaryTestDbConfiguration(9095))
+ .build();
+ RelationalStoreExecutor relationalStoreExecutor = (RelationalStoreExecutor) Relational.build(relationalExecutionConfiguration);
+ CONNECTION_MANAGER = relationalStoreExecutor.getStoreState().getRelationalExecutor().getConnectionManager();
+ return PlanExecutor.newPlanExecutorBuilder().withStoreExecutors(relationalStoreExecutor).build();
+
+ }
+ private static Result executeLine(String txt)
+ {
+ String expanded = expandMacros(txt);
+ String code = "###Pure\n" +
+ "function repl::__internal__::run():Any[*]\n{\n" + expanded + ";\n}";
+ PureModelContextData d = CLIENT.getModelState().parseWithTransient(code);
+ PureModel pureModel = REPL_INTERFACE.compile(d);
+ RichIterable extends Root_meta_pure_extension_Extension> extensions = PureCoreExtensionLoader.extensions().flatCollect(e->e.extraPureCoreExtensions(pureModel.getExecutionSupport()));
+
+ Root_meta_pure_executionPlan_ExecutionPlan plan = REPL_INTERFACE.generatePlan(pureModel,false);
+ String planStr = PlanGenerator.serializeToJSON(plan,"vX_X_X",pureModel,extensions, LegendPlanTransformers.transformers);
+
+ Result res = PLAN_EXECUTOR.execute((SingleExecutionPlan) PlanExecutor.readExecutionPlan(planStr),Maps.mutable.empty(),"user", Identity.getAnonymousIdentity());
+ if(res instanceof RelationalResult || res instanceof ConstantResult)
+ {
+ return res;
+ }
+ throw new RuntimeException(res.getClass() + "result type not supported");
+ }
+ private static final Pattern MACRO_PATTERN = Pattern.compile("macro\\{(.*?)\\}");
+ private static String expandMacros(String code)
+ {
+ Matcher matcher = MACRO_PATTERN.matcher(code);
+ StringBuffer result = new StringBuffer();
+ while (matcher.find())
+ {
+ String macroKey = matcher.group(1);
+ String replacement = MACROS.getOrDefault(macroKey, "macro{" + macroKey + "}");
+ matcher.appendReplacement(result, Matcher.quoteReplacement(replacement));
+ }
+ matcher.appendTail(result);
+ return result.toString();
+ }
+
+ @POST
+ @Path("/execute")
+ public Response execute(@Context HttpServletRequest request, @Context HttpServletResponse response) throws Exception
+ {
+ RelationalReplExtension relationalReplExtension = new RelationalReplExtension();
+ PLAN_EXECUTOR = buildPlanExecutor();
+ CLIENT = new Client(Lists.mutable.with(relationalReplExtension,new DataCubeReplExtension()), Lists.mutable.with(new RelationalCompleterExtension()),PLAN_EXECUTOR);
+// CLIENT.commands.add(0,new LoadProjectCommand(CLIENT));
+ PARSER = new JLine3Parser();
+ COMPLETER = new JLine3Completer(CLIENT.commands);
+ LINE_READER = (LineReader) LineReaderBuilder.builder().parser(PARSER).completer(COMPLETER).build();
+ try
+ {
+ Map payload = OBJECT_MAPPER.readValue(request.getInputStream(), new TypeReference