484 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			484 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# -*- coding: utf-8 -*-
 | 
						|
#
 | 
						|
# Copyright Grégory Soutadé 2015
 | 
						|
 | 
						|
# This file is part of iwla
 | 
						|
 | 
						|
# iwla is free software: you can redistribute it and/or modify
 | 
						|
# it under the terms of the GNU General Public License as published by
 | 
						|
# the Free Software Foundation, either version 3 of the License, or
 | 
						|
# (at your option) any later version.
 | 
						|
#
 | 
						|
# iwla is distributed in the hope that it will be useful,
 | 
						|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
# GNU General Public License for more details.
 | 
						|
#
 | 
						|
# You should have received a copy of the GNU General Public License
 | 
						|
# along with iwla.  If not, see <http://www.gnu.org/licenses/>.
 | 
						|
#
 | 
						|
 | 
						|
import os
 | 
						|
import codecs
 | 
						|
import time
 | 
						|
import logging
 | 
						|
 | 
						|
#
 | 
						|
# Create output HTML files
 | 
						|
#
 | 
						|
 | 
						|
# Just for detection
 | 
						|
def _(name): pass
 | 
						|
_('January'), _('February'), _('March'), _('April'), _('May'), _('June'), _('July')
 | 
						|
_('August'), _('September'), _('October'), _('November'), _('December')
 | 
						|
del _
 | 
						|
 | 
						|
class DisplayHTMLRaw(object):
 | 
						|
 | 
						|
    def __init__(self, iwla, html=u''):
 | 
						|
        self.iwla = iwla
 | 
						|
        self.html = html
 | 
						|
 | 
						|
    def resetHTML(self):
 | 
						|
        self.html = ''
 | 
						|
        
 | 
						|
    def setRawHTML(self, html):
 | 
						|
        self.html = html
 | 
						|
 | 
						|
    def _buildHTML(self):
 | 
						|
        pass
 | 
						|
 | 
						|
    def _build(self, f, html):
 | 
						|
        if html: f.write(html)
 | 
						|
 | 
						|
    def build(self, f, filters=None):
 | 
						|
        if filters: self.filter(filters)
 | 
						|
        self._buildHTML()
 | 
						|
        self._build(f, self.html)
 | 
						|
 | 
						|
    def _filter(self, function, **kwargs):
 | 
						|
        pass
 | 
						|
 | 
						|
    def filter(self, filters):
 | 
						|
        for (args, function) in filters:
 | 
						|
            self._filter(function, **args)
 | 
						|
 | 
						|
    def getTitle(self):
 | 
						|
        return ''
 | 
						|
 | 
						|
class DisplayHTMLBlock(DisplayHTMLRaw):
 | 
						|
 | 
						|
    def __init__(self, iwla, title=''):
 | 
						|
        super(DisplayHTMLBlock, self).__init__(iwla, html='')
 | 
						|
        self.title = title
 | 
						|
        self.cssclass = u'iwla_block'
 | 
						|
        self.title_cssclass = u'iwla_block_title'
 | 
						|
        self.value_cssclass = u'iwla_block_value'
 | 
						|
 | 
						|
    def getTitle(self):
 | 
						|
        return self.title
 | 
						|
 | 
						|
    def setTitle(self, value):
 | 
						|
        self.title = value
 | 
						|
        
 | 
						|
    def setCSSClass(self, cssclass):
 | 
						|
        self.cssclass = cssclass
 | 
						|
 | 
						|
    def setTitleCSSClass(self, cssclass):
 | 
						|
        self.title_cssclass = cssclass
 | 
						|
 | 
						|
    def setValueCSSClass(self, cssclass):
 | 
						|
        self.value_cssclass = cssclass
 | 
						|
 | 
						|
    def _buildHTML(self):
 | 
						|
        html = u'<div class="%s">' % (self.cssclass)
 | 
						|
        if self.title:
 | 
						|
            html += u'<div class="%s">%s</div>' % (self.title_cssclass, self.title)
 | 
						|
        html += u'<div class="%s">%s</div>' % (self.value_cssclass, self.html)
 | 
						|
        html += u'</div>'
 | 
						|
 | 
						|
        self.html = html
 | 
						|
 | 
						|
