I can add tables
Refactoring DbEngine Fixing unit tests Fixing unit tests Fixing unit tests Refactored DbManager for datagrid Improving front end performance I can add new table Fixed sidebar closing when clicking on it Fix drag event rebinding, improve listener options, and add debug Prevent duplicate drag event bindings with a dataset flag and ensure consistent scrollbar functionality. Change wheel event listener to passive mode for better performance. Refactor function naming for consistency, and add debug logs for event handling. Refactor Datagrid bindings and default state handling. Updated Javascript to conditionally rebind Datagrid on specific events. Improved Python components by handling empty DataFrame cases and removing redundant code. Revised default state initialization in settings for better handling of mutable fields. Added Rowindex visualisation support Working on Debugger with own implementation of JsonViewer Working on JsonViewer.py Fixed unit tests Adding unit tests I can fold and unfold fixed unit tests Adding css for debugger Added tooltip management Adding debugger functionalities Refactor serializers and improve error handling in DB engine Fixed error where tables were overwritten I can display footer menu Working on footer. Refactoring how heights are managed Refactored scrollbars management Working on footer menu I can display footer menu + fixed unit tests Fixed unit tests Updated click management I can display aggregations in footers Added docker management Refactor input handling and improve config defaults Fixed scrollbars colors Refactored tooltip management Improved tooltip management Improving FilterAll
This commit is contained in:
@@ -1,52 +0,0 @@
|
||||
import logging
|
||||
|
||||
from fasthtml.fastapp import fast_app
|
||||
|
||||
from components.addstuff.components.Repositories import Repositories
|
||||
from components.addstuff.constants import Routes
|
||||
from core.instance_manager import InstanceManager, debug_session
|
||||
|
||||
logger = logging.getLogger("AddStuffApp")
|
||||
|
||||
add_stuff_app, rt = fast_app()
|
||||
|
||||
|
||||
@rt(Routes.AddRepository)
|
||||
def get(session):
|
||||
_id = Repositories.create_component_id(session) # there is only one instance of Repositories
|
||||
instance = InstanceManager.get(session, _id)
|
||||
return instance.request_new_repository()
|
||||
|
||||
|
||||
@rt(Routes.AddRepository)
|
||||
def post(session, _id: str, tab_id: str, form_id: str, repository: str, table: str):
|
||||
logger.debug(
|
||||
f"Entering {Routes.AddRepository} with args {debug_session(session)}, {_id=}, {tab_id=}, {form_id=}, {repository=}, {table=}")
|
||||
instance = InstanceManager.get(session, _id) # Repository
|
||||
return instance.add_new_repository(tab_id, form_id, repository, table)
|
||||
|
||||
|
||||
@rt(Routes.AddTable)
|
||||
def get(session, _id: str, repository_name: str):
|
||||
instance = InstanceManager.get(session, _id)
|
||||
return instance.request_new_table(repository_name)
|
||||
|
||||
|
||||
@rt(Routes.AddTable)
|
||||
def post(session, _id: str, tab_id: str, form_id: str, repository_name: str, table_name: str):
|
||||
instance = InstanceManager.get(session, _id)
|
||||
return instance.add_new_table(tab_id, form_id, repository_name, table_name)
|
||||
|
||||
|
||||
@rt(Routes.SelectRepository)
|
||||
def put(session, _id: str, repository: str):
|
||||
logger.debug(f"Entering {Routes.SelectRepository} with args {debug_session(session)}, {_id=}, {repository=}")
|
||||
instance = InstanceManager.get(session, _id)
|
||||
return instance.select_repository(repository)
|
||||
|
||||
|
||||
@rt(Routes.ShowTable)
|
||||
def get(session, _id: str, repository: str, table: str):
|
||||
logger.debug(f"Entering {Routes.ShowTable} with args {debug_session(session)}, {_id=}, {repository=}, {table=}")
|
||||
instance = InstanceManager.get(session, _id)
|
||||
return instance.show_table(repository, table)
|
||||
@@ -1,3 +0,0 @@
|
||||
function bindRepositories(repositoryId) {
|
||||
bindTooltipsWithDelegation(repositoryId)
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
from fastcore.basics import NotStr
|
||||
|
||||
# Fluent Database20Regular
|
||||
icon_database = NotStr("""
|
||||
<svg name="database" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 20 20">
|
||||
<g fill="none">
|
||||
<path d="M4 5c0-1.007.875-1.755 1.904-2.223C6.978 2.289 8.427 2 10 2s3.022.289 4.096.777C15.125 3.245 16 3.993 16 5v10c0 1.007-.875 1.755-1.904 2.223C13.022 17.71 11.573 18 10 18s-3.022-.289-4.096-.777C4.875 16.755 4 16.007 4 15V5zm1 0c0 .374.356.875 1.318 1.313C7.234 6.729 8.536 7 10 7s2.766-.27 3.682-.687C14.644 5.875 15 5.373 15 5c0-.374-.356-.875-1.318-1.313C12.766 3.271 11.464 3 10 3s-2.766.27-3.682.687C5.356 4.125 5 4.627 5 5zm10 1.698a4.92 4.92 0 0 1-.904.525C13.022 7.711 11.573 8 10 8s-3.022-.289-4.096-.777A4.92 4.92 0 0 1 5 6.698V15c0 .374.356.875 1.318 1.313c.916.416 2.218.687 3.682.687s2.766-.27 3.682-.687C14.644 15.875 15 15.373 15 15V6.698z" fill="currentColor">
|
||||
</path>
|
||||
</g>
|
||||
</svg>
|
||||
""")
|
||||
|
||||
# Fluent Table20Regular
|
||||
icon_table = NotStr("""
|
||||
<svg name="table" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 20 20">
|
||||
<g fill="none">
|
||||
<path d="M17 5.5A2.5 2.5 0 0 0 14.5 3h-9A2.5 2.5 0 0 0 3 5.5v9A2.5 2.5 0 0 0 5.5 17h9a2.5 2.5 0 0 0 2.5-2.5v-9zm-13 9V13h3v3H5.5l-.144-.007A1.5 1.5 0 0 1 4 14.5zm8-1.5v3H8v-3h4zm2.5 3H13v-3h3v1.5l-.007.145A1.5 1.5 0 0 1 14.5 16zM12 8v4H8V8h4zm1 0h3v4h-3V8zm-1-4v3H8V4h4zm1 0h1.5l.145.007A1.5 1.5 0 0 1 16 5.5V7h-3V4zM7 4v3H4V5.5l.007-.144A1.5 1.5 0 0 1 5.5 4H7zm0 4v4H4V8h3z" fill="currentColor">
|
||||
</path>
|
||||
</g>
|
||||
</svg>
|
||||
""")
|
||||
@@ -1,23 +0,0 @@
|
||||
from components.addstuff.constants import ROUTE_ROOT, Routes
|
||||
|
||||
|
||||
class Commands:
|
||||
def __init__(self, owner):
|
||||
self._owner = owner
|
||||
self._id = owner.get_id()
|
||||
|
||||
def request_add_table(self, repository_name):
|
||||
return {
|
||||
"hx-get": f"{ROUTE_ROOT}{Routes.AddTable}",
|
||||
"hx-target": f"#{self._owner.tabs_manager.get_id()}",
|
||||
"hx-swap": "outerHTML",
|
||||
"hx-vals": f'{{"_id": "{self._id}", "repository_name": "{repository_name}"}}',
|
||||
}
|
||||
|
||||
def add_table(self):
|
||||
return {
|
||||
"hx-post": f"{ROUTE_ROOT}{Routes.AddTable}",
|
||||
"hx-target": f"#{self._owner.tabs_manager.get_id()}",
|
||||
"hx-swap": "outerHTML",
|
||||
# The repository_name and the table_name will be given by the form
|
||||
}
|
||||
@@ -1,26 +1,22 @@
|
||||
from fasthtml.components import *
|
||||
|
||||
from components.BaseComponent import BaseComponent
|
||||
from components.addstuff.constants import ADD_STUFF_INSTANCE_ID, ROUTE_ROOT, Routes
|
||||
from components.addstuff.settings import RepositoriesDbManager
|
||||
from components.addstuff.constants import ADD_STUFF_INSTANCE_ID
|
||||
from components.repositories.components.Repositories import Repositories
|
||||
from core.instance_manager import InstanceManager
|
||||
|
||||
|
||||
class AddStuffMenu(BaseComponent):
|
||||
def __init__(self, session: dict, _id: str, settings_manager=None, tabs_manager=None):
|
||||
super().__init__(session, _id)
|
||||
self.tabs_manager = tabs_manager # MyTabs component id
|
||||
self.mappings = {} # to keep track of when element is displayed on which tab
|
||||
self.settings = RepositoriesDbManager(session, settings_manager)
|
||||
self.repositories = InstanceManager.get(session, Repositories.create_component_id(session), Repositories)
|
||||
|
||||
def __ft__(self):
|
||||
return Div(
|
||||
Div("Add stuff...", tabindex="0"),
|
||||
Ul(
|
||||
Li(A("Add Database",
|
||||
hx_get=f"{ROUTE_ROOT}{Routes.AddRepository}",
|
||||
hx_target=f"#{self.tabs_manager.get_id()}",
|
||||
hx_swap="outerHTML",
|
||||
)),
|
||||
Li(A("Add Database", **self.repositories.commands.request_add_repository())),
|
||||
Li(A("Add Application")),
|
||||
tabindex="0",
|
||||
cls="menu menu-sm dropdown-content bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm"
|
||||
|
||||
@@ -1,209 +0,0 @@
|
||||
import logging
|
||||
|
||||
from fasthtml.components import *
|
||||
from fasthtml.xtend import Script
|
||||
|
||||
from components.BaseComponent import BaseComponent
|
||||
from components.addstuff.assets.icons import icon_database, icon_table
|
||||
from components.addstuff.commands import Commands
|
||||
from components.addstuff.constants import REPOSITORIES_INSTANCE_ID, ROUTE_ROOT, Routes
|
||||
from components.addstuff.settings import RepositoriesDbManager, Repository
|
||||
from components.datagrid_new.components.DataGrid import DataGrid
|
||||
from components.form.components.MyForm import MyForm, FormField
|
||||
from components_helpers import mk_icon, mk_ellipsis, mk_tooltip_container
|
||||
from core.instance_manager import InstanceManager
|
||||
|
||||
logger = logging.getLogger("Repositories")
|
||||
|
||||
|
||||
class Repositories(BaseComponent):
|
||||
def __init__(self, session: dict, _id: str, settings_manager=None, tabs_manager=None):
|
||||
super().__init__(session, _id)
|
||||
self._settings_manager = settings_manager
|
||||
self.db = RepositoriesDbManager(session, settings_manager)
|
||||
self.tabs_manager = tabs_manager
|
||||
self._contents = {} # ket tracks of already displayed contents
|
||||
self._commands = Commands(self)
|
||||
|
||||
def request_new_repository(self):
|
||||
# request for a new tab_id
|
||||
new_tab_id = self.tabs_manager.request_new_tab_id()
|
||||
|
||||
# create a new form to ask for the details of the new database
|
||||
add_repository_form = self._mk_add_repository_form(new_tab_id)
|
||||
|
||||
# create and display the form in a new tab
|
||||
self.tabs_manager.add_tab("Add Database", add_repository_form, tab_id=new_tab_id)
|
||||
return self.tabs_manager
|
||||
|
||||
def request_new_table(self, repository_name: str):
|
||||
# request for a new tab_id
|
||||
new_tab_id = self.tabs_manager.request_new_tab_id()
|
||||
|
||||
# create a new form to ask for the details of the new database
|
||||
add_table_form = self._mk_add_table_form(new_tab_id, repository_name)
|
||||
|
||||
# create and display the form in a new tab
|
||||
self.tabs_manager.add_tab("Add New Table", add_table_form, tab_id=new_tab_id)
|
||||
return self.tabs_manager
|
||||
|
||||
def add_new_repository(self, tab_id: str, form_id: str, repository_name: str, table_name: str):
|
||||
"""
|
||||
|
||||
:param tab_id: tab id where the table content will be displayed (and where the form was displayed)
|
||||
:param form_id: form used to give the repository name (to be used in case of error)
|
||||
:param repository_name: new repository name
|
||||
:param table_name: default table name
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
# Add the new repository and its default table to the list of repositories
|
||||
tables = [MyTable(table_name, {})] if table_name else []
|
||||
repository = self.db.add_repository(repository_name, tables)
|
||||
|
||||
# update the tab content with table content
|
||||
key = (repository_name, table_name)
|
||||
self.tabs_manager.set_tab_content(tab_id,
|
||||
self._get_table_content(key),
|
||||
title=table_name,
|
||||
key=key,
|
||||
active=True)
|
||||
|
||||
return self._mk_repository(repository, True), self.tabs_manager.refresh()
|
||||
|
||||
except ValueError as ex:
|
||||
logger.debug(f" Repository '{repository_name}' already exists.")
|
||||
add_repository_form = InstanceManager.get(self._session, form_id)
|
||||
add_repository_form.set_error(ex)
|
||||
|
||||
return self.tabs_manager.refresh()
|
||||
|
||||
def add_new_table(self, tab_id: str, form_id: str, repository_name: str, table_name: str):
|
||||
"""
|
||||
|
||||
:param tab_id: tab id where the table content will be displayed (and where the form was displayed)
|
||||
:param form_id: form used to give the repository name (to be used in case of error)
|
||||
:param repository_name: new repository name
|
||||
:param table_name: default table name
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
self.db.add_table(repository_name, table_name, {})
|
||||
repository = self.db.get_repository(repository_name)
|
||||
# update the tab content with table content
|
||||
key = (repository_name, table_name)
|
||||
self.tabs_manager.set_tab_content(tab_id,
|
||||
self._get_table_content(key),
|
||||
title=table_name,
|
||||
key=key,
|
||||
active=True)
|
||||
|
||||
return self._mk_repository(repository, True), self.tabs_manager.refresh()
|
||||
|
||||
except ValueError as ex:
|
||||
logger.debug(f" Repository '{repository_name}' already exists.")
|
||||
add_repository_form = InstanceManager.get(self._session, form_id)
|
||||
add_repository_form.set_error(ex)
|
||||
|
||||
return self.tabs_manager.refresh()
|
||||
|
||||
def select_repository(self, repository_name: str):
|
||||
self.db.select_repository(repository_name)
|
||||
|
||||
def show_table(self, repository_name: str, table_name: str):
|
||||
key = (repository_name, table_name)
|
||||
self.tabs_manager.add_tab(table_name, self._get_table_content(key), key)
|
||||
return self.tabs_manager
|
||||
|
||||
def refresh(self):
|
||||
return self._mk_repositories(oob=True)
|
||||
|
||||
def __ft__(self):
|
||||
return Div(
|
||||
mk_tooltip_container(self._id),
|
||||
Div(cls="divider"),
|
||||
mk_ellipsis("Repositories", cls="text-sm font-medium mb-1"),
|
||||
self._mk_repositories(),
|
||||
Script(f"bindRepositories('{self._id}')")
|
||||
)
|
||||
|
||||
def _mk_repositories(self, oob=False):
|
||||
settings = self.db._get_settings()
|
||||
return Div(
|
||||
*[self._mk_repository(repo, repo.name == settings.selected_repository_name)
|
||||
for repo in settings.repositories],
|
||||
id=self._id,
|
||||
hx_swap_oob="true" if oob else None,
|
||||
)
|
||||
|
||||
def _mk_repository(self, repo: Repository, selected):
|
||||
return Div(
|
||||
Input(type="radio",
|
||||
name=f"repo-accordion-{self._id}",
|
||||
checked="checked" if selected else None,
|
||||
cls="p-0! min-h-0!",
|
||||
hx_put=f"{ROUTE_ROOT}{Routes.SelectRepository}",
|
||||
hx_vals=f'{{"_id": "{self._id}", "repository": "{repo.name}"}}',
|
||||
# hx_trigger="changed delay:500ms",
|
||||
),
|
||||
Div(
|
||||
mk_icon(icon_database, can_select=False), mk_ellipsis(repo.name),
|
||||
cls="collapse-title p-0 min-h-0 flex truncate",
|
||||
),
|
||||
Div(
|
||||
*[
|
||||
Div(mk_icon(icon_table, can_select=False), mk_ellipsis(table.name),
|
||||
name="repo-table",
|
||||
hx_get=f"{ROUTE_ROOT}{Routes.ShowTable}",
|
||||
hx_target=f"#{self.tabs_manager.get_id()}",
|
||||
hx_swap="outerHTML",
|
||||
hx_vals=f'{{"_id": "{self._id}", "repository": "{repo.name}", "table": "{table.name}"}}',
|
||||
cls="flex")
|
||||
for table in repo.tables
|
||||
],
|
||||
Div("+ Add Table", **self._commands.request_add_table(repo.name)),
|
||||
cls="collapse-content pr-0! truncate",
|
||||
),
|
||||
tabindex="0", cls="collapse mb-2")
|
||||
|
||||
def _mk_add_repository_form(self, tab_id: str):
|
||||
htmx_params = {
|
||||
"hx-post": f"{ROUTE_ROOT}{Routes.AddRepository}",
|
||||
"hx-target": f"#{self._id}",
|
||||
"hx-swap": "beforeend",
|
||||
}
|
||||
return InstanceManager.get(self._session, MyForm.create_component_id(self._session), MyForm,
|
||||
title="Add Repository",
|
||||
fields=[FormField("repository", 'Repository Name', 'input'),
|
||||
FormField("table", 'First Table Name', 'input')],
|
||||
htmx_params=htmx_params,
|
||||
extra_values={"_id": self._id, "tab_id": tab_id})
|
||||
|
||||
def _mk_add_table_form(self, tab_id: str, repository_name: str = None):
|
||||
htmx_request = self._commands.add_table()
|
||||
return InstanceManager.get(self._session, MyForm.create_component_id(self._session), MyForm,
|
||||
title="Add Table",
|
||||
fields=[FormField("repository_name", 'Repository Name', 'input',
|
||||
value=repository_name,
|
||||
disabled=True),
|
||||
FormField("table_name", 'Table Name', 'input')],
|
||||
htmx_request=htmx_request,
|
||||
extra_values={"_id": self._id, "tab_id": tab_id, "repository_name": repository_name})
|
||||
|
||||
def _get_table_content(self, key):
|
||||
|
||||
if key in self._contents:
|
||||
return self._contents[key]
|
||||
|
||||
dg = InstanceManager.get(self._session,
|
||||
DataGrid.create_component_id(self._session),
|
||||
DataGrid,
|
||||
settings_manager=self._settings_manager,
|
||||
key=key)
|
||||
|
||||
self._contents[key] = dg
|
||||
return dg
|
||||
|
||||
@staticmethod
|
||||
def create_component_id(session):
|
||||
return f"{REPOSITORIES_INSTANCE_ID}{session['user_id']}"
|
||||
@@ -1,10 +1 @@
|
||||
ADD_STUFF_INSTANCE_ID = "__AddStuff__"
|
||||
ADD_DATABASE_INSTANCE_ID = "__AddDatabase__"
|
||||
REPOSITORIES_INSTANCE_ID = "__Repositories__"
|
||||
ROUTE_ROOT = "/add"
|
||||
|
||||
class Routes:
|
||||
AddRepository = "/add-repository"
|
||||
SelectRepository = "/select-repository"
|
||||
AddTable = "/add-table"
|
||||
ShowTable = "/show-table"
|
||||
@@ -1,112 +0,0 @@
|
||||
import dataclasses
|
||||
import logging
|
||||
|
||||
from core.settings_management import SettingsManager
|
||||
from core.settings_objects import BaseSettingObj
|
||||
|
||||
ADD_STUFF_SETTINGS_ENTRY = "AddStuffSettings"
|
||||
REPOSITORIES_SETTINGS_ENTRY = "Repositories"
|
||||
|
||||
logger = logging.getLogger("AddStuffSettings")
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Repository:
|
||||
name: str
|
||||
tables: list[str]
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class RepositoriesSettings:
|
||||
repositories: list[Repository] = dataclasses.field(default_factory=list)
|
||||
selected_repository_name: str = None
|
||||
|
||||
|
||||
class RepositoriesDbManager:
|
||||
|
||||
def __init__(self, session: dict, settings_manager: SettingsManager):
|
||||
self.session = session
|
||||
self.settings_manager = settings_manager
|
||||
|
||||
def _get_settings(self):
|
||||
return self.settings_manager.get(self.session, REPOSITORIES_SETTINGS_ENTRY, default=RepositoriesSettings())
|
||||
|
||||
def add_repository(self, repository_name: str, tables: list[str] = None):
|
||||
"""
|
||||
Adds a new repository to the list of repositories. The repository is identified
|
||||
by its name and can optionally include a list of associated tables.
|
||||
|
||||
:param repository_name: The name of the repository to be added.
|
||||
:param tables: A list of Table objects to be associated with the repository,
|
||||
defaulting to an empty list if not provided.
|
||||
:type tables: list[Table], optional
|
||||
:return: None
|
||||
"""
|
||||
|
||||
settings = self._get_settings()
|
||||
|
||||
if repository_name is None or repository_name == "":
|
||||
raise ValueError("Repository name cannot be empty.")
|
||||
|
||||
if repository_name in [repo.name for repo in settings.repositories]:
|
||||
raise ValueError(f"Repository '{repository_name}' already exists.")
|
||||
|
||||
existing_repositories = [r.name for r in settings.repositories]
|
||||
logger.info(f"Existing repositories:{existing_repositories}")
|
||||
|
||||
repository = Repository(repository_name, tables or [])
|
||||
settings.repositories.append(repository)
|
||||
self.settings_manager.put(self.session, REPOSITORIES_SETTINGS_ENTRY, settings)
|
||||
return repository
|
||||
|
||||
def get_repository(self, repository_name: str):
|
||||
if repository_name is None or repository_name == "":
|
||||
raise ValueError("Repository name cannot be empty.")
|
||||
|
||||
settings = self._get_settings()
|
||||
if repository_name not in [repo.name for repo in settings.repositories]:
|
||||
raise ValueError(f"Repository '{repository_name}' does not exists.")
|
||||
|
||||
return next(filter(lambda r: r.name == repository_name, settings.repositories))
|
||||
|
||||
def modify_repository(self, repository_name: str, tables: list[str]):
|
||||
repository = self.get_repository(repository_name)
|
||||
|
||||
|
||||
def get_repositories(self):
|
||||
return self._get_settings().repositories
|
||||
|
||||
def add_table(self, repository_name: str, table_name: str, table_settings: dict):
|
||||
"""
|
||||
Adds a table to the specified repository
|
||||
|
||||
:param repository_name: The name of the target repository.
|
||||
:param table_name: The name of the table to add.
|
||||
:param table_settings: A dictionary containing the settings or configuration details
|
||||
of the table.
|
||||
:return: None
|
||||
"""
|
||||
settings = self._get_settings()
|
||||
|
||||
repository = next(filter(lambda r: r.name == repository_name, settings.repositories), None)
|
||||
if repository is None:
|
||||
raise ValueError(f"Repository '{repository_name}' does not exists.")
|
||||
|
||||
if table_name in (t.name for t in repository.tables):
|
||||
raise ValueError(f"Table '{table_name}' already exists.")
|
||||
|
||||
repository.tables.append(MyTable(table_name, table_settings))
|
||||
self.settings_manager.put(self.session, ADD_STUFF_SETTINGS_ENTRY, settings)
|
||||
|
||||
def select_repository(self, repository_name: str):
|
||||
"""
|
||||
Select and save the specified repository name in the current session's settings.
|
||||
|
||||
:param repository_name: The name of the repository to be selected and stored.
|
||||
:type repository_name: str
|
||||
:return: None
|
||||
"""
|
||||
settings = self._get_settings()
|
||||
settings.selected_repository_name = repository_name
|
||||
self.settings_manager.put(self.session, ADD_STUFF_SETTINGS_ENTRY, settings)
|
||||
|
||||
Reference in New Issue
Block a user