Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 49 additions & 12 deletions front/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -1011,7 +1011,7 @@ height: 50px;
display: block;
position: absolute;
font-size: x-small;
top: -5px;
top: 5px;
}

/* --------------------------------------------------------- */
Expand Down Expand Up @@ -2221,7 +2221,7 @@ textarea[readonly],

.pluginsCore .helpIcon
{
margin-left: -20px;
margin-left: -15px;
font-size: x-small;
}

Expand Down Expand Up @@ -2607,21 +2607,47 @@ table.dataTable tbody > tr.selected
}

/* -----------------------------------------------------------------------------
Donations
Device -> Details
----------------------------------------------------------------------------- */
.donations .box
#panDetails
{
padding:15px;
margin-bottom: 0px;
padding:10px;
}
.donations .box-header

/* -----------------------------------------------------------------------------
Device -> Tools
----------------------------------------------------------------------------- */
#panTools
{
color:15px;
padding:10px;
}
.donations h3

/* -----------------------------------------------------------------------------
Device -> Sessions
----------------------------------------------------------------------------- */
#panSessions
{
margin-top: 10px;
padding:10px;
}


/* -----------------------------------------------------------------------------
Device -> Presence
----------------------------------------------------------------------------- */
#panPresence
{
padding:10px;
}

/* -----------------------------------------------------------------------------
Device -> Events
----------------------------------------------------------------------------- */
#panEvents
{
padding:10px;
}


/* -----------------------------------------------------------------------------
Field Lock/Unlock Buttons & Source Indicators
----------------------------------------------------------------------------- */
Expand Down Expand Up @@ -2660,15 +2686,26 @@ table.dataTable tbody > tr.selected
----------------------------------------------------------------------------- */

.change-log .helpIcon{
font-size: x-small;
top: 65px;
}

#panHistory .change-log .helpIcon{
left: 15px;
font-size: x-small;
top: 45px;
}

#panHistory
{
min-height:850px
}

#navDevice .tab-content
{
padding:0px;
}

