Arrange to remeber the tab selected in web page

This commit is contained in:
Brian Read 2024-07-15 06:35:15 +01:00
parent c7cf518477
commit a1c9a698ee
2 changed files with 76 additions and 83 deletions

View File

@ -54,10 +54,10 @@
<br /> <br />
<!--Tabs --> <!--Tabs -->
<div class="tab-container"> <div class="tab-container">
<div class="tab tab-active" onclick="openTab(event, 'tab1')">Table</div> <div class="tab tab-active" data-index="0" onclick="openTab(event, 'tab1')">Table</div>
<div class="tab" onclick="openTab(event, 'tab2')">Stacked Bar Graph</div> <div class="tab" data-index="1" onclick="openTab(event, 'tab2')">Stacked Bar Graph</div>
<div class="tab" onclick="openTab(event, 'tab3')">Heat Map</div> <div class="tab" data-index="2" onclick="openTab(event, 'tab3')">Heat Map</div>
<div class="tab" onclick="openTab(event, 'tab4')">Line Graph</div> <div class="tab" data-index="3" onclick="openTab(event, 'tab4')">Line Graph</div>
</div> </div>
<div id="tab1" class="tab-content tab-content-active"> <div id="tab1" class="tab-content tab-content-active">
@ -125,18 +125,54 @@
<script> <script>
window.onload = function(){doNavs();} window.onload = function(){doNavs();}
function openTab(event, tabId){ //function openTab(event, tabId){
// Get all elements with class="tab-content" and hide them //// Get all elements with class="tab-content" and hide them
const tabContents = document.querySelectorAll('.tab-content'); //const tabContents = document.querySelectorAll('.tab-content');
tabContents.forEach(content => content.classList.remove('tab-content-active')); //tabContents.forEach(content => content.classList.remove('tab-content-active'));
//// Get all elements with class="tab" and remove the class "tab-active"
//const tabs = document.querySelectorAll('.tab');
//tabs.forEach(tab => tab.classList.remove('tab-active'));
//// Show the current tab content, and add an "active" class to the clicked tab
//document.getElementById(tabId).classList.add('tab-content-active');
//event.target.classList.add('tab-active');}
function openTab(evt, tabName) {
// Declare all variables
var i, tab_content, tab;
// Get all elements with class="tab_content" and hide them
tab_content = document.getElementsByClassName("tab-content");
for (i = 0; i < tab_content.length; i++) {
tab_content[i].style.display = "none";
}
// Get all elements with class="tab" and remove the class "active"
tab = document.getElementsByClassName("tab");
for (i = 0; i < tab.length; i++) {
tab[i].className = tab[i].className.replace(" tab-active", "");
}
// Show the current tab, and add an "active" class to the link that opened the tab
document.getElementById(tabName).style.display = "block";
evt.currentTarget.className += " tab-active";
// Store the active tab index
sessionStorage.setItem('activeTab', evt.currentTarget.getAttribute("data-index"));
}
document.addEventListener("DOMContentLoaded", function() {
// Get the stored tab index from sessionStorage
let activeTab = sessionStorage.getItem('activeTab');
// If there's a stored tab, show that tab
if (activeTab !== null) {
document.querySelector(`[data-index="$${activeTab}"]`).click();
}
});
// Get all elements with class="tab" and remove the class "tab-active"
const tabs = document.querySelectorAll('.tab');
tabs.forEach(tab => tab.classList.remove('tab-active'));
// Show the current tab content, and add an "active" class to the clicked tab
document.getElementById(tabId).classList.add('tab-content-active');
event.target.classList.add('tab-active');}
</script> </script>
<p class="cssvalid"> <p class="cssvalid">

View File

