I can load and save Jira and Table Processor details
This commit is contained in:
@@ -41,6 +41,7 @@ def post(session, _id: str, component_type: str, x: int, y: int):
|
|||||||
instance = InstanceManager.get(session, _id)
|
instance = InstanceManager.get(session, _id)
|
||||||
return instance.add_component(component_type, x, y)
|
return instance.add_component(component_type, x, y)
|
||||||
|
|
||||||
|
|
||||||
@rt(Routes.MoveComponent)
|
@rt(Routes.MoveComponent)
|
||||||
def post(session, _id: str, component_id: str, x: int, y: int):
|
def post(session, _id: str, component_id: str, x: int, y: int):
|
||||||
logger.debug(
|
logger.debug(
|
||||||
@@ -56,6 +57,7 @@ def post(session, _id: str, component_id: str):
|
|||||||
instance = InstanceManager.get(session, _id)
|
instance = InstanceManager.get(session, _id)
|
||||||
return instance.delete_component(component_id)
|
return instance.delete_component(component_id)
|
||||||
|
|
||||||
|
|
||||||
@rt(Routes.AddConnection)
|
@rt(Routes.AddConnection)
|
||||||
def post(session, _id: str, from_id: str, to_id: str):
|
def post(session, _id: str, from_id: str, to_id: str):
|
||||||
logger.debug(
|
logger.debug(
|
||||||
@@ -63,9 +65,55 @@ def post(session, _id: str, from_id: str, to_id: str):
|
|||||||
instance = InstanceManager.get(session, _id)
|
instance = InstanceManager.get(session, _id)
|
||||||
return instance.add_connection(from_id, to_id)
|
return instance.add_connection(from_id, to_id)
|
||||||
|
|
||||||
|
|
||||||
@rt(Routes.ResizeDesigner)
|
@rt(Routes.ResizeDesigner)
|
||||||
def post(session, _id: str, designer_height: int):
|
def post(session, _id: str, designer_height: int):
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"Entering {Routes.ResizeDesigner} with args {debug_session(session)}, {_id=}, {designer_height=}")
|
f"Entering {Routes.ResizeDesigner} with args {debug_session(session)}, {_id=}, {designer_height=}")
|
||||||
instance = InstanceManager.get(session, _id)
|
instance = InstanceManager.get(session, _id)
|
||||||
return instance.set_designer_height(designer_height)
|
return instance.set_designer_height(designer_height)
|
||||||
|
|
||||||
|
|
||||||
|
@rt(Routes.SelectComponent)
|
||||||
|
def post(session, _id: str, component_id: str):
|
||||||
|
logger.debug(
|
||||||
|
f"Entering {Routes.SelectComponent} with args {debug_session(session)}, {_id=}, {component_id=}")
|
||||||
|
instance = InstanceManager.get(session, _id)
|
||||||
|
return instance.select_component(component_id)
|
||||||
|
|
||||||
|
|
||||||
|
@rt(Routes.SaveProperties)
|
||||||
|
def post(session, _id: str, component_id: str, details: dict):
|
||||||
|
logger.debug(
|
||||||
|
f"Entering {Routes.SaveProperties} with args {debug_session(session)}, {_id=}, {component_id=}, {details=}")
|
||||||
|
instance = InstanceManager.get(session, _id)
|
||||||
|
details.pop("_id")
|
||||||
|
details.pop("component_id")
|
||||||
|
return instance.save_properties(component_id, details)
|
||||||
|
|
||||||
|
|
||||||
|
@rt(Routes.CancelProperties)
|
||||||
|
def post(session, _id: str, component_id: str):
|
||||||
|
logger.debug(
|
||||||
|
f"Entering {Routes.CancelProperties} with args {debug_session(session)}, {_id=}, {component_id=}")
|
||||||
|
instance = InstanceManager.get(session, _id)
|
||||||
|
return instance.cancel_properties(component_id)
|
||||||
|
|
||||||
|
|
||||||
|
@rt(Routes.SelectProcessor)
|
||||||
|
def post(session, _id: str, component_id: str, processor_name: str):
|
||||||
|
logger.debug(
|
||||||
|
f"Entering {Routes.SelectProcessor} with args {debug_session(session)}, {_id=}, {component_id=}, {processor_name=}")
|
||||||
|
instance = InstanceManager.get(session, _id)
|
||||||
|
return instance.set_selected_processor(component_id, processor_name)
|
||||||
|
|
||||||
|
|
||||||
|
@rt(Routes.OnProcessorDetailsEvent)
|
||||||
|
def post(session, _id: str, component_id: str, event_name: str, details: dict):
|
||||||
|
logger.debug(
|
||||||
|
f"Entering {Routes.OnProcessorDetailsEvent} with args {debug_session(session)}, {_id=}, {component_id=}, {event_name=}, {details=}")
|
||||||
|
instance = InstanceManager.get(session, _id)
|
||||||
|
details.pop("_id")
|
||||||
|
details.pop("component_id")
|
||||||
|
details.pop("event_name")
|
||||||
|
return instance.on_processor_details_event(component_id, event_name, details)
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
.wkf-toolbox-item {
|
.wkf-toolbox-item {
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
color: var(--color-neutral);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wkf-toolbox-item:active {
|
.wkf-toolbox-item:active {
|
||||||
@@ -74,7 +73,6 @@
|
|||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
height: 64px;
|
height: 64px;
|
||||||
color: var(--color-neutral);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wkf-workflow-component:hover {
|
.wkf-workflow-component:hover {
|
||||||
|
|||||||
@@ -217,10 +217,10 @@ function bindWorkflowDesignerToolbox(elementId) {
|
|||||||
if (!component) return;
|
if (!component) return;
|
||||||
|
|
||||||
componentId = component.dataset.componentId
|
componentId = component.dataset.componentId
|
||||||
htmx.ajax('POST', '/workflows/select-connection', {
|
htmx.ajax('POST', '/workflows/select-component', {
|
||||||
target: `#c_${elementId}`,
|
target: `#p_${elementId}`,
|
||||||
headers: {"Content-Type": "application/x-www-form-urlencoded"},
|
headers: {"Content-Type": "application/x-www-form-urlencoded"},
|
||||||
swap: "innerHTML",
|
swap: "outerHTML",
|
||||||
values: {
|
values: {
|
||||||
_id: elementId,
|
_id: elementId,
|
||||||
component_id: componentId
|
component_id: componentId
|
||||||
|
|||||||
@@ -28,3 +28,42 @@ class WorkflowsCommandManager(BaseCommandManager):
|
|||||||
"hx-swap": "outerHTML",
|
"hx-swap": "outerHTML",
|
||||||
"hx-vals": f'js:{{"_id": "{self._id}", "name": "{workflow_name}", "tab_boundaries": getTabContentBoundaries("{self._owner.tabs_manager.get_id()}")}}',
|
"hx-vals": f'js:{{"_id": "{self._id}", "name": "{workflow_name}", "tab_boundaries": getTabContentBoundaries("{self._owner.tabs_manager.get_id()}")}}',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class WorkflowDesignerCommandManager(BaseCommandManager):
|
||||||
|
def __init__(self, owner):
|
||||||
|
super().__init__(owner)
|
||||||
|
|
||||||
|
def select_processor(self, component_id :str):
|
||||||
|
return {
|
||||||
|
"hx_post": f"{ROUTE_ROOT}{Routes.SelectProcessor}",
|
||||||
|
"hx-target": f"#p_{self._id}",
|
||||||
|
"hx-swap": "outerHTML",
|
||||||
|
"hx-trigger": "change",
|
||||||
|
"hx-vals": f'js:{{"_id": "{self._id}", "component_id": "{component_id}"}}',
|
||||||
|
}
|
||||||
|
|
||||||
|
def save_properties(self, component_id: str):
|
||||||
|
return {
|
||||||
|
"hx_post": f"{ROUTE_ROOT}{Routes.SaveProperties}",
|
||||||
|
"hx-target": f"#p_{self._id}",
|
||||||
|
"hx-swap": "outerHTML",
|
||||||
|
"hx-vals": f'js:{{"_id": "{self._id}", "component_id": "{component_id}"}}',
|
||||||
|
}
|
||||||
|
|
||||||
|
def cancel_properties(self, component_id: str):
|
||||||
|
return {
|
||||||
|
"hx_post": f"{ROUTE_ROOT}{Routes.CancelProperties}",
|
||||||
|
"hx-target": f"#p_{self._id}",
|
||||||
|
"hx-swap": "outerHTML",
|
||||||
|
"hx-vals": f'js:{{"_id": "{self._id}", "component_id": "{component_id}"}}',
|
||||||
|
}
|
||||||
|
|
||||||
|
def on_processor_details_event(self, component_id: str, event_name: str):
|
||||||
|
return {
|
||||||
|
"hx_post": f"{ROUTE_ROOT}{Routes.OnProcessorDetailsEvent}",
|
||||||
|
"hx-target": f"#p_{self._id}",
|
||||||
|
"hx-trigger": "change",
|
||||||
|
"hx-swap": "outerHTML",
|
||||||
|
"hx-vals": f'js:{{"_id": "{self._id}", "component_id": "{component_id}", "event_name": "{event_name}"}}',
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
from fastcore.basics import NotStr
|
from fastcore.basics import NotStr
|
||||||
from fasthtml.components import *
|
from fasthtml.components import *
|
||||||
from fasthtml.xtend import Script
|
from fasthtml.xtend import Script
|
||||||
|
|
||||||
from components.BaseComponent import BaseComponent
|
from components.BaseComponent import BaseComponent
|
||||||
|
from components.workflows.commands import WorkflowDesignerCommandManager
|
||||||
from components.workflows.constants import WORKFLOW_DESIGNER_INSTANCE_ID
|
from components.workflows.constants import WORKFLOW_DESIGNER_INSTANCE_ID
|
||||||
from components.workflows.db_management import WorkflowsDesignerSettings, WorkflowComponent, \
|
from components.workflows.db_management import WorkflowsDesignerSettings, WorkflowComponent, \
|
||||||
Connection, WorkflowsDesignerDbManager
|
Connection, WorkflowsDesignerDbManager
|
||||||
from components_helpers import apply_boundaries, mk_tooltip
|
from components_helpers import apply_boundaries, mk_tooltip, mk_dialog_buttons
|
||||||
from core.utils import get_unique_id
|
from core.utils import get_unique_id, make_safe_id
|
||||||
|
from utils.DbManagementHelper import DbManagementHelper
|
||||||
|
|
||||||
|
logger = logging.getLogger("WorkflowDesigner")
|
||||||
|
|
||||||
# Component templates
|
# Component templates
|
||||||
COMPONENT_TYPES = {
|
COMPONENT_TYPES = {
|
||||||
@@ -15,22 +21,27 @@ COMPONENT_TYPES = {
|
|||||||
"title": "Data Producer",
|
"title": "Data Producer",
|
||||||
"description": "Generates or loads data",
|
"description": "Generates or loads data",
|
||||||
"icon": "📊",
|
"icon": "📊",
|
||||||
"color": "bg-green-100 border-green-300"
|
"color": "bg-green-100 border-green-300 text-neutral"
|
||||||
},
|
},
|
||||||
"filter": {
|
"filter": {
|
||||||
"title": "Data Filter",
|
"title": "Data Filter",
|
||||||
"description": "Filters and transforms data",
|
"description": "Filters and transforms data",
|
||||||
"icon": "🔍",
|
"icon": "🔍",
|
||||||
"color": "bg-blue-100 border-blue-300"
|
"color": "bg-blue-100 border-blue-300 text-neutral"
|
||||||
},
|
},
|
||||||
"presenter": {
|
"presenter": {
|
||||||
"title": "Data Presenter",
|
"title": "Data Presenter",
|
||||||
"description": "Displays or exports data",
|
"description": "Displays or exports data",
|
||||||
"icon": "📋",
|
"icon": "📋",
|
||||||
"color": "bg-purple-100 border-purple-300"
|
"color": "bg-purple-100 border-purple-300 text-neutral"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PROCESSOR_TYPES = {
|
||||||
|
"producer": ["Repository", "Jira"],
|
||||||
|
"filter": ["Default"],
|
||||||
|
"presenter": ["Default"]}
|
||||||
|
|
||||||
|
|
||||||
class WorkflowDesigner(BaseComponent):
|
class WorkflowDesigner(BaseComponent):
|
||||||
def __init__(self, session,
|
def __init__(self, session,
|
||||||
@@ -46,13 +57,17 @@ class WorkflowDesigner(BaseComponent):
|
|||||||
self._db = WorkflowsDesignerDbManager(session, settings_manager)
|
self._db = WorkflowsDesignerDbManager(session, settings_manager)
|
||||||
self._state = self._db.load_state(key)
|
self._state = self._db.load_state(key)
|
||||||
self._boundaries = boundaries
|
self._boundaries = boundaries
|
||||||
|
self.commands = WorkflowDesignerCommandManager(self)
|
||||||
|
|
||||||
def set_boundaries(self, boundaries: dict):
|
def set_boundaries(self, boundaries: dict):
|
||||||
self._boundaries = boundaries
|
self._boundaries = boundaries
|
||||||
|
|
||||||
def refresh(self, oob=False):
|
def refresh_designer(self):
|
||||||
return self._mk_elements()
|
return self._mk_elements()
|
||||||
|
|
||||||
|
def refresh_properties(self):
|
||||||
|
return self._mk_properties()
|
||||||
|
|
||||||
def add_component(self, component_type, x, y):
|
def add_component(self, component_type, x, y):
|
||||||
self._state.component_counter += 1
|
self._state.component_counter += 1
|
||||||
|
|
||||||
@@ -70,7 +85,7 @@ class WorkflowDesigner(BaseComponent):
|
|||||||
|
|
||||||
self._state.components[component_id] = component
|
self._state.components[component_id] = component
|
||||||
self._db.save_state(self._key, self._state) # update db
|
self._db.save_state(self._key, self._state) # update db
|
||||||
return self.refresh()
|
return self.refresh_designer()
|
||||||
|
|
||||||
def move_component(self, component_id, x, y):
|
def move_component(self, component_id, x, y):
|
||||||
if component_id in self._state.components:
|
if component_id in self._state.components:
|
||||||
@@ -78,7 +93,7 @@ class WorkflowDesigner(BaseComponent):
|
|||||||
self._state.components[component_id].y = int(y)
|
self._state.components[component_id].y = int(y)
|
||||||
self._db.save_state(self._key, self._state) # update db
|
self._db.save_state(self._key, self._state) # update db
|
||||||
|
|
||||||
return self.refresh()
|
return self.refresh_designer()
|
||||||
|
|
||||||
def delete_component(self, component_id):
|
def delete_component(self, component_id):
|
||||||
# Remove component
|
# Remove component
|
||||||
@@ -91,13 +106,13 @@ class WorkflowDesigner(BaseComponent):
|
|||||||
# update db
|
# update db
|
||||||
self._db.save_state(self._key, self._state)
|
self._db.save_state(self._key, self._state)
|
||||||
|
|
||||||
return self.refresh()
|
return self.refresh_designer()
|
||||||
|
|
||||||
def add_connection(self, from_id, to_id):
|
def add_connection(self, from_id, to_id):
|
||||||
# Check if connection already exists
|
# Check if connection already exists
|
||||||
for connection in self._state.connections:
|
for connection in self._state.connections:
|
||||||
if connection.from_id == from_id and connection.to_id == to_id:
|
if connection.from_id == from_id and connection.to_id == to_id:
|
||||||
return self.refresh() # , self.error_message("Connection already exists")
|
return self.refresh_designer() # , self.error_message("Connection already exists")
|
||||||
|
|
||||||
connection_id = f"conn_{len(self._state.connections) + 1}"
|
connection_id = f"conn_{len(self._state.connections) + 1}"
|
||||||
connection = Connection(id=connection_id, from_id=from_id, to_id=to_id)
|
connection = Connection(id=connection_id, from_id=from_id, to_id=to_id)
|
||||||
@@ -106,12 +121,48 @@ class WorkflowDesigner(BaseComponent):
|
|||||||
# update db
|
# update db
|
||||||
self._db.save_state(self._key, self._state)
|
self._db.save_state(self._key, self._state)
|
||||||
|
|
||||||
return self.refresh()
|
return self.refresh_designer()
|
||||||
|
|
||||||
def set_designer_height(self, height):
|
def set_designer_height(self, height):
|
||||||
self._state.designer_height = height
|
self._state.designer_height = height
|
||||||
self._db.save_state(self._key, self._state)
|
self._db.save_state(self._key, self._state)
|
||||||
return self.__ft__() # refresh the whole component
|
return self.__ft__() # refresh the whole component
|
||||||
|
|
||||||
|
def select_component(self, component_id):
|
||||||
|
if component_id in self._state.components:
|
||||||
|
self._state.selected_component_id = component_id
|
||||||
|
self._db.save_state(self._key, self._state)
|
||||||
|
|
||||||
|
return self.refresh_properties()
|
||||||
|
|
||||||
|
def save_properties(self, component_id: str, details: dict):
|
||||||
|
if component_id in self._state.components:
|
||||||
|
component = self._state.components[component_id]
|
||||||
|
component.properties = details
|
||||||
|
self._db.save_state(self._key, self._state)
|
||||||
|
logger.debug(f"Saved properties for component {component_id}: {details}")
|
||||||
|
|
||||||
|
return self.refresh_properties()
|
||||||
|
|
||||||
|
def cancel_properties(self, component_id: str):
|
||||||
|
if component_id in self._state.components:
|
||||||
|
logger.debug(f"Cancel saving properties for component {component_id}")
|
||||||
|
|
||||||
|
return self.refresh_properties()
|
||||||
|
|
||||||
|
def set_selected_processor(self, component_id: str, processor_name: str):
|
||||||
|
self._state.selected_processor_name[component_id] = processor_name
|
||||||
|
self._db.save_state(self._key, self._state)
|
||||||
|
return self.refresh_properties()
|
||||||
|
|
||||||
|
def on_processor_details_event(self, component_id: str, event_name: str, details: dict):
|
||||||
|
if component_id in self._state.components:
|
||||||
|
component = self._state.components[component_id]
|
||||||
|
if event_name == "OnRepositoryChanged":
|
||||||
|
component.properties["repository"] = details["repository"]
|
||||||
|
component.properties["table"] = None
|
||||||
|
|
||||||
|
return self.refresh_properties()
|
||||||
|
|
||||||
def __ft__(self):
|
def __ft__(self):
|
||||||
return Div(
|
return Div(
|
||||||
@@ -125,56 +176,6 @@ class WorkflowDesigner(BaseComponent):
|
|||||||
id=f"{self._id}",
|
id=f"{self._id}",
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def create_component_id(session, suffix=None):
|
|
||||||
prefix = f"{WORKFLOW_DESIGNER_INSTANCE_ID}{session['user_id']}"
|
|
||||||
if suffix is None:
|
|
||||||
suffix = get_unique_id()
|
|
||||||
|
|
||||||
return f"{prefix}{suffix}"
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _mk_toolbox_item(component_type: str, info: dict):
|
|
||||||
return Div(
|
|
||||||
mk_tooltip(
|
|
||||||
Div(
|
|
||||||
Span(info["icon"], cls="mb-2"),
|
|
||||||
H4(info["title"], cls="font-semibold text-xs"),
|
|
||||||
cls=f"p-2 rounded-lg border-2 {info['color']} flex text-center"
|
|
||||||
),
|
|
||||||
tooltip=info["description"]),
|
|
||||||
cls="wkf-toolbox-item p-2",
|
|
||||||
draggable="true",
|
|
||||||
data_type=component_type
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _mk_workflow_component(component: WorkflowComponent):
|
|
||||||
info = COMPONENT_TYPES[component.type]
|
|
||||||
return Div(
|
|
||||||
# Input connection point
|
|
||||||
Div(cls="wkf-connection-point wkf-input-point",
|
|
||||||
data_component_id=component.id,
|
|
||||||
data_point_type="input"),
|
|
||||||
|
|
||||||
# Component content
|
|
||||||
Div(
|
|
||||||
Span(info["icon"], cls="text-xl mb-1"),
|
|
||||||
H4(component.title, cls="font-semibold text-xs"),
|
|
||||||
cls=f"p-3 rounded-lg border-2 {info['color']} bg-white shadow-lg flex items-center"
|
|
||||||
),
|
|
||||||
|
|
||||||
# Output connection point
|
|
||||||
Div(cls="wkf-connection-point wkf-output-point",
|
|
||||||
data_component_id=component.id,
|
|
||||||
data_point_type="output"),
|
|
||||||
|
|
||||||
cls="wkf-workflow-component w-32",
|
|
||||||
style=f"left: {component.x}px; top: {component.y}px;",
|
|
||||||
data_component_id=component.id,
|
|
||||||
draggable="true"
|
|
||||||
)
|
|
||||||
|
|
||||||
def _mk_connection_svg(self, conn: Connection):
|
def _mk_connection_svg(self, conn: Connection):
|
||||||
if conn.from_id not in self._state.components or conn.to_id not in self._state.components:
|
if conn.from_id not in self._state.components or conn.to_id not in self._state.components:
|
||||||
return ""
|
return ""
|
||||||
@@ -241,22 +242,158 @@ class WorkflowDesigner(BaseComponent):
|
|||||||
style=f"height:{self._state.designer_height}px;"
|
style=f"height:{self._state.designer_height}px;"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _mk_processor_properties(self, component, processor_name):
|
||||||
|
if processor_name == "Jira":
|
||||||
|
return self._mk_jira_processor_details(component)
|
||||||
|
elif processor_name == "Repository":
|
||||||
|
return self._mk_repository_processor_details(component)
|
||||||
|
|
||||||
|
return Div('Not defined yet !')
|
||||||
|
|
||||||
|
def _mk_properties_details(self):
|
||||||
|
def _mk_header():
|
||||||
|
return Div(
|
||||||
|
Div(
|
||||||
|
Span(icon),
|
||||||
|
H4(component.title, cls="font-semibold text-xs"),
|
||||||
|
cls=f"rounded-lg border-2 {color} flex text-center px-2"
|
||||||
|
),
|
||||||
|
H1(component_id, cls="ml-4"),
|
||||||
|
cls="flex mb-2"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _mk_select():
|
||||||
|
return Select(
|
||||||
|
*[Option(processor_name, selected="selected" if processor_name == selected_processor_name else None)
|
||||||
|
for processor_name in PROCESSOR_TYPES[component.type]],
|
||||||
|
cls="select select-sm w-64 mb-2",
|
||||||
|
id="processor_name",
|
||||||
|
name="processor_name",
|
||||||
|
**self.commands.select_processor(component_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
if self._state.selected_component_id is None or self._state.selected_component_id not in self._state.components:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
component_id = self._state.selected_component_id
|
||||||
|
component = self._state.components[component_id]
|
||||||
|
selected_processor_name = self._state.selected_processor_name.get(component_id, None)
|
||||||
|
icon = COMPONENT_TYPES[component.type]["icon"]
|
||||||
|
color = COMPONENT_TYPES[component.type]["color"]
|
||||||
|
return Form(
|
||||||
|
_mk_header(),
|
||||||
|
_mk_select(),
|
||||||
|
self._mk_processor_properties(component, selected_processor_name),
|
||||||
|
mk_dialog_buttons(cls="mt-4",
|
||||||
|
on_ok=self.commands.save_properties(component_id),
|
||||||
|
on_cancel=self.commands.cancel_properties(component_id)),
|
||||||
|
cls="font-mono text-sm",
|
||||||
|
)
|
||||||
|
|
||||||
def _mk_properties(self):
|
def _mk_properties(self):
|
||||||
return Div(
|
return Div(
|
||||||
Div(
|
self._mk_properties_details(),
|
||||||
H4("Properties"),
|
|
||||||
Div(
|
|
||||||
P("Workflow name:", cls="text-sm"),
|
|
||||||
Div(self._designer_settings.workflow_name, cls="text-base"),
|
|
||||||
cls="flex flex-col gap-2"
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
cls="p-2 bg-base-100 rounded-lg border",
|
cls="p-2 bg-base-100 rounded-lg border",
|
||||||
style=f"height:{self._get_properties_height()}px;",
|
style=f"height:{self._get_properties_height()}px;",
|
||||||
id=f"p_{self._id}",
|
id=f"p_{self._id}",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _mk_jira_processor_details(component):
|
||||||
|
return Div(
|
||||||
|
Fieldset(
|
||||||
|
Legend("JQL", cls="fieldset-legend"),
|
||||||
|
Input(type="text",
|
||||||
|
name="jira_jql",
|
||||||
|
value=component.properties.get("jira_jql", ""),
|
||||||
|
placeholder="Enter JQL",
|
||||||
|
cls="input w-full"),
|
||||||
|
P("Write your jsl code"),
|
||||||
|
cls="fieldset bg-base-200 border-base-300 rounded-box border p-4"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def _mk_repository_processor_details(self, component):
|
||||||
|
selected_repo = component.properties.get("repository", None)
|
||||||
|
selected_table = component.properties.get("table", None)
|
||||||
|
|
||||||
|
return Div(
|
||||||
|
Fieldset(
|
||||||
|
Legend("Repository", cls="fieldset-legend"),
|
||||||
|
Div(
|
||||||
|
Select(
|
||||||
|
*[Option(repo.name, selected="selected" if repo.name == selected_repo else None)
|
||||||
|
for repo in DbManagementHelper.list_repositories(self._session)],
|
||||||
|
cls="select w-64",
|
||||||
|
id=f"repository_{self._id}",
|
||||||
|
name="repository",
|
||||||
|
**self.commands.on_processor_details_event(component.id, "OnRepositoryChanged"),
|
||||||
|
),
|
||||||
|
Select(
|
||||||
|
*[Option(table, selected="selected" if table == selected_table else None)
|
||||||
|
for table in DbManagementHelper.list_tables(self._session, selected_repo)],
|
||||||
|
cls="select w-64 ml-4",
|
||||||
|
id=f"table_{self._id}",
|
||||||
|
name="table",
|
||||||
|
),
|
||||||
|
cls="flex",
|
||||||
|
),
|
||||||
|
P("Select the source table"),
|
||||||
|
cls="fieldset bg-base-200 border-base-300 rounded-box border p-4"
|
||||||
|
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def _get_properties_height(self):
|
def _get_properties_height(self):
|
||||||
print(f"height: {self._boundaries['height']}")
|
print(f"height: {self._boundaries['height']}")
|
||||||
return self._boundaries["height"] - self._state.designer_height - 86
|
return self._boundaries["height"] - self._state.designer_height - 86
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_component_id(session, suffix=None):
|
||||||
|
prefix = f"{WORKFLOW_DESIGNER_INSTANCE_ID}{session['user_id']}"
|
||||||
|
if suffix is None:
|
||||||
|
suffix = get_unique_id()
|
||||||
|
|
||||||
|
return make_safe_id(f"{prefix}{suffix}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _mk_toolbox_item(component_type: str, info: dict):
|
||||||
|
return Div(
|
||||||
|
mk_tooltip(
|
||||||
|
Div(
|
||||||
|
Span(info["icon"], cls="mb-2"),
|
||||||
|
H4(info["title"], cls="font-semibold text-xs"),
|
||||||
|
cls=f"p-2 rounded-lg border-2 {info['color']} flex text-center"
|
||||||
|
),
|
||||||
|
tooltip=info["description"]),
|
||||||
|
cls="wkf-toolbox-item p-2",
|
||||||
|
draggable="true",
|
||||||
|
data_type=component_type
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _mk_workflow_component(component: WorkflowComponent):
|
||||||
|
info = COMPONENT_TYPES[component.type]
|
||||||
|
return Div(
|
||||||
|
# Input connection point
|
||||||
|
Div(cls="wkf-connection-point wkf-input-point",
|
||||||
|
data_component_id=component.id,
|
||||||
|
data_point_type="input"),
|
||||||
|
|
||||||
|
# Component content
|
||||||
|
Div(
|
||||||
|
Span(info["icon"], cls="text-xl mb-1"),
|
||||||
|
H4(component.title, cls="font-semibold text-xs"),
|
||||||
|
cls=f"p-3 rounded-lg border-2 {info['color']} bg-white shadow-lg flex items-center"
|
||||||
|
),
|
||||||
|
|
||||||
|
# Output connection point
|
||||||
|
Div(cls="wkf-connection-point wkf-output-point",
|
||||||
|
data_component_id=component.id,
|
||||||
|
data_point_type="output"),
|
||||||
|
|
||||||
|
cls="wkf-workflow-component w-32",
|
||||||
|
style=f"left: {component.x}px; top: {component.y}px;",
|
||||||
|
data_component_id=component.id,
|
||||||
|
draggable="true"
|
||||||
|
)
|
||||||
|
|||||||
@@ -17,4 +17,8 @@ class Routes:
|
|||||||
MoveComponent = "/move-component"
|
MoveComponent = "/move-component"
|
||||||
DeleteComponent = "/delete-component"
|
DeleteComponent = "/delete-component"
|
||||||
AddConnection = "/add-connection"
|
AddConnection = "/add-connection"
|
||||||
ResizeDesigner = "/resize-designer"
|
ResizeDesigner = "/resize-designer"
|
||||||
|
SaveProperties = "/save-properties"
|
||||||
|
CancelProperties = "/cancel-properties"
|
||||||
|
SelectProcessor = "/select-processor"
|
||||||
|
OnProcessorDetailsEvent = "/on-processor-details-event"
|
||||||
@@ -17,6 +17,7 @@ class WorkflowComponent:
|
|||||||
y: int
|
y: int
|
||||||
title: str
|
title: str
|
||||||
description: str
|
description: str
|
||||||
|
properties: dict = field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -37,6 +38,8 @@ class WorkflowsDesignerState:
|
|||||||
connections: list[Connection] = field(default_factory=list)
|
connections: list[Connection] = field(default_factory=list)
|
||||||
component_counter = 0
|
component_counter = 0
|
||||||
designer_height = 230
|
designer_height = 230
|
||||||
|
selected_component_id = None
|
||||||
|
selected_processor_name: dict = field(default_factory=dict) # selected processor for each component
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|||||||
14
src/utils/DbManagementHelper.py
Normal file
14
src/utils/DbManagementHelper.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from utils.ComponentsInstancesHelper import ComponentsInstancesHelper
|
||||||
|
|
||||||
|
|
||||||
|
class DbManagementHelper:
|
||||||
|
@staticmethod
|
||||||
|
def list_repositories(session):
|
||||||
|
return ComponentsInstancesHelper.get_repositories(session).db.get_repositories()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def list_tables(session, repository_name):
|
||||||
|
if not repository_name:
|
||||||
|
return []
|
||||||
|
repository = ComponentsInstancesHelper.get_repositories(session).db.get_repository(repository_name)
|
||||||
|
return repository.tables
|
||||||
Reference in New Issue
Block a user