class DisplayHTMLBlockTable(DisplayHTMLBlock):
 | 
						|
    
 | 
						|
    def __init__(self, iwla, title, cols, human_readable_cols=None):
 | 
						|
        super(DisplayHTMLBlockTable, self).__init__(iwla=iwla, title=title)
 | 
						|
        self.cols = listToStr(cols)
 | 
						|
        self.rows = []
 | 
						|
        self.cols_cssclasses = [u''] * len(cols)
 | 
						|
        self.rows_cssclasses = []
 | 
						|
        self.table_css = u'iwla_table'
 | 
						|
        self.human_readable_cols = human_readable_cols or []
 | 
						|
        self.objects = []
 | 
						|
        
 | 
						|
    def appendRow(self, row, _object=None):
 | 
						|
        self.rows.append(listToStr(row))
 | 
						|
        self.rows_cssclasses.append([u''] * len(row))
 | 
						|
        self.objects.append(_object)
 | 
						|
 | 
						|
    def insertCol(self, col_number, col_title='', col_css_class=''):
 | 
						|
        self.cols.insert(col_number, col_title)
 | 
						|
        for r in self.rows:
 | 
						|
            r.insert(col_number, u'')
 | 
						|
        for r in self.rows_cssclasses:
 | 
						|
            v = r[0]
 | 
						|
            # If all cells have the same CSS class, set it
 | 
						|
            for cur_value in r:
 | 
						|
                if v != cur_value:
 | 
						|
                    v = None
 | 
						|
                    break
 | 
						|
            v = v or u''
 | 
						|
            r.insert(col_number, v)
 | 
						|
        self.cols_cssclasses.insert(col_number, col_css_class)
 | 
						|
 | 
						|
    def getNbRows(self):
 | 
						|
        return len(self.rows)
 | 
						|
 | 
						|
    def getNbCols(self):
 | 
						|
        return len(self.cols)
 | 
						|
 | 
						|
    def getCellValue(self, row, col):
 | 
						|
        if row < 0 or col < 0 or\
 | 
						|
           row >= len(self.rows) or col >= len(self.cols):
 | 
						|
            raise ValueError('Invalid indices %d,%d' % (row, col))
 | 
						|
 | 
						|
        return self.rows[row][col]
 | 
						|
 | 
						|
    def getRowObject(self, row):
 | 
						|
        if row < 0 or row >= len(self.rows):
 | 
						|
            raise ValueError('Invalid indices %d' % (row))
 | 
						|
 | 
						|
        return self.objects[row]
 | 
						|
 | 
						|
    def setCellValue(self, row, col, value):
 | 
						|
        if row < 0 or col < 0 or\
 | 
						|
           row >= len(self.rows) or col >= len(self.cols):
 | 
						|
            raise ValueError('Invalid indices %d,%d' % (row, col))
 | 
						|
 | 
						|
        self.rows[row][col] = value
 | 
						|
 | 
						|
    def setCellCSSClass(self, row, col, value):
 | 
						|
        if row < 0 or col < 0 or\
 | 
						|
           row >= len(self.rows) or col >= len(self.cols):
 | 
						|
            raise ValueError('Invalid indices %d,%d' % (row, col))
 | 
						|
 | 
						|
        self.rows_cssclasses[row][col] = value
 | 
						|
 | 
						|
    def getCellCSSClass(self, row, col):
 | 
						|
        if row < 0 or col < 0 or\
 | 
						|
           row >= len(self.rows) or col >= len(self.cols):
 | 
						|
            raise ValueError('Invalid indices %d,%d' % (row, col))
 | 
						|
 | 
						|
        return self.rows_cssclasses[row][col]
 | 
						|
 | 
						|
    def getColCSSClass(self, col):
 | 
						|
        if col < 0 or col >= len(self.cols):
 | 
						|
            raise ValueError('Invalid indice %d' % (col))
 | 
						|
 | 
						|
        return self.cols_cssclasses[col]
 | 
						|
 | 
						|
    def setRowCSSClass(self, row, value):
 | 
						|
        if row < 0 or row >= len(self.rows):
 | 
						|
            raise ValueError('Invalid indice %d' % (row))
 | 
						|
 | 
						|
        self.rows_cssclasses[row] = [value] * len(self.rows_cssclasses[row])
 | 
						|
 | 
						|
    def setColCSSClass(self, col, value):
 | 
						|
        if col < 0 or col >= len(self.cols):
 | 
						|
            raise ValueError('Invalid indice %d' % (col))
 | 
						|
 | 
						|
        self.cols_cssclasses[col] = value
 | 
						|
 | 
						|
    def setColsCSSClass(self, values):
 | 
						|
        if len(values) != len(self.cols):
 | 
						|
            raise ValueError('Invalid values size')
 | 
						|
 | 
						|
        self.cols_cssclasses = listToStr(values)
 | 
						|
 | 
						|
    def computeRatio(self, column, column_insertion=None):
 | 
						|
        if column_insertion is None:
 | 
						|
            column_insertion = column+1
 | 
						|
 | 
						|
        total = 0
 | 
						|
        for r in self.rows:
 | 
						|
            if r[column]:
 | 
						|
                total += int(r[column])
 | 
						|
 | 
						|
        self.insertCol(column_insertion, self.iwla._('Ratio'), u'iwla_hit')
 | 
						|
        for (index, r) in enumerate(self.rows):
 | 
						|
            val = r[column] and int(r[column]) or 0
 | 
						|
            self.setCellValue(index, column_insertion, '%.1f%%' % (total and float(val*100)/float(total) or 0))
 | 
						|
 | 
						|
    def _filter(self, function, column, args):
 | 
						|
        target_col = None
 | 
						|
        for col in range(0, len(self.cols)):
 | 
						|
            if self.cols[col] == column:
 | 
						|
                target_col = col
 | 
						|
                break
 | 
						|
        if target_col is None: return
 | 
						|
        for idx, row in enumerate(self.rows):
 | 
						|
            res = function(row[target_col], self.objects[idx], **args)
 | 
						|
            if res is not None:
 | 
						|
                row[target_col] = res
 | 
						|
 | 
						|
    def _buildHTML(self):
 | 
						|
        style = u''
 | 
						|
        if self.table_css: style = u' class="%s"' % (self.table_css)
 | 
						|
        html = u'<table%s>' % (style)
 | 
						|
        if self.cols:
 | 
						|
            html += u'<tr>'
 | 
						|
            for i in range (0, len(self.cols)):
 | 
						|
                title = self.cols[i]
 | 
						|
                style = self.getColCSSClass(i)
 | 
						|
                if style: style = u' class="%s"' % (style)
 | 
						|
                html += u'<th%s>%s</th>' % (style, title)
 | 
						|
            html += u'</tr>'
 | 
						|
        for i in range(0, len(self.rows)):
 | 
						|
            row = self.rows[i]
 | 
						|
            html += u'<tr>'
 | 
						|
            for j in range(0, len(row)):
 | 
						|
                v = row[j]
 | 
						|
                if j in self.human_readable_cols:
 | 
						|
                    v = bytesToStr(v)
 | 
						|
                style = self.getCellCSSClass(i, j)
 | 
						|
                if style: style = u' class="%s"' % (style)
 | 
						|
                html += u'<td%s>%s</td>' % (style, v)
 | 
						|
            html += u'</tr>'
 | 
						|
        html += u'</table>'
 | 
						|
 | 
						|
        self.html += html
 | 
						|
 | 
						|
        super(DisplayHTMLBlockTable, self)._buildHTML()
 | 
						|
 | 
						|
