This commit is contained in:
jokob-sk
2025-03-15 13:19:58 +11:00
parent 9b340532be
commit 1efdf66c19
6 changed files with 302 additions and 402 deletions

View File

@@ -67,7 +67,8 @@ function processData(data) {
{ data: 'ObjectSecondaryID', title: getString('AppEvents_ObjectSecondaryID') }, { data: 'ObjectSecondaryID', title: getString('AppEvents_ObjectSecondaryID') },
{ data: 'ObjectStatus', title: getString('AppEvents_ObjectStatus') }, { data: 'ObjectStatus', title: getString('AppEvents_ObjectStatus') },
{ data: 'ObjectPlugin', title: getString('AppEvents_Plugin') }, { data: 'ObjectPlugin', title: getString('AppEvents_Plugin') },
{ data: 'ObjectGUID', title: "GUID" }, { data: 'ObjectGUID', title: "Object GUID" },
{ data: 'GUID', title: "Event GUID" },
// Add other columns as needed // Add other columns as needed
], ],
// Add column-specific configurations if needed // Add column-specific configurations if needed

View File

@@ -1849,10 +1849,28 @@ input[readonly] {
padding: 5px; padding: 5px;
} }
.workflows .section-title
{
padding: 10px;
font-weight: bolder;
font-size: large;
}
.workflows .panel, .workflows .box {
padding-top: 10px;
padding-bottom: 10px;
}
.workflows .btn-secondary{ .workflows .btn-secondary{
color: #000; color: #000;
} }
.workflows .bottom-buttons button
{
margin: 5px;
}
.workflows .condition-list button .workflows .condition-list button
{ {
margin: 2px; margin: 2px;

View File

@@ -40,7 +40,7 @@
} }
] ]
}, },
"default_value": 5000, "default_value": 100,
"options": [], "options": [],
"localized": ["name", "description"], "localized": ["name", "description"],
"name": [ "name": [

View File

@@ -10,21 +10,20 @@
<div id="workflowContainer"></div> <div id="workflowContainer"></div>
</div> </div>
<div id="buttons" class="buttons col-sm-12"> <div id="buttons" class="bottom-buttons col-sm-12">
<div class="add-workflow col-sm-6"> <div class="add-workflow col-sm-12">
<button type="button" class="btn btn-primary btn-default pa-btn bg-green" id="save" onclick="addWorkflow()"> <button type="button" class="btn btn-primary add-workflow-btn col-sm-12" id="save">
<?= lang('Gen_Add');?> <?= lang('Gen_Add');?>
</button> </button>
</div> </div>
<div class="save-workflows col-sm-6"> <div class="save-workflows col-sm-12">
<button type="button" class="btn btn-primary btn-default pa-btn bg-green" id="save" onclick="saveWorkflows()"> <button type="button" class="btn btn-primary col-sm-12" id="save" onclick="saveWorkflows()">
<?= lang('DevDetail_button_Save');?> <?= lang('DevDetail_button_Save');?>
</button> </button>
</div> </div>
</div> </div>
</section> </section>
<script> <script>
let workflows = []; let workflows = [];
@@ -142,12 +141,18 @@ function generateWorkflowUI(wf, wfIndex) {
$wfCollapsiblePanel.append($wfNameInput) $wfCollapsiblePanel.append($wfNameInput)
let $triggerTitle = $("<div>",
{
class:"section-title"
}
).text("Trigger:")
// Trigger Section with dropdowns // Trigger Section with dropdowns
let $triggerSection = $("<div>", let $triggerSection = $("<div>",
{ {
class: "condition-list box box-secondary" class: "condition-list box box-secondary"
} }
).append("<strong>Trigger:</strong> "); ).append($triggerTitle);
let $triggerTypeDropdown = createEditableDropdown( let $triggerTypeDropdown = createEditableDropdown(
`[${wfIndex}].trigger.object_type`, `[${wfIndex}].trigger.object_type`,
@@ -170,22 +175,36 @@ function generateWorkflowUI(wf, wfIndex) {
$wfCollapsiblePanel.append($triggerSection); $wfCollapsiblePanel.append($triggerSection);
// Conditions // Conditions
let $conditionsContainer = $("<div>").append("<strong>Conditions:</strong>"); let $conditionsTitle = $("<div>",
{
class:"section-title"
}
).text("Conditions:")
let $conditionsContainer = $("<div>").append($conditionsTitle);
$conditionsContainer.append(renderConditions(wfIndex, `[${wfIndex}]`, 0, wf.conditions)); $conditionsContainer.append(renderConditions(wfIndex, `[${wfIndex}]`, 0, wf.conditions));
$wfCollapsiblePanel.append($conditionsContainer); $wfCollapsiblePanel.append($conditionsContainer);
let $actionsTitle = $("<div>",
{
class:"section-title"
}
).text("Actions:")
// Actions with action.field as dropdown // Actions with action.field as dropdown
let $actionsContainer = $("<div>", let $actionsContainer = $("<div>",
{ {
class: "actions-list box box-secondary" class: "actions-list box box-secondary"
} }
).append("<strong>Actions:</strong>"); ).append($actionsTitle);
lastActionIndex = 0 lastActionIndex = 0
$.each(wf.actions, function (actionIndex, action) { $.each(wf.actions, function (actionIndex, action) {
let $actionEl = $("<div>"); let $actionEl = $("<div>", {
class: "panel"
});
// Dropdown for action.field // Dropdown for action.field
let $fieldDropdown = createEditableDropdown( let $fieldDropdown = createEditableDropdown(
@@ -220,30 +239,60 @@ function generateWorkflowUI(wf, wfIndex) {
$actionEl.append($fieldDropdown); $actionEl.append($fieldDropdown);
$actionEl.append($actionValueInput); $actionEl.append($actionValueInput);
// add actions group button // Actions
let $actionRemoveButton = $("<button>", {
class : "btn btn-secondary remove-action",
actionIndex : actionIndex,
wfIndex: wfIndex
}).text("Remove Action")
$actionEl.append($actionRemoveButton); let $actionRemoveButtonWrap = $("<div>", { class: "button-container col-sm-12 col-sx-12" });
let $actionRemoveIcon = $("<i>", {
class: "fa-solid fa-minus"
});
let $actionRemoveButton = $("<button>", {
class: "btn btn-secondary remove-action btn-orange",
actionIndex: actionIndex,
wfIndex: wfIndex
})
.append($actionRemoveIcon) // Add icon
.append("Remove Action"); // Add text
$actionRemoveButtonWrap.append($actionRemoveButton);
$actionEl.append($actionRemoveButtonWrap);
$actionsContainer.append($actionEl); $actionsContainer.append($actionEl);
lastActionIndex = actionIndex lastActionIndex = actionIndex
}); });
// add actions group button // add action button
let $actionAddIcon = $("<i>", {
class: "fa-solid fa-plus"
});
let $actionAddButton = $("<button>", { let $actionAddButton = $("<button>", {
class : "btn btn-secondary add-action", class : "btn btn-secondary add-action",
lastActionIndex : lastActionIndex, lastActionIndex : lastActionIndex,
wfIndex: wfIndex wfIndex: wfIndex
}).text("Add Action") }).append($actionAddIcon).append("Add Action")
$actionsContainer.append($actionAddButton) $actionsContainer.append($actionAddButton)
let $wfRemoveButtonWrap = $("<div>", { class: "button-container col-sm-12 col-sx-12" });
let $wfRemoveIcon = $("<i>", {
class: "fa-solid fa-trash"
});
let $wfRemoveButton = $("<button>", {
class: "btn btn-secondary remove-wf",
wfIndex: wfIndex
})
.append($wfRemoveIcon) // Add icon
.append("Remove Workflow"); // Add text
$wfCollapsiblePanel.append($actionsContainer); $wfCollapsiblePanel.append($actionsContainer);
$wfCollapsiblePanel.append($wfRemoveButtonWrap.append($wfRemoveButton))
$wfContainer.append($wfCollapsiblePanel) $wfContainer.append($wfCollapsiblePanel)
return $wfContainer; return $wfContainer;
@@ -327,12 +376,15 @@ function renderConditions(wfIndex, parentIndexPath, conditionGroupsIndex, condit
$conditionItem.append($editableInput); // Append editable input for condition value $conditionItem.append($editableInput); // Append editable input for condition value
let $conditionRemoveButtonWrap = $("<div>", { class: "button-container col-sm-12 col-sx-12" }); let $conditionRemoveButtonWrap = $("<div>", { class: "button-container col-sm-12 col-sx-12" });
let $conditionRemoveButtonIcon = $("<i>", {
class: "fa-solid fa-minus"
});
let $conditionRemoveButton = $("<button>", { let $conditionRemoveButton = $("<button>", {
class : "btn btn-secondary remove-condition", class : "btn btn-secondary remove-condition",
lastConditionIndex : lastConditionIndex, lastConditionIndex : lastConditionIndex,
wfIndex: wfIndex, wfIndex: wfIndex,
parentIndexPath: parentIndexPath parentIndexPath: parentIndexPath
}).text("Remove Condition") }).append($conditionRemoveButtonIcon).append("Remove Condition")
$conditionRemoveButtonWrap .append($conditionRemoveButton); $conditionRemoveButtonWrap .append($conditionRemoveButton);
$conditionItem.append($conditionRemoveButtonWrap); $conditionItem.append($conditionRemoveButtonWrap);
@@ -350,22 +402,28 @@ function renderConditions(wfIndex, parentIndexPath, conditionGroupsIndex, condit
if (conditionGroupsIndex != 0) { if (conditionGroupsIndex != 0) {
// Add Condition button // Add Condition button
let $conditionAddWrap = $("<div>", { class: "button-container col-sm-6 col-sx-12" }); let $conditionAddWrap = $("<div>", { class: "button-container col-sm-6 col-sx-12" });
let $conditionAddIcon = $("<i>", {
class: "fa-solid fa-plus"
});
let $conditionAddButton = $("<button>", { let $conditionAddButton = $("<button>", {
class: "btn btn-secondary add-condition col-sx-12", class: "btn btn-secondary add-condition col-sx-12",
lastConditionIndex: lastConditionIndex, lastConditionIndex: lastConditionIndex,
wfIndex: wfIndex, wfIndex: wfIndex,
parentIndexPath: parentIndexPath parentIndexPath: parentIndexPath
}).text("Add Condition"); }).append($conditionAddIcon).append("Add Condition");
$conditionAddWrap.append($conditionAddButton); $conditionAddWrap.append($conditionAddButton);
// Remove Condition Group button // Remove Condition Group button
let $conditionGroupRemoveWrap = $("<div>", { class: "button-container col-sm-6 col-sx-12" }); let $conditionGroupRemoveWrap = $("<div>", { class: "button-container col-sm-6 col-sx-12" });
let $conditionGroupRemoveIcon = $("<i>", {
class: "fa-solid fa-trash"
});
let $conditionGroupRemoveButton = $("<button>", { let $conditionGroupRemoveButton = $("<button>", {
class: "btn btn-secondary remove-condition-group col-sx-12", class: "btn btn-secondary remove-condition-group col-sx-12",
lastConditionIndex: lastConditionIndex, lastConditionIndex: lastConditionIndex,
wfIndex: wfIndex, wfIndex: wfIndex,
parentIndexPath: parentIndexPath parentIndexPath: parentIndexPath
}).text("Remove Condition Group"); }).append($conditionGroupRemoveIcon).append("Remove Condition Group");
$conditionGroupRemoveWrap.append($conditionGroupRemoveButton); $conditionGroupRemoveWrap.append($conditionGroupRemoveButton);
$buttonWrap.append($conditionAddWrap); $buttonWrap.append($conditionAddWrap);
@@ -374,11 +432,14 @@ function renderConditions(wfIndex, parentIndexPath, conditionGroupsIndex, condit
// Add Condition Group button // Add Condition Group button
let $conditionsGroupAddWrap = $("<div>", { class: "button-container col-sm-12 col-sx-12" }); let $conditionsGroupAddWrap = $("<div>", { class: "button-container col-sm-12 col-sx-12" });
let $conditionsGroupAddIcon = $("<i>", {
class: "fa-solid fa-plus"
});
let $conditionsGroupAddButton = $("<button>", { let $conditionsGroupAddButton = $("<button>", {
class: "btn btn-secondary add-condition-group col-sx-12", class: "btn btn-secondary add-condition-group col-sx-12",
wfIndex: wfIndex, wfIndex: wfIndex,
parentIndexPath: parentIndexPath parentIndexPath: parentIndexPath
}).text("Add Condition Group"); }).append($conditionsGroupAddIcon).append("Add Condition Group");
$conditionsGroupAddWrap.append($conditionsGroupAddButton); $conditionsGroupAddWrap.append($conditionsGroupAddButton);
$buttonWrap.append($conditionsGroupAddWrap); $buttonWrap.append($conditionsGroupAddWrap);
@@ -571,6 +632,48 @@ function parsePath(path) {
// Buttons functionality // Buttons functionality
// --------------------------------------------------- // ---------------------------------------------------
// ---------------------------------------------------
// Function to add a new Workflow
function addWorkflow() {
workflows.push({
"name": "New Workflow",
"trigger": {
"object_type": "Devices",
"event_type": "create"
},
"conditions": [
],
"actions": [
]
}
);
// // Ensure the workflows object is updated in memory
// workflows = workflows;
// Update the cache with the modified workflows object
setCache("workflows", JSON.stringify(workflows));
// Re-render the UI
renderWorkflows();
}
// ---------------------------------------------------
// Function to remove a Workflow
function removeWorkflow(wfIndex) {
workflows.splice(wfIndex, 1);
// // Ensure the workflows object is updated in memory
// workflows = workflows;
// Update the cache with the modified workflows object
setCache("workflows", JSON.stringify(workflows));
// Re-render the UI
renderWorkflows();
}
// --------------------------------------------------- // ---------------------------------------------------
// Function to add a new condition // Function to add a new condition
function addCondition(wfIndex, parentIndexPath) { function addCondition(wfIndex, parentIndexPath) {
@@ -593,10 +696,10 @@ function addCondition(wfIndex, parentIndexPath) {
value: "" // Default empty value value: "" // Default empty value
}); });
// 🔥 Ensure the workflows object is updated in memory // Ensure the workflows object is updated in memory
workflows[wfIndex] = { ...workflows[wfIndex] }; workflows[wfIndex] = { ...workflows[wfIndex] };
// 🔥 Update the cache with the modified workflows object // Update the cache with the modified workflows object
setCache("workflows", JSON.stringify(workflows)); setCache("workflows", JSON.stringify(workflows));
// Re-render the UI // Re-render the UI
@@ -624,10 +727,10 @@ function addConditionGroup(wfIndex, parentIndexPath) {
conditions: [] conditions: []
}); });
// 🔥 Ensure the workflows object is updated in memory // Ensure the workflows object is updated in memory
workflows[wfIndex] = { ...workflows[wfIndex] }; workflows[wfIndex] = { ...workflows[wfIndex] };
// 🔥 Update the cache with the modified workflows object // Update the cache with the modified workflows object
setCache("workflows", JSON.stringify(workflows)); setCache("workflows", JSON.stringify(workflows));
// Re-render the UI // Re-render the UI
@@ -662,10 +765,10 @@ function removeCondition(wfIndex, parentIndexPath, lastConditionIndex) {
// Remove the specified condition // Remove the specified condition
target.conditions.splice(lastConditionIndex, 1); target.conditions.splice(lastConditionIndex, 1);
// 🔥 Ensure the workflows object is updated in memory // Ensure the workflows object is updated in memory
workflows[wfIndex] = { ...workflows[wfIndex] }; workflows[wfIndex] = { ...workflows[wfIndex] };
// 🔥 Update the cache with the modified workflows object // Update the cache with the modified workflows object
setCache("workflows", JSON.stringify(workflows)); setCache("workflows", JSON.stringify(workflows));
// Re-render the UI // Re-render the UI
@@ -675,27 +778,27 @@ function removeCondition(wfIndex, parentIndexPath, lastConditionIndex) {
function removeAction(wfIndex, actionIndex) { function removeAction(wfIndex, actionIndex) {
if (!actionIndex || actionIndex === undefined) return; if (!actionIndex || actionIndex === undefined) return;
// // Navigate to the target nested object // Navigate to the target nested object
// let target = getNestedObject(workflows, parentIndexPath); let target = getNestedObject(workflows, wfIndex);
// console.log("Target before removal:", target); console.log("Target before removal:", target);
// if (!target || !Array.isArray(target.conditions)) { if (!target || !Array.isArray(target.actions)) {
// console.error("❌ Invalid path or conditions array missing:", parentIndexPath); console.error("❌ Invalid path or conditions array missing:", actionIndex);
// return; return;
// } }
// // Remove the specified condition // Remove the specified condition
// target.conditions.splice(lastConditionIndex, 1); target.actions.splice(actionIndex, 1);
// // 🔥 Ensure the workflows object is updated in memory // Ensure the workflows object is updated in memory
// workflows[wfIndex] = { ...workflows[wfIndex] }; workflows[wfIndex] = { ...workflows[wfIndex] };
// // 🔥 Update the cache with the modified workflows object // Update the cache with the modified workflows object
// setCache("workflows", JSON.stringify(workflows)); setCache("workflows", JSON.stringify(workflows));
// // Re-render the UI // Re-render the UI
// renderWorkflows(); renderWorkflows();
} }
function removeConditionGroup(wfIndex, parentIndexPath) { function removeConditionGroup(wfIndex, parentIndexPath) {
@@ -726,10 +829,10 @@ function removeConditionGroup(wfIndex, parentIndexPath) {
// Remove the specified condition group // Remove the specified condition group
delete target.conditions.splice(lastIndex, 1); delete target.conditions.splice(lastIndex, 1);
// 🔥 Ensure the workflows object is updated in memory // Ensure the workflows object is updated in memory
workflows[wfIndex] = { ...workflows[wfIndex] }; workflows[wfIndex] = { ...workflows[wfIndex] };
// 🔥 Update the cache with the modified workflows object // Update the cache with the modified workflows object
setCache("workflows", JSON.stringify(workflows)); setCache("workflows", JSON.stringify(workflows));
// Re-render the UI // Re-render the UI
@@ -759,6 +862,15 @@ function getNestedObject(obj, path) {
// --------------------------------------------------- // ---------------------------------------------------
// Event listeners // Event listeners
$(document).on("click", ".add-workflow-btn", function () {
addWorkflow();
});
$(document).on("click", ".remove-wf", function () {
let wfIndex = $(this).attr("wfindex");
removeWorkflow(wfIndex);
});
$(document).on("click", ".add-condition", function () { $(document).on("click", ".add-condition", function () {
let wfIndex = $(this).attr("wfindex"); let wfIndex = $(this).attr("wfindex");
let parentIndexPath = $(this).attr("parentIndexPath"); let parentIndexPath = $(this).attr("parentIndexPath");

View File

@@ -627,7 +627,8 @@ def process_plugin_events(db, plugin, plugEventsArr):
("Plugin", "Object_PrimaryID", "Object_SecondaryID", "DateTimeCreated", ("Plugin", "Object_PrimaryID", "Object_SecondaryID", "DateTimeCreated",
"DateTimeChanged", "Watched_Value1", "Watched_Value2", "Watched_Value3", "DateTimeChanged", "Watched_Value1", "Watched_Value2", "Watched_Value3",
"Watched_Value4", "Status", "Extra", "UserData", "ForeignKey", "SyncHubNodeName", "Watched_Value4", "Status", "Extra", "UserData", "ForeignKey", "SyncHubNodeName",
"HelpVal1", "HelpVal2", "HelpVal3", "HelpVal4", "ObjectGUID") "HelpVal1", "HelpVal2", "HelpVal3", "HelpVal4",
"ObjectGUID")
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", objects_to_insert """, objects_to_insert
) )
@@ -639,7 +640,8 @@ def process_plugin_events(db, plugin, plugEventsArr):
UPDATE Plugins_Objects UPDATE Plugins_Objects
SET "Plugin" = ?, "Object_PrimaryID" = ?, "Object_SecondaryID" = ?, "DateTimeCreated" = ?, SET "Plugin" = ?, "Object_PrimaryID" = ?, "Object_SecondaryID" = ?, "DateTimeCreated" = ?,
"DateTimeChanged" = ?, "Watched_Value1" = ?, "Watched_Value2" = ?, "Watched_Value3" = ?, "DateTimeChanged" = ?, "Watched_Value1" = ?, "Watched_Value2" = ?, "Watched_Value3" = ?,
"Watched_Value4" = ?, "Status" = ?, "Extra" = ?, "UserData" = ?, "ForeignKey" = ?, "SyncHubNodeName" = ?, "HelpVal1" = ?, "HelpVal2" = ?, "HelpVal3" = ?, "HelpVal4" = ?, "Watched_Value4" = ?, "Status" = ?, "Extra" = ?, "UserData" = ?, "ForeignKey" = ?, "SyncHubNodeName" = ?,
"HelpVal1" = ?, "HelpVal2" = ?, "HelpVal3" = ?, "HelpVal4" = ?,
"ObjectGUID" = ? "ObjectGUID" = ?
WHERE "Index" = ? WHERE "Index" = ?
""", objects_to_update """, objects_to_update

View File

@@ -22,57 +22,120 @@ Logger(get_setting_value('LOG_LEVEL'))
from const import applicationPath, logPath, apiPath, confFileName, sql_generateGuid from const import applicationPath, logPath, apiPath, confFileName, sql_generateGuid
from helper import timeNowTZ from helper import timeNowTZ
#-------------------------------------------------------------------------------
# Execution object handling
#-------------------------------------------------------------------------------
class AppEvent_obj: class AppEvent_obj:
def __init__(self, db): def __init__(self, db):
self.db = db self.db = db
# drop table # Drop existing table
self.db.sql.execute("""DROP TABLE IF EXISTS "AppEvents" """) self.db.sql.execute("""DROP TABLE IF EXISTS "AppEvents" """)
# Drop all triggers # Drop all triggers
self.db.sql.execute('DROP TRIGGER IF EXISTS trg_create_device;') self.drop_all_triggers()
self.db.sql.execute('DROP TRIGGER IF EXISTS trg_read_device;')
self.db.sql.execute('DROP TRIGGER IF EXISTS trg_update_device;') # Create the AppEvents table if missing
self.db.sql.execute('DROP TRIGGER IF EXISTS trg_delete_device;') self.create_app_events_table()
self.db.sql.execute('DROP TRIGGER IF EXISTS trg_delete_plugin_object;') # Define object mapping for different table structures, including fields, expressions, and constants
self.db.sql.execute('DROP TRIGGER IF EXISTS trg_create_plugin_object;') self.object_mapping = {
self.db.sql.execute('DROP TRIGGER IF EXISTS trg_update_plugin_object;') "Devices": {
"fields": {
"ObjectGUID": "NEW.devGUID",
"ObjectPrimaryID": "NEW.devMac",
"ObjectSecondaryID": "NEW.devLastIP",
"ObjectForeignKey": "NEW.devGUID",
"ObjectStatus": "CASE WHEN NEW.devPresentLastScan = 1 THEN 'online' ELSE 'offline' END",
"ObjectStatusColumn": "'devPresentLastScan'",
"ObjectIsNew": "NEW.devIsNew",
"ObjectIsArchived": "NEW.devIsArchived",
"ObjectPlugin": "'DEVICES'"
}
},
"Plugins_Objects": {
"fields": {
"ObjectGUID": "NEW.ObjectGUID",
"ObjectPrimaryID": "NEW.Plugin",
"ObjectSecondaryID": "NEW.Object_PrimaryID",
"ObjectForeignKey": "NEW.ForeignKey",
"ObjectStatus": "NEW.Status",
"ObjectStatusColumn": "'Status'",
"ObjectIsNew": "CASE WHEN NEW.Status = 'new' THEN 1 ELSE 0 END",
"ObjectIsArchived": "0", # Default value
"ObjectPlugin": "NEW.Plugin"
}
}
}
# Create AppEvent table if missing
self.db.sql.execute("""CREATE TABLE IF NOT EXISTS "AppEvents" ( # Re-Create triggers dynamically
"Index" INTEGER, for table, config in self.object_mapping.items():
"GUID" TEXT UNIQUE, self.create_trigger(table, "insert", config)
"AppEventProcessed" BOOLEAN, self.create_trigger(table, "update", config)
"DateTimeCreated" TEXT, self.create_trigger(table, "delete", config)
"ObjectType" TEXT, -- ObjectType (Plugins, Notifications, Events)
"ObjectGUID" TEXT, self.save()
"ObjectPlugin" TEXT,
"ObjectPrimaryID" TEXT, def drop_all_triggers(self):
"ObjectSecondaryID" TEXT, """Drops all relevant triggers to ensure a clean start."""
"ObjectForeignKey" TEXT, self.db.sql.execute("""
"ObjectIndex" TEXT, SELECT 'DROP TRIGGER IF EXISTS ' || name || ';'
"ObjectIsNew" BOOLEAN, FROM sqlite_master
"ObjectIsArchived" BOOLEAN, WHERE type = 'trigger';
"ObjectStatusColumn" TEXT, -- Status (Notifications, Plugins), eve_EventType (Events)
"ObjectStatus" TEXT, -- new_devices, down_devices, events, new, watched-changed, watched-not-changed, missing-in-last-scan, Device down, New Device, IP Changed, Connected, Disconnected, VOIDED - Disconnected, VOIDED - Connected, <missing event>
"AppEventType" TEXT, -- "create", "update", "delete" (+TBD)
"Helper1" TEXT,
"Helper2" TEXT,
"Helper3" TEXT,
"Extra" TEXT,
PRIMARY KEY("Index" AUTOINCREMENT)
);
""") """)
# ------------- # Fetch all drop statements
# Device events drop_statements = self.db.sql.fetchall()
sql_devices_mappedColumns = ''' # Execute each drop statement
for statement in drop_statements:
self.db.sql.execute(statement[0])
self.save()
def create_app_events_table(self):
"""Creates the AppEvents table if it doesn't exist."""
self.db.sql.execute("""
CREATE TABLE IF NOT EXISTS "AppEvents" (
"Index" INTEGER PRIMARY KEY AUTOINCREMENT,
"GUID" TEXT UNIQUE,
"AppEventProcessed" BOOLEAN,
"DateTimeCreated" TEXT,
"ObjectType" TEXT,
"ObjectGUID" TEXT,
"ObjectPlugin" TEXT,
"ObjectPrimaryID" TEXT,
"ObjectSecondaryID" TEXT,
"ObjectForeignKey" TEXT,
"ObjectIndex" TEXT,
"ObjectIsNew" BOOLEAN,
"ObjectIsArchived" BOOLEAN,
"ObjectStatusColumn" TEXT,
"ObjectStatus" TEXT,
"AppEventType" TEXT,
"Helper1" TEXT,
"Helper2" TEXT,
"Helper3" TEXT,
"Extra" TEXT
);
""")
def create_trigger(self, table_name, event, config):
"""Generic function to create triggers dynamically."""
trigger_name = f"trg_{event}_{table_name.lower()}"
query = f"""
CREATE TRIGGER IF NOT EXISTS "{trigger_name}"
AFTER {event.upper()} ON "{table_name}"
WHEN NOT EXISTS (
SELECT 1 FROM AppEvents
WHERE AppEventProcessed = 0
AND ObjectType = '{table_name}'
AND ObjectGUID = {config['fields']['ObjectGUID']}
AND ObjectStatus = {config['fields']['ObjectStatus']}
AND AppEventType = '{event.lower()}'
)
BEGIN
INSERT INTO "AppEvents" (
"GUID", "GUID",
"DateTimeCreated", "DateTimeCreated",
"AppEventProcessed", "AppEventProcessed",
@@ -85,328 +148,32 @@ class AppEvent_obj:
"ObjectIsNew", "ObjectIsNew",
"ObjectIsArchived", "ObjectIsArchived",
"ObjectForeignKey", "ObjectForeignKey",
"AppEventType"
'''
# Trigger for create event
self.db.sql.execute(f'''
CREATE TRIGGER IF NOT EXISTS "trg_create_device"
AFTER INSERT ON "Devices"
BEGIN
INSERT INTO "AppEvents" (
{sql_devices_mappedColumns}
)
VALUES (
{sql_generateGuid},
DATETIME('now'),
FALSE,
'Devices',
NEW.devGUID,
NEW.devMac,
NEW.devLastIP,
CASE WHEN NEW.devPresentLastScan = 1 THEN 'online' ELSE 'offline' END,
'devPresentLastScan',
NEW.devIsNew,
NEW.devIsArchived,
NEW.devMac,
'create'
);
END;
''')
# 🔴 This would generate too many events, disabled for now
# # Trigger for read event
# self.db.sql.execute('''
# TODO
# ''')
# Trigger for update event
self.db.sql.execute(f'''
CREATE TRIGGER IF NOT EXISTS "trg_update_device"
AFTER UPDATE ON "Devices"
BEGIN
INSERT INTO "AppEvents" (
{sql_devices_mappedColumns}
)
VALUES (
{sql_generateGuid},
DATETIME('now'),
FALSE,
'Devices',
NEW.devGUID,
NEW.devMac,
NEW.devLastIP,
CASE WHEN NEW.devPresentLastScan = 1 THEN 'online' ELSE 'offline' END,
'devPresentLastScan',
NEW.devIsNew,
NEW.devIsArchived,
NEW.devMac,
'update'
);
END;
''')
# Trigger for delete event
self.db.sql.execute(f'''
CREATE TRIGGER IF NOT EXISTS "trg_delete_device"
AFTER DELETE ON "Devices"
BEGIN
INSERT INTO "AppEvents" (
{sql_devices_mappedColumns}
)
VALUES (
{sql_generateGuid},
DATETIME('now'),
FALSE,
'Devices',
OLD.devGUID,
OLD.devMac,
OLD.devLastIP,
CASE WHEN OLD.devPresentLastScan = 1 THEN 'online' ELSE 'offline' END,
'devPresentLastScan',
OLD.devIsNew,
OLD.devIsArchived,
OLD.devMac,
'delete'
);
END;
''')
# -------------
# Plugins_Objects events
sql_plugins_objects_mappedColumns = '''
"GUID",
"DateTimeCreated",
"AppEventProcessed",
"ObjectType",
"ObjectGUID",
"ObjectPlugin", "ObjectPlugin",
"ObjectPrimaryID",
"ObjectSecondaryID",
"ObjectForeignKey",
"ObjectStatusColumn",
"ObjectStatus",
"AppEventType" "AppEventType"
'''
# Create trigger for update event on Plugins_Objects
self.db.sql.execute(f'''
CREATE TRIGGER IF NOT EXISTS trg_update_plugin_object
AFTER UPDATE ON Plugins_Objects
BEGIN
INSERT INTO AppEvents (
{sql_plugins_objects_mappedColumns}
) )
VALUES ( VALUES (
{sql_generateGuid}, {sql_generateGuid},
DATETIME('now'), DATETIME('now'),
FALSE, FALSE,
'Plugins_Objects', '{table_name}',
NEW.ObjectGUID, {config['fields']['ObjectGUID']}, -- ObjectGUID
NEW.Plugin, {config['fields']['ObjectPrimaryID']}, -- ObjectPrimaryID
NEW.Object_PrimaryID, {config['fields']['ObjectSecondaryID']}, -- ObjectSecondaryID
NEW.Object_SecondaryID, {config['fields']['ObjectStatus']}, -- ObjectStatus
NEW.ForeignKey, {config['fields']['ObjectStatusColumn']}, -- ObjectStatusColumn
'Status', {config['fields']['ObjectIsNew']}, -- ObjectIsNew
NEW.Status, {config['fields']['ObjectIsArchived']}, -- ObjectIsArchived
'update' {config['fields']['ObjectForeignKey']}, -- ObjectForeignKey
{config['fields']['ObjectPlugin']}, -- ObjectForeignKey
'{event.lower()}'
); );
END; END;
''') """
# Create trigger for CREATE event on Plugins_Objects mylog("verbose", [query])
self.db.sql.execute(f'''
CREATE TRIGGER IF NOT EXISTS trg_create_plugin_object
AFTER INSERT ON Plugins_Objects
BEGIN
INSERT INTO AppEvents (
{sql_plugins_objects_mappedColumns}
)
VALUES (
{sql_generateGuid},
DATETIME('now'),
FALSE,
'Plugins_Objects',
NEW.ObjectGUID,
NEW.Plugin,
NEW.Object_PrimaryID,
NEW.Object_SecondaryID,
NEW.ForeignKey,
'Status',
NEW.Status,
'create'
);
END;
''')
# Create trigger for DELETE event on Plugins_Objects self.db.sql.execute(query)
self.db.sql.execute(f'''
CREATE TRIGGER IF NOT EXISTS trg_delete_plugin_object
AFTER DELETE ON Plugins_Objects
BEGIN
INSERT INTO AppEvents (
{sql_plugins_objects_mappedColumns}
)
VALUES (
{sql_generateGuid},
DATETIME('now'),
FALSE,
'Plugins_Objects',
OLD.ObjectGUID,
OLD.Plugin,
OLD.Object_PrimaryID,
OLD.Object_SecondaryID,
OLD.ForeignKey,
'Status',
OLD.Status,
'delete'
);
END;
''')
self.save()
# -------------------------------------------------------------------------------
# -------------------------------------------------------------------------------
# below code is unused
# -------------------------------------------------------------------------------
# Create a new DB entry if new notifications are available, otherwise skip
def create(self, Extra="", **kwargs):
# Check if nothing to report, end
if not any(kwargs.values()):
return False
# Continue and save into DB if notifications are available
self.GUID = str(uuid.uuid4())
self.DateTimeCreated = timeNowTZ()
self.ObjectType = "Plugins" # Modify ObjectType as needed
# Optional parameters
self.ObjectGUID = kwargs.get("ObjectGUID", "")
self.ObjectPlugin = kwargs.get("ObjectPlugin", "")
self.ObjectMAC = kwargs.get("ObjectMAC", "")
self.ObjectIP = kwargs.get("ObjectIP", "")
self.ObjectPrimaryID = kwargs.get("ObjectPrimaryID", "")
self.ObjectSecondaryID = kwargs.get("ObjectSecondaryID", "")
self.ObjectForeignKey = kwargs.get("ObjectForeignKey", "")
self.ObjectIndex = kwargs.get("ObjectIndex", "")
self.ObjectRowID = kwargs.get("ObjectRowID", "")
self.ObjectStatusColumn = kwargs.get("ObjectStatusColumn", "")
self.ObjectStatus = kwargs.get("ObjectStatus", "")
self.AppEventStatus = "new" # Modify AppEventStatus as needed
self.Extra = Extra
self.upsert()
return True
def upsert(self):
self.db.sql.execute("""
INSERT OR REPLACE INTO AppEvents (
"GUID",
"DateTimeCreated",
"ObjectType",
"ObjectGUID",
"ObjectPlugin",
"ObjectMAC",
"ObjectIP",
"ObjectPrimaryID",
"ObjectSecondaryID",
"ObjectForeignKey",
"ObjectIndex",
"ObjectRowID",
"ObjectStatusColumn",
"ObjectStatus",
"AppEventStatus",
"Extra"
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
self.GUID,
self.DateTimeCreated,
self.ObjectType,
self.ObjectGUID,
self.ObjectPlugin,
self.ObjectMAC,
self.ObjectIP,
self.ObjectPrimaryID,
self.ObjectSecondaryID,
self.ObjectForeignKey,
self.ObjectIndex,
self.ObjectRowID,
self.ObjectStatusColumn,
self.ObjectStatus,
self.AppEventStatus,
self.Extra
))
self.save()
def save(self): def save(self):
# Commit changes # Commit changes
self.db.commitDB() self.db.commitDB()
def getPluginObject(**kwargs):
# Check if nothing, end
if not any(kwargs.values()):
return None
# Optional parameters
GUID = kwargs.get("GUID", "")
Plugin = kwargs.get("Plugin", "")
MAC = kwargs.get("MAC", "")
IP = kwargs.get("IP", "")
PrimaryID = kwargs.get("PrimaryID", "")
SecondaryID = kwargs.get("SecondaryID", "")
ForeignKey = kwargs.get("ForeignKey", "")
Index = kwargs.get("Index", "")
RowID = kwargs.get("RowID", "")
# we need the plugin
if Plugin == "":
return None
plugins_objects = apiPath + 'table_plugins_objects.json'
try:
with open(plugins_objects, 'r') as json_file:
data = json.load(json_file)
for item in data.get("data",[]):
if item.get("Index") == Index:
return item
for item in data.get("data",[]):
if item.get("ObjectPrimaryID") == PrimaryID and item.get("ObjectSecondaryID") == SecondaryID:
return item
for item in data.get("data",[]):
if item.get("ObjectPrimaryID") == MAC and item.get("ObjectSecondaryID") == IP:
return item
for item in data.get("data",[]):
if item.get("ObjectPrimaryID") == PrimaryID and item.get("ObjectSecondaryID") == IP:
return item
for item in data.get("data",[]):
if item.get("ObjectPrimaryID") == MAC and item.get("ObjectSecondaryID") == IP:
return item
mylog('debug', [f'[{module_name}] ⚠ ERROR - Object not found - GUID:{GUID} | Plugin:{Plugin} | MAC:{MAC} | IP:{IP} | PrimaryID:{PrimaryID} | SecondaryID:{SecondaryID} | ForeignKey:{ForeignKey} | Index:{Index} | RowID:{RowID} '])
return None
except (FileNotFoundError, json.JSONDecodeError, ValueError) as e:
# Handle the case when the file is not found, JSON decoding fails, or data is not in the expected format
mylog('none', [f'[{module_name}] ⚠ ERROR - JSONDecodeError or FileNotFoundError for file {plugins_objects}'])
return None