2026-06-02 00:47:03 -04:00
import json
from collections import defaultdict
from datetime import datetime
from flask import session
from config_utils import (
2026-06-02 12:49:39 -04:00
collect_layout_tokens , get_dashboard_pending , load_all_groups , get_done_timestamps ,
2026-06-02 00:47:03 -04:00
_apply_changes_immediately , _find_cmd_in_queues , WEB_APP_DISPLAY_NAME ,
)
2026-06-02 12:49:39 -04:00
from factory import LEVEL_RANK , e , client_level , build_snap_val , snap_expand_row , load_icon
2026-06-02 00:47:03 -04:00
def collect_tokens ( cfg ) :
2026-06-02 12:49:39 -04:00
tokens = collect_layout_tokens ( cfg )
2026-06-02 00:47:03 -04:00
tokens [ ' GENERAL_APPLY_ON_SAVE ' ] = ' true ' if session . get ( ' apply_changes_immediately ' , False ) else ' false '
all_groups = load_all_groups ( )
group_uuid_set = { g [ ' uuid ' ] for g , _ in all_groups }
pending_items = get_dashboard_pending ( )
if pending_items :
pgroups = defaultdict ( list )
for uuid , ts , cmd , user in pending_items :
pgroups [ cmd ] . append ( ( uuid , user ) )
rows = ' '
for cmd , entries in pgroups . items ( ) :
users = ' , ' . join ( sorted ( { u for _ , u in entries if u and u != ' unknown ' } ) )
snap_uuids = [ uuid for uuid , _ in entries if uuid in group_uuid_set ]
if snap_uuids :
req_tags = ' ' . join (
f ' <span class= " tag " data-tooltip= " { uuid } " data-uuid= " { uuid } " > '
f ' <span class= " tl-full " > { uuid [ : 8 ] } </span> '
f ' <span class= " tl-short " > { uuid [ : 8 ] } </span> '
f ' <span class= " tl-min " > { uuid [ : 8 ] } </span> '
' </span> '
for uuid in snap_uuids
)
req_cell = f ' <td class= " table-cell " ><div class= " tag-list " > { req_tags } </div></td> '
else :
req_cell = ' <td class= " table-cell " >-</td> '
rows + = (
' <tr> '
f ' <td class= " table-cell " > { e ( cmd ) } </td> '
f ' <td class= " table-cell " > { e ( users ) } </td> '
f ' { req_cell } '
' </tr> '
)
pending_html = (
' <table class= " data-table " ><thead><tr> '
' <th class= " table-header " >Command</th> '
' <th class= " table-header " >User</th> '
' <th class= " table-header " >Required By</th> '
' </tr></thead> '
f ' <tbody> { rows } </tbody></table> '
)
else :
pending_html = ' <p class= " text-muted " >No pending actions.</p> '
tokens [ ' PENDING_ACTIONS_HTML ' ] = pending_html
tokens [ ' NO_PENDING ' ] = ' true ' if not pending_items else ' '
tokens [ ' NO_DISMISSIBLE_PENDING ' ] = ' true ' if not any ( c != ' fix problems ' for _ , _ , c , _ in pending_items ) else ' '
tokens [ ' APPLY_WARNING ' ] = (
2026-06-02 12:49:39 -04:00
f ' <span style= " color:var(--warning) " ><p> { load_icon ( " arrow-left " ) } <strong>Applying actions will briefly disrupt connections as network services are restarted.</strong></p></span> '
2026-06-02 00:47:03 -04:00
if pending_items else ' '
)
done_ts_map = get_done_timestamps ( )
if all_groups :
2026-06-03 15:31:39 -04:00
level = client_level ( )
is_manager = LEVEL_RANK . get ( level , 0 ) > = LEVEL_RANK . get ( ' manager ' , 0 )
2026-06-02 00:47:03 -04:00
no_revert = set ( )
for g , _ in all_groups :
if g [ ' reverts_group ' ] :
no_revert . add ( g [ ' uuid ' ] )
no_revert . add ( g [ ' reverts_group ' ] )
hist_rows = ' '
hist_onclick = (
' onclick= " if(event.target.type!== \' checkbox \' ) '
' this.nextElementSibling.hidden=!this.nextElementSibling.hidden " '
)
for g , changes in all_groups :
uuid = g [ ' uuid ' ]
applied_ts = done_ts_map . get ( uuid )
dt_str = datetime . fromtimestamp ( applied_ts ) . strftime ( ' % Y- % m- %d % H: % M ' ) if applied_ts else ' - '
all_before_null = all ( c [ ' before ' ] is None for c in changes )
all_after_null = all ( c [ ' after ' ] is None for c in changes )
if g [ ' reverts_group ' ] :
verb = ' Reverted '
elif all_before_null :
verb = ' Added '
elif all_after_null :
verb = ' Deleted '
else :
verb = ' Edited '
item = g . get ( ' item_value ' ) or ' '
summary = f ' { verb } { g [ " parent_path " ] } : { item } ' if item else f ' { verb } { g [ " parent_path " ] } '
snap_tag = (
f ' <div class= " tag-list " ><span class= " tag " data-tooltip= " { e ( uuid ) } " data-uuid= " { e ( uuid ) } " > '
f ' <span class= " tl-full " > { e ( uuid [ : 8 ] ) } </span> '
f ' <span class= " tl-short " > { e ( uuid [ : 8 ] ) } </span> '
f ' <span class= " tl-min " > { e ( uuid [ : 8 ] ) } </span> '
' </span></div> '
)
snap_user = e ( g . get ( ' user ' , ' ' ) )
2026-06-03 15:31:39 -04:00
cb_attrs = ' ' if is_manager else ( ' disabled title= " Cannot revert " ' if uuid in no_revert else ' ' )
2026-06-02 00:47:03 -04:00
hist_rows + = (
f ' <tr class= " row-expandable " data-uuid= " { e ( uuid ) } " { hist_onclick } > '
f ' <td class= " table-cell " ><input type= " checkbox " name= " selected_uuids " value= " { e ( uuid ) } " { cb_attrs } /></td> '
f ' <td class= " table-cell " > { e ( dt_str ) } </td> '
f ' <td class= " table-cell " > { e ( summary ) } </td> '
f ' <td class= " table-cell " > { build_snap_val ( changes ) } </td> '
f ' <td class= " table-cell " > { snap_tag } </td> '
f ' <td class= " table-cell " > { snap_user } </td> '
' </tr> '
f ' { snap_expand_row ( changes , 6 ) } '
)
select_all = (
' <input type= " checkbox " '
' onchange= " document.querySelectorAll( \' [name=selected_uuids]:not(:disabled) \' ).forEach(c=>c.checked=this.checked) " /> '
)
history_html = (
' <table class= " data-table " ><thead><tr> '
f ' <th class= " table-header " > { select_all } </th> '
' <th class= " table-header " >Applied</th> '
' <th class= " table-header " >Change</th> '
' <th class= " table-header " >Fields</th> '
' <th class= " table-header " >Change ID</th> '
' <th class= " table-header " >User</th> '
' </tr></thead> '
f ' <tbody> { hist_rows } </tbody></table> '
)
else :
history_html = ' <p class= " text-muted " >No change history.</p> '
tokens [ ' CHANGE_HISTORY_HTML ' ] = history_html
tokens [ ' NO_HISTORY ' ] = ' true ' if not all_groups else ' '
return tokens