class DisplayHTMLBlockTableWithGraph(DisplayHTMLBlockTable):
 | 
						|
 | 
						|
    def __init__(self, iwla, title, cols, short_titles=None, nb_valid_rows=0, graph_cols=None,
 | 
						|
                 human_readable_cols=None):
 | 
						|
        super(DisplayHTMLBlockTableWithGraph, self).__init__(iwla=iwla, title=title, cols=cols,
 | 
						|
                                                             human_readable_cols=human_readable_cols)
 | 
						|
        self.short_titles = short_titles or []
 | 
						|
        self.short_titles = listToStr(self.short_titles)
 | 
						|
        self.nb_valid_rows = nb_valid_rows
 | 
						|
        self.icon_path = self.iwla.getConfValue('icon_path', '/')
 | 
						|
        self.maxes = [0] * len(cols)
 | 
						|
        self.table_graph_css = u'iwla_graph_table'
 | 
						|
        self.td_img_css = u'iwla_td_img'
 | 
						|
        self.graph_cols = graph_cols or []
 | 
						|
 | 
						|
    def appendShortTitle(self, short_title):
 | 
						|
        self.short_titles.append(short_title)
 | 
						|
 | 
						|
    def setShortTitle(self, short_titles):
 | 
						|
        self.short_titles = listToStr(short_titles)
 | 
						|
 | 
						|
    def setNbValidRows(self, nb_valid_rows):
 | 
						|
        self.nb_valid_rows = nb_valid_rows
 | 
						|
 | 
						|
    def _computeMax(self):
 | 
						|
        for i in range(0, self.nb_valid_rows):
 | 
						|
            row = self.rows[i]
 | 
						|
            for j in range(1, len(row)):
 | 
						|
                if type(row[j]) != int:
 | 
						|
                    continue
 | 
						|
                if row[j] > self.maxes[j]:
 | 
						|
                    self.maxes[j] = row[j]
 | 
						|
 | 
						|
    def _getIconFromStyle(self, style):
 | 
						|
        if   style.startswith(u'iwla_page'):      icon = u'vp.png'
 | 
						|
        elif style.startswith(u'iwla_hit'):       icon = u'vh.png'
 | 
						|
        elif style.startswith(u'iwla_bandwidth'): icon = u'vk.png'
 | 
						|
        elif style.startswith(u'iwla_visitor'):   icon = u'vu.png'
 | 
						|
        elif style.startswith(u'iwla_visit'):     icon = u'vv.png'
 | 
						|
        else: return ''
 | 
						|
 | 
						|
        return u'/%s/other/%s' % (self.icon_path, icon)
 | 
						|
 | 
						|
    def _buildHTML(self):
 | 
						|
        self._computeMax()
 | 
						|
 | 
						|
        style = u''
 | 
						|
        if self.table_graph_css: style = u' class="%s"' % (self.table_graph_css)
 | 
						|
        html = u'<table%s>' % (style)
 | 
						|
        html += u'<tr>'
 | 
						|
        for i in range(0, self.nb_valid_rows):
 | 
						|
            row = self.rows[i]
 | 
						|
            css = u''
 | 
						|
            if self.td_img_css: css=u' class="%s"' % (self.td_img_css)
 | 
						|
            html += u'<td%s>' % (css)
 | 
						|
            for j in self.graph_cols:
 | 
						|
                style = self.getColCSSClass(j)
 | 
						|
                icon = self._getIconFromStyle(style)
 | 
						|
                if not icon: continue
 | 
						|
                if style: style = u' class="%s"' % (style)
 | 
						|
                alt = u'%s: %s' % (row[j], self.cols[j])
 | 
						|
                if self.maxes[j]:
 | 
						|
                    height = int((self.rows[i][j] * 100) / self.maxes[j]) or 1
 | 
						|
                else:
 | 
						|
                    height = 1
 | 
						|
                html += u'<img%s src="%s" height="%d" width="6" alt="%s" title="%s" />' % (style, icon, height, alt, alt)
 | 
						|
            html += u'</td>'
 | 
						|
        html += u'</tr>'
 | 
						|
        html += u'<tr>'
 | 
						|
        for i in range(0, len(self.short_titles)):
 | 
						|
            style = self.getCellCSSClass(i, 0)
 | 
						|
            if style: style = u' class="%s"' % (style)
 | 
						|
            html += u'<td%s>%s</td>' % (style, self.short_titles[i])
 | 
						|
        html += u'</tr>'
 | 
						|
        html += u'</table>'
 | 
						|
 | 
						|
        self.html += html
 | 
						|
 | 
						|
        super(DisplayHTMLBlockTableWithGraph, self)._buildHTML()
 | 
						|
 | 
						|
