WhakerKit 2.0

https://sourceforge.net/projects/whakerkit/

Module whakerkit.responses

Class StatsResponse

Description

The bake system for the website frequency of use page.

It analyzes the wsgi.log file and draw some statistics.

Constructor

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.

View Source
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()

Public functions

create

Override. Create the page tree.

Set page name and add required js in the tree->head.

View Source
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')

load_and_view_logs

Fill-in the list of log contents and add notes to see the contents.

View Source
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('<', '&lt;').replace('>', '&gt;')
                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)

pages_frequencies

Add nodes to see the access frequency of pages.

View Source
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)

applied_filters

Fill-in the list of log contents and add notes to see the contents.

View Source
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)

Private functions

_process_events

Process the given events coming from the POST of any form.

Parameters
  • events: (dict) the posted events
  • kwargs: (dict) the keyword arguments
Returns
  • (bool) True to bake the page, False otherwise
View Source
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

_bake

Create the dynamic content in the body->main of the page.

View Source
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()

_get_pages

View Source
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)

_generate_svg

Return an SVG image with an histogram.

Parameters
  • monthes
  • page
  • color
  • max_value
View Source
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)

_get_filters

View Source
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)

_add_filter_into_tr

Return the content of a table row filled with the given filter.

Parameters
  • some_filter
View Source
@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)

_applied_filter_to_list

View Source
@staticmethod
def _applied_filter_to_list(f):
    try:
        return ast.literal_eval(f)
    except (SyntaxError, ValueError):
        return None