The bake system for the website frequency of use page.
It analyzes the wsgi.log file and draw some statistics.
The bake system for the website frequency of use page.
It analyzes the wsgi.log file and draw some statistics.
Create the response for the index page.
The body->main of this page is created fully dynamically, there's no file to get content from.
def __init__(self):
"""Create the response for the index page.
The body->main of this page is created fully dynamically, there's no
file to get content from.
"""
super(StatsResponse, self).__init__(name=None, title=get_msg(MSG_TITLE_INDEX))
self.__logs = list()
Override. Create the page tree.
Set page name and add required js in the tree->head.
def create(self):
"""Override. Create the page tree.
Set page name and add required js in the tree->head.
"""
get_base_response_class().create(self)
self.set_pagename('consultations.html')
Fill-in the list of log contents and add notes to see the contents.
def load_and_view_logs(self):
"""Fill-in the list of log contents and add notes to see the contents.
"""
h2_logs = self._htree.element('h2')
h2_logs.set_value(get_msg(MSG_TITLE_SEE_LOGS))
for entry in os.listdir(whakerkit.sg.base_dir):
if entry.endswith('.log') is True:
with open(whakerkit.sg.base_dir + entry, 'r', encoding='utf-8') as f:
content = '\n'.join(f.readlines())
self.__logs.append(content)
content = content.replace('<', '<').replace('>', '>')
content = content.replace('\n\n', '\n')
content = content.replace('\n', '<br style="line-height: 0.5rem;">\n')
details_logs = self._htree.element('details')
summary_logs = HTMLNode(details_logs.identifier, None, 'summary', value=get_msg(MSG_EXPAND_FOR_CONTENT).format(entry=entry, content_len=len(content)))
details_logs.append_child(summary_logs)
div_logs = HTMLNode(details_logs.identifier, None, 'div', attributes={'class': 'font-mono', 'style': 'font-size: 80%;'}, value=content)
details_logs.append_child(div_logs)
Add nodes to see the access frequency of pages.
def pages_frequencies(self):
"""Add nodes to see the access frequency of pages.
"""
(pages, monthes) = self._get_pages()
max_value = 0
h2 = self._htree.element('h2')
h2.set_value(get_msg(MSG_TITLE_SEE_FREQ))
h3 = self._htree.element('h3')
h3.set_value(get_msg(MSG_FREQ_OVERALL))
table = self._htree.element('table')
table.add_attribute('role', 'grid')
table_content = list()
table_content.append(f'<thead><tr><th>{get_msg(MSG_PAGE_SENT)}</th><th>{get_msg(MSG_OCC)}</th></tr></thead>')
for page_name in pages:
count = 0
for m in pages[page_name]:
value = pages[page_name][m]
count += value
if value > max_value:
max_value = pages[page_name][m]
table_content.append(f'<tr><td>{page_name}</td><td>{count}</td></tr>')
table.set_value('\n'.join(table_content))
h3 = self._htree.element('h3')
h3.set_value(get_msg(MSG_FREQ_DETAILS))
for page_name in pages:
color = hex_code_colors()
t = self._htree.element('h4')
t.set_value(page_name)
t.set_attribute('style', f'color: {color};')
div = self._htree.element('div')
svg = self._generate_svg(monthes, pages[page_name], color, max_value)
div.set_value(svg)
Fill-in the list of log contents and add notes to see the contents.
def applied_filters(self):
"""Fill-in the list of log contents and add notes to see the contents.
"""
(found, not_found) = self._get_filters()
h2_logs = self._htree.element('h2')
h2_logs.set_value(get_msg(MSG_TITLE_SEE_FILTERS))
h3_logs = self._htree.element('h3')
h3_logs.set_value(get_msg(MSG_FILTERS_INVALID) + f' ({len(not_found)})')
t = self._htree.element('table')
for some_filter in not_found:
tr = HTMLNode(t.identifier, None, 'tr')
tr.set_value(self._add_filter_into_tr(some_filter))
t.append_child(tr)
h3_logs = self._htree.element('h3')
h3_logs.set_value(get_msg(MSG_FILTERS_VALID) + f' ({len(found)})')
t = self._htree.element('table')
for some_filter in found:
tr = HTMLNode(t.identifier, None, 'tr')
tr.set_value(self._add_filter_into_tr(some_filter))
t.append_child(tr)
Process the given events coming from the POST of any form.
def _process_events(self, events: dict, **kwargs) -> bool:
"""Process the given events coming from the POST of any form.
:param events: (dict) the posted events
:param kwargs: (dict) the keyword arguments
:return: (bool) True to bake the page, False otherwise
"""
logging.debug(f'StatsResponse._process_events: {events.keys()}.')
self._status.code = 200
return True
Create the dynamic content in the body->main of the page.
def _bake(self) -> None:
"""Create the dynamic content in the body->main of the page.
"""
h1 = self._htree.element('h1')
h1.set_value(get_msg(MSG_TITLE_HOME))
self.load_and_view_logs()
self.pages_frequencies()
self.applied_filters()
def _get_pages(self):
pages = dict()
monthes = list()
for content in self.__logs:
for line in content.split('\n'):
if 'Requested page name: ' in line:
tab = line.split(' ')
page_name = tab[-1]
full_date = tab[0].split('-')
try:
month = float(full_date[0] + '.' + full_date[1])
if page_name.endswith('.html') is True:
if page_name not in pages:
pages[page_name] = dict()
if month not in pages[page_name]:
pages[page_name][month] = 0
pages[page_name][month] += 1
if month not in monthes:
monthes.append(month)
except Exception as e:
logging.error(' ... ' + str(e))
return (pages, monthes)
Return an SVG image with an histogram.
def _generate_svg(self, monthes, page, color, max_value):
"""Return an SVG image with an histogram."""
w = 800
h = 400
margin = 20
step = (w - margin) / (len(monthes) + 1)
bar_width = w // ((len(monthes) + 1) * 2)
max_h = h - margin * 2
svg = list()
svg.append(f'<svg width="{w}" height="{h}">')
svg.append(f'<line x1="{margin}" y1="{margin}" x2="{margin}" y2="{h - margin}" stroke="black" stroke-width="2" />')
svg.append(f'<line x1="{margin}" y1="{h - margin}" x2="{w}" y2="{h - margin}" stroke="black" stroke-width="2" />')
for (i, m) in enumerate(sorted(monthes)):
svg.append(f'<text x="{step * (i + 1)}" y="{h - 4}" text-anchor="middle">{m}</text>')
for (i, m) in enumerate(sorted(monthes)):
if m in page:
value = page[m]
else:
value = 0
y = h - margin - value * max_h / max_value
svg.append(f'<rect x="{step * (i + 1) - bar_width // 2}" y="{y}" width="{bar_width}" height="{h - margin - y}" fill="{color}" class="getData" data-legend="{m}" />')
for (i, m) in enumerate(sorted(monthes)):
if m in page:
value = page[m]
else:
value = 0
y = h - margin - value * max_h / max_value
svg.append(f'<text x="{step * (i + 1)}" y="{y - 4}" text-anchor="middle" class="legend" id="{m}">{value}</text>')
svg.append(f'</svg>')
return '\n'.join(svg)
def _get_filters(self):
found = list()
not_found = list()
for content in self.__logs:
all_lines = content.split('\n')
i = 0
while i < len(all_lines):
if 'Apply filters: ' in all_lines[i]:
filters = list()
filters_date = None
while 'Found ' not in all_lines[i] and ' documents' not in all_lines[i]:
if 'Merging ' not in all_lines[i]:
if filters_date is None:
_tmp = all_lines[i].split(' ')
filters_date = _tmp[0] if len(_tmp) > 0 else None
if '[' in all_lines[i] and ']' in all_lines[i]:
s = all_lines[i].index('[')
e = all_lines[i].rindex(']')
applied_filter = self._applied_filter_to_list(all_lines[i][s + 1:e])
filters.append(applied_filter)
i += 1
if filters_date is not None:
if ' 0 ' in all_lines[i]:
not_found.append((filters_date, filters))
else:
found.append((filters_date, filters))
i += 1
return (found, not_found)
Return the content of a table row filled with the given filter.
@staticmethod
def _add_filter_into_tr(some_filter: tuple):
"""Return the content of a table row filled with the given filter."""
content = list()
content.append('<td>')
content.append(some_filter[0])
content.append('</td>')
content.append('<td><ul>')
for f in some_filter[1]:
if isinstance(f[0], tuple) is True:
for ff in f:
content.append('<li>')
content.append(' '.join((str(c) for c in ff)))
content.append('</li>')
else:
content.append('<li>')
content.append(' '.join((str(c) for c in f)))
content.append('</li>')
content.append('</ul></td>')
return '\n'.join(content)
@staticmethod
def _applied_filter_to_list(f):
try:
return ast.literal_eval(f)
except (SyntaxError, ValueError):
return None