class DisplayHTMLPage(object):
 | 
						|
 | 
						|
    def __init__(self, iwla, title, filename, css_path):
 | 
						|
        self.iwla = iwla
 | 
						|
        self.title = title
 | 
						|
        self.filename = filename
 | 
						|
        self.blocks = []
 | 
						|
        self.css_path = listToStr(css_path)
 | 
						|
        self.logger = logging.getLogger(self.__class__.__name__)
 | 
						|
 | 
						|
    def getFilename(self):
 | 
						|
        return self.filename;
 | 
						|
 | 
						|
    def getBlock(self, title):
 | 
						|
        for b in self.blocks:
 | 
						|
            if title == b.getTitle():
 | 
						|
                return b
 | 
						|
        return None
 | 
						|
 | 
						|
    def getAllBlocks(self):
 | 
						|
        return self.blocks
 | 
						|
 | 
						|
    def appendBlock(self, block):
 | 
						|
        self.blocks.append(block)
 | 
						|
 | 
						|
    def build(self, root, displayVersion=True, filters=None):
 | 
						|
        filename = os.path.join(root, self.filename)
 | 
						|
 | 
						|
        base = os.path.dirname(filename)
 | 
						|
        if not os.path.exists(base):
 | 
						|
            os.makedirs(base)
 | 
						|
 | 
						|
        self.logger.debug('Write %s' % (filename))
 | 
						|
 | 
						|
        f = codecs.open(filename, 'w', 'utf-8')
 | 
						|
        f.write(u'<!DOCTYPE html>\n')
 | 
						|
        f.write(u'<html lang="{}">\n'.format(self.iwla.getConfValue('locale', 'en')))
 | 
						|
        f.write(u'<head>\n')
 | 
						|
        f.write(u'<meta http-equiv="Content-type" content="text/html; charset=UTF-8"/>\n')
 | 
						|
        f.write(u'<link rel="icon" type="image/png" href="/resources/icon/favicon.png"/>\n')
 | 
						|
        for css in self.css_path:
 | 
						|
            f.write(u'<link rel="stylesheet" href="/%s"/>\n' % (css))
 | 
						|
        if self.title:
 | 
						|
            f.write(u'<title>%s</title>\n' % (self.title))
 | 
						|
        f.write(u'</head><body>\n')
 | 
						|
        for block in self.blocks:
 | 
						|
            block.build(f, filters=filters)
 | 
						|
        if displayVersion:
 | 
						|
            f.write(u'<div style="text-align:center;width:100%%">Generated by <a href="%s">IWLA %s</a></div>\n' %
 | 
						|
                    ("http://indefero.soutade.fr/p/iwla", self.iwla.getVersion()))
 | 
						|
        f.write(u'</body></html>')
 | 
						|
        f.close()
 | 
						|
 | 
						|