@ -213,10 +213,6 @@ def create_stacked_bar_graph(data2d, xLabels, yLabels, save_path='stacked_bar_gr
# Get unique colors for each category # Get unique colors for each category
extended_colors = generate_distinct_colors(len(filtered_xLabels)) extended_colors = generate_distinct_colors(len(filtered_xLabels))
#print(len(filtered_xLabels))
#print(extended_colors)
#quit()
for i, category in enumerate(filtered_xLabels): for i, category in enumerate(filtered_xLabels):
fig.add_trace(go.Bar( fig.add_trace(go.Bar(
name=category, name=category,
@ -244,7 +240,7 @@ def create_stacked_bar_graph(data2d, xLabels, yLabels, save_path='stacked_bar_gr
# Save the graph to an HTML file # Save the graph to an HTML file
fig.write_html(save_path) fig.write_html(save_path)
# Write it to a var and return the string # Write it to a var and return the string
graph_html = fig.to_html(full_html=False) graph_html = fig.to_html(full_html=False,include_plotlyjs='https://cdn.plot.ly/plotly-latest.min.js')
return graph_html return graph_html
def sanitize_and_filter_data(data2d, exclude_labels, xLabels): def sanitize_and_filter_data(data2d, exclude_labels, xLabels):
@ -331,7 +327,7 @@ def create_heatmap(data2d, xLabels, yLabels, save_path='heatmap.html'):
fig.write_html(save_path) fig.write_html(save_path)
# Write it to a var and return the string # Write it to a var and return the string
graph_html = fig.to_html(full_html=False) graph_html = fig.to_html(full_html=False,include_plotlyjs='https://cdn.plot.ly/plotly-latest.min.js')
return graph_html return graph_html
@ -372,7 +368,7 @@ def create_line_chart(data2d, xLabels, yLabels, save_path='line_chart.html'):
fig.write_html(save_path) fig.write_html(save_path)
# Write it to a var and return the string # Write it to a var and return the string
graph_html = fig.to_html(full_html=False) graph_html = fig.to_html(full_html=False,include_plotlyjs='https://cdn.plot.ly/plotly-latest.min.js')
return graph_html return graph_html
@ -522,14 +518,11 @@ def read_in_relevant_log_file(file_path,analysis_date=yesterday):
timestamp = datetime.strptime(timestamp_str, "%Y-%m-%d %H:%M:%S") timestamp = datetime.strptime(timestamp_str, "%Y-%m-%d %H:%M:%S")
except ValueError as e: except ValueError as e:
print(f"ValueError {e} on timestamp extract {timestamp_str}:{entry[1]}") print(f"ValueError {e} on timestamp extract {timestamp_str}:{entry[1]}")
#print(f"{timestamp.date()} {analysis_date.date()}")
#quit()
if timestamp.date() == analysis_date.date(): if timestamp.date() == analysis_date.date():
log_entries.append((timestamp, entry[1])) log_entries.append((timestamp, entry[1]))
else: else:
ignore_record_count += 1 ignore_record_count += 1
except UnicodeDecodeError as e: except UnicodeDecodeError as e:
#print(f"{Line} {len(log_entries)} {e} ")
pass pass
return [log_entries,skip_record_count,ignore_record_count] return [log_entries,skip_record_count,ignore_record_count]
@ -538,8 +531,6 @@ def filter_summary_records(log_entries):
filtered_log_entries = [] filtered_log_entries = []
skipped_entry_count = 0 skipped_entry_count = 0
for line in log_entries: for line in log_entries:
#print(line)
#quit()
if '`' in line[1]: if '`' in line[1]:
filtered_log_entries.append(line) filtered_log_entries.append(line)
else: else:
@ -558,7 +549,6 @@ def parse_data(data):
# Adjust the field names and parsing logic according to your data format. # Adjust the field names and parsing logic according to your data format.
# Split at the backtick - before it fields split at space, after, fields split at tab # Split at the backtick - before it fields split at space, after, fields split at tab
parts = data.split('`') parts = data.split('`')
#print(f"{parts[0]}:{parts[1]}")
fields1 = parts[0].strip().split() if len(parts) > 0 else [] fields1 = parts[0].strip().split() if len(parts) > 0 else []
fields2 = parts[1].split('\t') if len(parts) > 1 else [] fields2 = parts[1].split('\t') if len(parts) > 1 else []
# then merge them # then merge them
@ -594,8 +584,6 @@ def parse_data(data):
except: except:
#print(f"error:len:{len(fields)}") #print(f"error:len:{len(fields)}")
return_dict = {} return_dict = {}
#print(return_dict)
#quit()
return return_dict return return_dict
def count_entries_by_hour(log_entries): def count_entries_by_hour(log_entries):
@ -789,16 +777,12 @@ def read_html_from_file(filepath):
with open(filepath, 'r', encoding='utf-8') as file: with open(filepath, 'r', encoding='utf-8') as file:
html_contents = file.read() html_contents = file.read()
print("reading from html file") print("reading from html file")
#print(len(html_contents))
# Get Filepath # Get Filepath
css_path = os.path.dirname(filepath)+"/../css/mailstats.css" css_path = os.path.dirname(filepath)+"/../css/mailstats.css"
#print(css_path)
# Read in CSS # Read in CSS
with open(css_path, 'r', encoding='utf-8') as file: with open(css_path, 'r', encoding='utf-8') as file:
css_contents = file.read() css_contents = file.read()
#print(len(css_contents))
html_contents = insert_string_after(html_contents,"\n"+css_contents,"<!--css here-->") html_contents = insert_string_after(html_contents,"\n"+css_contents,"<!--css here-->")
#print(len(html_contents))
return html_contents return html_contents
def read_text_from_file(filepath): def read_text_from_file(filepath):
@ -1036,8 +1020,6 @@ if __name__ == "__main__":
cursor.execute(delete_query, (analysis_date,)) #Don't forget the syntactic sugar of the extra comma to make it a tuple! cursor.execute(delete_query, (analysis_date,)) #Don't forget the syntactic sugar of the extra comma to make it a tuple!
# Get the number of records deleted # Get the number of records deleted
rows_deleted = cursor.rowcount rows_deleted = cursor.rowcount
print(rows_deleted)
#quit()
if rows_deleted > 0: if rows_deleted > 0:
print(f"Deleted {rows_deleted} rows for {analysis_date} ") print(f"Deleted {rows_deleted} rows for {analysis_date} ")
except mysql.connector.Error as e: except mysql.connector.Error as e:
@ -1073,10 +1055,6 @@ if __name__ == "__main__":
log_file = logs_dir+'current.log' log_file = logs_dir+'current.log'
log_entries,skip_count,ignored_count = read_in_relevant_log_file(log_file,anaysis_date_obj) log_entries,skip_count,ignored_count = read_in_relevant_log_file(log_file,anaysis_date_obj)
# if len(log_entries) == 0:
# print(f"No records found in {log_file}")
# quit()
# else:
print(f"Found {len(log_entries)} entries in log for for {anaysis_date_obj.strftime('%Y-%m-%d')} Ignored: {ignored_count} skipped: {skip_count}") print(f"Found {len(log_entries)} entries in log for for {anaysis_date_obj.strftime('%Y-%m-%d')} Ignored: {ignored_count} skipped: {skip_count}")
summary_log_entries,skip_count = filter_summary_records(log_entries) summary_log_entries,skip_count = filter_summary_records(log_entries)
print(f"Found {len(summary_log_entries)} summary entries and skipped {skip_count} entries") print(f"Found {len(summary_log_entries)} summary entries and skipped {skip_count} entries")
@ -1220,7 +1198,6 @@ if __name__ == "__main__":
match = re.search(spam_pattern, parsed_data['spam-status']) match = re.search(spam_pattern, parsed_data['spam-status'])
if match: if match:
score = float(match.group(1)) score = float(match.group(1))
#print(score,SATagLevel)
if score < float(SATagLevel): if score < float(SATagLevel):
# Accumulate allowed score (inc negatives?) # Accumulate allowed score (inc negatives?)
hamavg += score hamavg += score
@ -1235,7 +1212,6 @@ if __name__ == "__main__":
if match: if match:
score = float(match.group(1)) score = float(match.group(1))
required = float(match.group(2)) required = float(match.group(2))
#print(f"{parsed_data['spam-status']} / {score} {required}")
if score >= SARejectLevel: if score >= SARejectLevel:
columnCounts_2d[hour][DelSpam] += 1 columnCounts_2d[hour][DelSpam] += 1
columnCounts_2d[ColTotals][DelSpam] += 1 columnCounts_2d[ColTotals][DelSpam] += 1
@ -1251,9 +1227,7 @@ if __name__ == "__main__":
# Count the qpsmtpd codes # Count the qpsmtpd codes
if parsed_data['error-plugin'].strip() == 'naughty': if parsed_data['error-plugin'].strip() == 'naughty':
#print(f"Found naughty {parsed_data['error-msg']}")
if parsed_data['error-msg'].startswith("(dnsbl)"): if parsed_data['error-msg'].startswith("(dnsbl)"):
#print("Found dnsbl")
columnCounts_2d[hour][RBLDNS]+= 1 columnCounts_2d[hour][RBLDNS]+= 1
columnCounts_2d[ColTotals][RBLDNS]+= 1 columnCounts_2d[ColTotals][RBLDNS]+= 1
elif parsed_data['error-msg'].startswith("(karma)"): elif parsed_data['error-msg'].startswith("(karma)"):
@ -1272,12 +1246,9 @@ if __name__ == "__main__":
#Now increment the column which the plugin name indicates #Now increment the column which the plugin name indicates
if parsed_data['action'] == '(deny)' and parsed_data['error-plugin']: if parsed_data['action'] == '(deny)' and parsed_data['error-plugin']:
#print(f"Found plugin {parsed_data['error-plugin']}")
if parsed_data['error-plugin']: if parsed_data['error-plugin']:
row = search_2d_list(parsed_data['error-plugin'],columnPlugin) row = search_2d_list(parsed_data['error-plugin'],columnPlugin)
#print(row,parsed_data['error-plugin'])
if not row == -1: if not row == -1:
#print(f"Found row: {row}")
columnCounts_2d[hour][row] += 1 columnCounts_2d[hour][row] += 1
columnCounts_2d[ColTotals][row] += 1 columnCounts_2d[ColTotals][row] += 1
# a few ad hoc extra extractons of data # a few ad hoc extra extractons of data
@ -1345,6 +1316,7 @@ if __name__ == "__main__":
print_progress_bar(i, log_len, prefix='Scanning for sub tables:', suffix='Complete', length=50) print_progress_bar(i, log_len, prefix='Scanning for sub tables:', suffix='Complete', length=50)
# Match initial connection message # Match initial connection message
try:
match = helo_pattern.match(data[1]) match = helo_pattern.match(data[1])
if match: if match:
ip = match.group(1) ip = match.group(1)
@ -1354,8 +1326,12 @@ if __name__ == "__main__":
else: else:
totalexternalsmtpsessions += 1 totalexternalsmtpsessions += 1
continue continue
except Exception as e:
print(f" Helo pattern error {e} {data[1]} {analysis_date}")
continue
#Pull out Geoip countries for analysis table #Pull out Geoip countries for analysis table
try:
match = geoip_pattern.match(data[1]) match = geoip_pattern.match(data[1])
if match: if match:
j += 1 j += 1
@ -1363,6 +1339,9 @@ if __name__ == "__main__":
found_countries[country] += 1 found_countries[country] += 1
total_countries += 1 total_countries += 1
continue continue
except Exception as e:
print(f" Geoip pattern error {e} {data[1]} {analysis_date}")
continue
#Pull out DMARC approvals #Pull out DMARC approvals
match = dmarc_pattern.match(data[1]) match = dmarc_pattern.match(data[1])
@ -1377,9 +1356,6 @@ if __name__ == "__main__":
connection_type_counts[connection_type] += 1 connection_type_counts[connection_type] += 1
continue continue
#print(columnCounts_2d)
#quit()
#Compute next and previous dates #Compute next and previous dates
day_format = "%Y-%m-%d" day_format = "%Y-%m-%d"
# Convert the time string to a datetime object # Convert the time string to a datetime object
@ -1451,25 +1427,6 @@ if __name__ == "__main__":
cursor.close() cursor.close()
conn.close() conn.close()
# #Add in navigation html - next/previous/see in browser
# navigation_str_html = "<div class='linksattop'>\
# <a class='prevlink' href='http://${DomainName}/mailstats/mailstats_for_${PreviousDate}.html'>Previous</a>\
# <div class='divshowindex'><a class='showindex' href='http://${DomainName}/mailstats/'>Index of files</a></div>\
# <a class='nextlink' href='http://${DomainName}/mailstats/mailstats_for_${NextDate}.html'>Next</a>\
# </div>"
# try:
# template = PageTemplate(navigation_str_html)
# try:
# Nav_str = template(PreviousDate=previous_date_str,NextDate=next_date_str,TodayDate=analysis_date,DomainName=DomainName)
# except Exception as e:
# print(f"Chameleon nav template Exception {e}")
# except Exception as e:
# print(f"Chameleon nav render Exception {e}")
# # And insert it
# total_html = insert_string_after(total_html,Nav_str, "<!---Navigation here-->")
# Write the rendered HTML to a file # Write the rendered HTML to a file
output_path = html_page_dir+'mailstats_for_'+analysis_date output_path = html_page_dir+'mailstats_for_'+analysis_date
output_path = output_path.replace(' ','_') output_path = output_path.replace(' ','_')