#panHistory .change-log-skeleton-class
{
margin-left: 0px;
Expand Down Expand Up @@ -2729,7 +2766,7 @@ table.dataTable tbody > tr.selected
#settings-skeleton, #devices-skeleton, #device-details-skeleton,
#events-skeleton, #presence-skeleton, #report-skeleton, #skel-app-events,
#notifications-skeleton, #workflows-skeleton, #plugins-skeleton { z-index: 50; }
.skel-tab-pane { margin: -10px; z-index: 10; min-height: 400px; background-color: var(--skel-panel-bg);}
.skel-tab-pane { z-index: 10; min-height: 400px; background-color: var(--skel-panel-bg);}
#settings-skeleton { margin: 15px; min-height: 500px; }
#devices-skeleton { margin: 15px; top: 50px; min-height: 500px; }
#skel-app-events { margin: 15px; top: 50px; min-height: 500px; z-index: inherit;}
Expand All @@ -2741,7 +2778,7 @@ table.dataTable tbody > tr.selected
#workflows-skeleton { top: 50px; width: 770px; margin: 0 auto; min-height: 300px; }
#plugins-skeleton { top: 50px; padding: 0; min-height: 400px; }
#change-log-skeleton { display: block; min-height: 400px; z-index: 1; position: absolute; margin-left: 15px; padding-top: 15px; width:100%;}
#skel-tab-sessions, #skel-tab-events, #skel-tab-presence {margin: -10px; padding: 20px;}
#skel-tab-sessions, #skel-tab-events, #skel-tab-presence { padding: 20px;}

/* Hide scrollbars */
#skel-tab-sessions, #skel-tab-events, #skel-tab-presence {
Expand Down
4 changes: 2 additions & 2 deletions front/plugins/unifi_api_import/unifi_api_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,12 @@ def get_device_data(site, api):
# --- Devices ---
unifi_devices_resp = api.get_unifi_devices(site_id)
unifi_devices = unifi_devices_resp.get("data", [])
mylog('verbose', [f'[{pluginName}] Site: {site_name} unifi devices: {json.dumps(unifi_devices_resp, indent=2)}'])
mylog('trace', [f'[{pluginName}] Site: {site_name} unifi devices: {json.dumps(unifi_devices_resp, indent=2)}'])

# --- Clients ---
clients_resp = api.get_clients(site_id)
clients = clients_resp.get("data", [])
mylog('verbose', [f'[{pluginName}] Site: {site_name} clients: {json.dumps(clients_resp, indent=2)}'])
mylog('trace', [f'[{pluginName}] Site: {site_name} clients: {json.dumps(clients_resp, indent=2)}'])

# Build a lookup for devices by their 'id' to find parent MAC easily
device_id_to_mac = {}
Expand Down
3 changes: 2 additions & 1 deletion front/report_templates/report_template.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<html>
<head></head>
<body>
<span style="display:none;">PREHEADER</span>
<font face=sans-serif>
<table align=center width=100% cellpadding=0 cellspacing=0 style="border-radius: 5px;">
<tr>
Expand All @@ -10,7 +11,7 @@
</td>
</tr>
<tr>
<td height=200 valign=top style="padding: 10px">
<td height=200 valign=top style="padding: 10px">
NEW_DEVICES_TABLE
DOWN_DEVICES_TABLE
DOWN_RECONNECTED_TABLE
Expand Down
4 changes: 2 additions & 2 deletions server/messaging/reporting.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,8 @@ def get_section_condition(section):
sqlQuery = template.format(**fallback_vars)
parameters = {}

mylog("debug", [f"[Notification] {section} SQL query: ", sqlQuery])
mylog("debug", [f"[Notification] {section} parameters: ", parameters])
mylog("trace", [f"[Notification] {section} SQL query: ", sqlQuery])
mylog("trace", [f"[Notification] {section} parameters: ", parameters])

try:
json_obj = db.get_table_as_json(sqlQuery, parameters)
Expand Down
6 changes: 0 additions & 6 deletions server/models/device_history_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,6 @@ def _query_grouped(self, devGUID, changedColumn, changedBy, limit, offset, sort,
clauses, params = self._build_clauses(devGUID, changedColumn, changedBy, search)
where = f"WHERE {' AND '.join(clauses)}" if clauses else ""

mylog("none", f"[HISTORY] SQL {where} params={params}")
mylog("none", f"[HISTORY] sort={sort}")
mylog("none", f"[HISTORY] sort type={type(sort)}")
mylog("none", f"[HISTORY] search {search} ")
mylog("none", f"[HISTORY] FINAL CALL limit={limit} offset={offset} sort={sort}")

rows = self._fetchall(
f"""
SELECT devGUID, timestamp, changedBy, changedColumn, oldValue, newValue
Expand Down
47 changes: 41 additions & 6 deletions server/models/notification_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,14 @@ def create(self, JSON, Extra=""):
mail_text = mail_text.replace("REPORT_DASHBOARD_URL", self.serverUrl)
mail_html = mail_html.replace("REPORT_DASHBOARD_URL", self.serverUrl)

# Preheaders for preview
preheaders = []

# Generate TEXT & HTML for each notification section
for section in SECTION_ORDER:
html, text = construct_notifications(self.JSON, section)
html, text, preheader = construct_notifications(self.JSON, section)
if preheader:
preheaders.append(preheader)
placeholder = f"{section.upper()}_TABLE"
mail_text = mail_text.replace(placeholder, text + "\n")
mail_html = mail_html.replace(placeholder, html)
Expand All @@ -141,6 +146,11 @@ def create(self, JSON, Extra=""):
mail_html, conf.REPORT_DASHBOARD_URL + "/deviceDetails.php?mac="
)

# Add Preheaders for preview after mac links created
mail_html = mail_html.replace(
"PREHEADER", " • ".join(preheaders)
)

final_html = indent(
mail_html, indentation=" ", newline="\r\n", indent_text=True
)
Expand Down Expand Up @@ -276,24 +286,23 @@ def save(self):
# -----------------------------------------------------------------------------
# Reporting
# -----------------------------------------------------------------------------


# ------------------------------------------------------------------------------
def construct_notifications(JSON, section):
jsn = JSON[section]

# Return if empty
if jsn == []:
return "", ""
return "", "", ""

tableTitle = JSON[section + "_meta"]["title"]
headers = JSON[section + "_meta"]["columnNames"]

html = ""
text = ""
preheader_preview = ""

table_attributes = {
"style": "border-collapse: collapse; font-size: 12px; color:#70707",
"style": "border-collapse: collapse; font-size: 12px; color: #70707;",
"width": "100%",
"cellspacing": 0,
"cellpadding": "3px",
Expand Down Expand Up @@ -326,6 +335,9 @@ def construct_notifications(JSON, section):
table_attributes=table_attributes,
)

# pre-header in emails (html) to show a more valuable preview
preheader_preview = build_preheader(tableTitle, jsn, headers)

# Cleanup the generated HTML table notification
html = (
format_table(html, "data", headerProps, tableTitle)
Expand Down Expand Up @@ -357,7 +369,7 @@ def construct_notifications(JSON, section):
for header in headers:
html = format_table(html, header, thProps)

return html, text
return html, text, preheader_preview


# -----------------------------------------------------------------------------
Expand All @@ -378,3 +390,26 @@ def format_table(html, thValue, props, newThValue=""):
return html.replace(
"<th>" + thValue + "</th>", "<th " + props + " >" + newThValue + "</th>"
)


# -----------------------------------------------------------------------------
# Pre-header Preview
def build_preheader(tableTitle, jsn, headers):
previews = []

# pre-headers for email previews
preview_fields = headers[:3]

for entry in jsn[:3]:
vals = [
str(entry[h])
for h in preview_fields
if entry.get(h)
]
previews.append(" • ".join(vals))

suffix = ""
if len(jsn) > 3:
suffix = f" (+{len(jsn) - 3} more)"

return f"{tableTitle}: " + "; ".join(previews) + suffix
4 changes: 2 additions & 2 deletions server/workflows/conditions.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class ConditionGroup:
"""Handles condition groups with AND, OR logic, supporting nested groups."""

def __init__(self, group_json):
mylog("verbose", f"[WF] ConditionGroup json.dumps(group_json): {json.dumps(group_json)}")
mylog("trace", f"[WF] ConditionGroup json.dumps(group_json): {json.dumps(group_json)}")

self.logic = group_json.get("logic", "AND").upper()
self.conditions = []
Expand All @@ -76,5 +76,5 @@ def evaluate(self, event):
return any(results)
else:
m = f"[WF] ConditionGroup unsupported logic: {self.logic}"
mylog("verbose", [m])
mylog("none", [m])
raise ValueError(m)
10 changes: 3 additions & 7 deletions server/workflows/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ def __init__(self, db):

# -------------------------------------------------------------------------
# Token validation

def _validate_workflow_tokens(self, workflow):
"""Recursively scan a workflow dict for {{trigger.X}} tokens.
Returns True if every token maps to a valid Devices column."""
Expand All @@ -52,7 +51,6 @@ def _scan(node):

# -------------------------------------------------------------------------
# Loading

def load_workflows(self):
"""Load workflows from workflows.json, rejecting any with invalid tokens."""
try:
Expand Down Expand Up @@ -87,7 +85,6 @@ def get_new_app_events(self):

# -------------------------------------------------------------------------
# Event processing

def process_event(self, event):
"""Process one AppEvent against all enabled workflows."""
evGuid = event["guid"]
Expand Down Expand Up @@ -116,7 +113,6 @@ def process_event(self, event):

# -------------------------------------------------------------------------
# Workflow execution

def execute_workflow(self, workflow, trigger):
"""Execute workflow actions if any condition group evaluates to True."""
wfName = workflow["name"]
Expand All @@ -130,11 +126,11 @@ def execute_workflow(self, workflow, trigger):
evaluator = ConditionGroup(condition_group)
if evaluator.evaluate(trigger):
mylog("none", f"[WF] Workflow {wfName} will be executed - conditions were evaluated as TRUE")
mylog("debug", [f"[WF] Workflow condition_group: {condition_group}"])
mylog("trace", [f"[WF] Workflow condition_group: {condition_group}"])
self.execute_actions(workflow["actions"], trigger)
return

mylog("none", ["[WF] No condition group matched. Actions not executed."])
mylog("trace", ["[WF] No condition group matched. Actions not executed."])

def _resolve_target_devices(self, action, trigger_device):
"""Return the list of device dicts that the action should be applied to.
Expand Down Expand Up @@ -180,7 +176,7 @@ def execute_actions(self, actions, trigger):
target_devices = self._resolve_target_devices(action, trigger_obj)

if not target_devices:
mylog("debug", [f"[WF] No target devices matched for action '{action_type}'"])
mylog("trace", [f"[WF] No target devices matched for action '{action_type}'"])
continue

for target_device in target_devices:
Expand Down
6 changes: 3 additions & 3 deletions server/workflows/triggers.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def __init__(self, triggerJson, event, db):
self.object_type == event["objectType"] and self.event_type == event["appEventType"]
)

mylog("debug", f"""[WF] self.triggered '{self.triggered}' for event '{get_array_from_sql_rows(event)} and trigger {json.dumps(triggerJson)}' """)
mylog("trace", f"""[WF] self.triggered '{self.triggered}' for event '{get_array_from_sql_rows(event)} and trigger {json.dumps(triggerJson)}' """)

if self.triggered:
# object type corresponds with the DB table name
Expand All @@ -45,10 +45,10 @@ def __init__(self, triggerJson, event, db):
WHERE {refField} = '{event["objectGuid"]}'
"""

mylog("debug", [query])
mylog("trace", [query])

result = db.sql.execute(query).fetchall()

if len(result) > 0:
self.object = result[0]
else:
Expand Down
Loading
Loading