class DisplayHTMLBuild(object):
 | 
						|
 | 
						|
    def __init__(self, iwla):
 | 
						|
        self.iwla = iwla
 | 
						|
        self.filters = []
 | 
						|
        self.clear()
 | 
						|
 | 
						|
    def clear(self):
 | 
						|
        self.pages = []
 | 
						|
 | 
						|
    def createPage(self, *args):
 | 
						|
        return DisplayHTMLPage(self.iwla, *args)
 | 
						|
    
 | 
						|
    def createBlock(self, _class, *args):
 | 
						|
        return _class(self.iwla, *args)
 | 
						|
 | 
						|
    def getPage(self, filename):
 | 
						|
        for page in self.pages:
 | 
						|
            if page.getFilename() == filename:
 | 
						|
                return page
 | 
						|
        return None
 | 
						|
 | 
						|
    def getAllPages(self):
 | 
						|
        return self.pages
 | 
						|
 | 
						|
    def addPage(self, page):
 | 
						|
        self.pages.append(page)
 | 
						|
 | 
						|
    def build(self, root):
 | 
						|
        display_root = self.iwla.getConfValue('DISPLAY_ROOT', '')
 | 
						|
        if not os.path.exists(display_root):
 | 
						|
            os.makedirs(display_root)
 | 
						|
        for res_path in self.iwla.getResourcesPath():
 | 
						|
            target = os.path.abspath(res_path)
 | 
						|
            link_name = os.path.join(display_root, res_path)
 | 
						|
            if not os.path.exists(link_name):
 | 
						|
                os.symlink(target, link_name)
 | 
						|
 | 
						|
        for page in self.pages:
 | 
						|
            page.build(root, filters=self.filters)
 | 
						|
 | 
						|
    def addColumnFilter(self, column, function, args):
 | 
						|
        self.filters.append(({'column':column, 'args':args}, function))
 | 
						|
 | 
						|
    def getDisplayName(self, visitor):
 | 
						|
        display_visitor_ip = True
 | 
						|
        compact_host_name = True
 | 
						|
        address = visitor['remote_addr']
 | 
						|
        if display_visitor_ip and\
 | 
						|
           super_hit.get('dns_name_replaced', False):
 | 
						|
            host_name = address
 | 
						|
            if compact_host_name:
 | 
						|
                ip = visitor['remote_ip'].replace('.', '-')
 | 
						|
                host_name = host_name.replace(ip, 'IP')
 | 
						|
                ip = ip.replace('-', '')
 | 
						|
                host_name = host_name.replace(ip, 'IP')
 | 
						|
            address = '%s [%s]' % (host_name, visitor['remote_ip'])
 | 
						|
        return address
 | 
						|
        
 | 
						|
 | 
						|
#
 | 
						|
# Global functions
 | 
						|
#
 | 
						|
 | 
						|
def bytesToStr(_bytes):
 | 
						|
    suffixes = [u'', u' kB', u' MB', u' GB', u' TB']
 | 
						|
 | 
						|
    for i in range(0, len(suffixes)):
 | 
						|
        if _bytes < 1024: break
 | 
						|
        _bytes /= 1024.0
 | 
						|
 | 
						|
    if i:
 | 
						|
        return '%.02f%s' % (_bytes, suffixes[i])
 | 
						|
    else:
 | 
						|
        return '%d%s' % (_bytes, suffixes[i])
 | 
						|
 | 
						|
def _toStr(v):
 | 
						|
    return v
 | 
						|
    if type(v) != unicode: return unicode(v)
 | 
						|
    else: return v
 | 
						|
 | 
						|
def listToStr(l): return l #map(lambda v : _toStr(v), l)
 | 
						|
 | 
						|
def generateHTMLLink(url, name=None, max_length=100, prefix=u'http'):
 | 
						|
    url = url
 | 
						|
    if not name: name = url
 | 
						|
    if not url.startswith(prefix): url = u'%s://%s' % (prefix, url)
 | 
						|
    return u'<a href="%s">%s</a>' % (url, name[:max_length])
 | 
						|
 | 
						|
def createCurTitle(iwla, title):
 | 
						|
    title = iwla._(title)
 | 
						|
    month_name = time.strftime(u'%B', iwla.getCurTime())
 | 
						|
    year = time.strftime(u'%Y', iwla.getCurTime())
 | 
						|
    title += u' - %s %s' % (iwla._(month_name), year)
 | 
						|
    domain_name = iwla.getConfValue('domain_name', '')
 | 
						|
    if domain_name:
 | 
						|
        title += u' - %s' % (domain_name)
 | 
						|
    return title
 |