from string import Template
import logparse
-from . import interface, util, config
+from . import interface, util, config, mail
import logging
logger = logging.getLogger(__name__)
+
locale.setlocale(locale.LC_ALL, '') # inherit system locale
#DEG = "°".encode('unicode_escape')
DEG = u'\N{DEGREE SIGN}'
CORNERCHARS_SINGLE = ['└', '┘', '┐', '┌']
LINECHARS_DOUBLE = ['║', '═']
LINECHARS_SINGLE = ['│', '─']
+BULLET = "• "
INDENT = " "
+
class Output:
"""
Base class for a data processor.
self.destination = ""
def append(self, content):
+ """
+ Add a string
+ """
self.content += content
def write(self, destination=""):
+ """
+ Output contents into a file
+ """
if destination == "":
destination = self.destination
if destination == "":
self.linewidth = linewidth;
def append_header(self, template=''):
+ """
+ Print details with some primitive formatting
+ """
init_varfilter()
box = PlaintextBox(content=Template("$title $version on $hostname\n\n$time $date").safe_substitute(varsubst), vpadding=2, hpadding="\t\t", linewidth=config.prefs['linewidth'])
line = PlaintextLine(self.linewidth)
self.append(box.draw() + line.draw())
def append_footer(self):
+ """
+ Append a horizontal line and some details
+ """
init_varfilter()
self.append(PlaintextLine(self.linewidth, vpadding=1).draw())
self.append(Template("$hostname $time $date").safe_substitute(varsubst))
def append_section(self, section):
+ """
+ Call the appropriate methods to format a section (provided by a parser).
+ This should be run by interface.py after every instance of parse_log().
+ """
self.append(PlaintextBox(content=section.title, double=False, fullwidth=False, vpadding=0, hpadding=" ").draw())
self.append('\n'*2)
for data in section.data:
self.append('\n')
def _fmt_data(self, subtitle, data = None): # write title and data
+ """
+ Format the properties of a data object into usable plaintext form with a few fancy symbols/formatting tricks.
+ Subtitle is required, data is not. If only subtitle is supplied or subtitle + one data item, a single line will be printed.
+ """
if (subtitle == ""):
logger.warning("No subtitle provided.. skipping section")
return
else:
itemoutput = subtitle + '\n'
for datum in data:
- datum = '• ' + datum
+ datum = BULLET + datum
if len(datum) > config.prefs['linewidth'] - 3:
words = datum.split()
if max(map(len, words)) > config.prefs['linewidth'] - len(INDENT):
self.css = ""
def embed_css(self, css):
+ """
+ Convert stylesheet to inline tags
+ """
self.content = mail.mailprep(self.content, css)
+ return self.content
- def append_header(self, template): # insert variables into header template file
+ def append_header(self, template):
+ """
+ Insert variables into header template file and append HTML tags
+ """
init_varfilter()
headercontent = Template(open(template, 'r').read())
self.append(headercontent.safe_substitute(varsubst))
self.append(self.opentag('div', id='main'))
def append_footer(self):
- self.append(closetag('div') + closetag('body') + closetag('html'))
+ """
+ Close HTML tags that were opened in the template.
+ TODO: add footer template similar to header template.
+ """
+ self.append(self.closetag('div') + self.closetag('body') + self.closetag('html'))
def append_section(self, section):
+ """
+ Call the appropriate methods to generate HTML tags for a section (provided by a parser).
+ This should be run by interface.py after every instance of parse_log().
+ """
self.append(self.opentag('div', 1, section.title, 'section'))
self.append(self._gen_title(section.title))
for data in section.data:
self.append(self._fmt_data(data.subtitle, data.items))
- self.append(closetag('div', 1))
+ self.append(self.closetag('div', 1))
- def _gen_title(self, title): # write title for a section
+ def _gen_title(self, title):
+ """
+ Format the title for a section
+ """
if (title == '' or '\n' in title):
logger.error("Invalid title")
raise ValueError
logger.debug("Writing title for " + title)
- return tag('h2', 0, title)
+ return self.tag('h2', False, title)
- def _fmt_data(self, subtitle, data = None): # write title and data
+ def _fmt_data(self, subtitle, data = None):
+ """
+ Format the properties of a data object into usable HTML tags.
+ Subtitle is required, data is not. If only subtitle is supplied or subtitle + one data item, a single line will be printed.
+ """
if (subtitle == ""):
logger.warning("No subtitle provided.. skipping section")
return
if (data == None or len(data) == 0):
logger.debug("No data provided.. just printing subtitle")
- return tag('p', 0, subtitle)
+ return self.tag('p', False, subtitle)
else:
logger.debug("Received data " + str(data))
subtitle += ':'
if (len(data) == 1):
- return tag('p', 0, subtitle + ' ' + data[0])
+ return self.tag('p', False, subtitle + ' ' + data[0])
else:
output = ""
- output += tag('p', 0, subtitle)
+ output += self.tag('p', False, subtitle)
output += self.opentag('ul', 1)
coderegex = re.compile('`(.*)`')
for datum in data:
if datum == "" or datum == None:
continue
- datum = coderegex.sub(r'<code>{\1}</code>', str(datum))
- output += tag('li', 0, datum)
- output += closetag('ul', 1)
+ datum = coderegex.sub(r"<code>\1</code>", str(datum))
+ output += self.tag('li', False, datum)
+ output += self.closetag('ul', True)
return output
- def opentag(self, tag, block = 0, id = None, cl = None): # write html opening tag
+ def opentag(self, tag, block=False, id=None, cl=None):
+ """
+ Write HTML opening tag
+ """
output = ""
if (block):
output += '\n'
output += '\n'
return output
- def closetag(self, tag, block = 0): # write html closing tag
- if (block == 0):
- return "</" + tag + ">"
- else:
+ def closetag(self, tag, block=False):
+ """
+ Write HTML closing tag
+ """
+ if block:
return "\n</" + tag + ">\n"
+ else:
+ return "</" + tag + ">"
- def tag(self, tag, block = 0, content = ""): # write html opening tag, content, and html closing tag
+ def tag(self, tag, block=False, content=""):
+ """
+ Write HTML opening tag, content, and closing tag
+ """
o = self.opentag(tag, block)
c = self.closetag(tag, block)
return o + content + c
-
class Section:
"""
Each parser should output a Section() which contains the title and returned data.
def append_data(self, data):
self.data.append(data)
+
class Data:
"""
Each section (parser) can have one or more Data() objects which are essentially glorified lists.
self.items = items
def truncl(self, limit): # truncate list
+ """
+ Truncate self.items to a specified value and state how many items are hidden.
+ """
if (len(self.items) > limit):
more = str(len(self.items) - limit)
self.items = self.items[:limit]
- self.items.append("+ " + more + " more")
-
- def orderbyfreq(self): # order a list by the frequency of its elements and remove duplicates
-# temp = list(self.items)[:]
-# logger.debug(self.items)
-# self.items = list(set(self.items))
-# self.items = [[i, temp.count(i)] for i in self.items] # add count of each element
-# self.items.sort(key=lambda x:temp.count(x[0])) # sort by count
-# self.items = [i[0] + ' (' + str(i[1]) + ')' for i in self.items] # put element and count into string
-# self.items = self.items[::-1] # reverse
+ self.items.append("+ {0} more".format(more))
+
+ def orderbyfreq(self):
+ """
+ Order a list by frequency of each item, then remove duplicates and append frequency in parentheses.
+ """
unsorted = list(self.items)
- self.items = [ "{0} ({1})".format(y, unsorted.count(y)) for y in sorted(set(unsorted), key = lambda x: -unsorted.count(x)) ]
+ self.items = ["{0} ({1})".format(y, unsorted.count(y)) for y in sorted(set(unsorted), key = lambda x: -unsorted.count(x))]
class PlaintextLine:
"""
- Draw a horizontal line for plain text format, with optional padding/styling
+ Draw a horizontal line for plain text format, with optional padding/styling.
"""
def __init__(self, linewidth=80, double=True, vpadding=1, hpadding=""):
line = (LINECHARS_DOUBLE[1] if self.double else LINECHARS_SINGLE[1])
return "\n" * self.vpadding + self.hpadding + line * (self.linewidth - 2 * len(self.hpadding)) + self.hpadding + "\n" * self.vpadding
+
class PlaintextBox:
"""
Draw a rectangular box around text, with customisable padding/size/style
contentlines.append(cornerchars[0] + linechars[1] * (contentwidth + len(self.hpadding)*2) + cornerchars[1])
return ('\n').join(contentlines)
+
def init_varfilter():
global varfilter
global varpattern
varpattern = re.compile("|".join(varfilter.keys()))
varsubst = dict(title=config.prefs['title'], date=interface.start.strftime(DATEFMT), time=interface.start.strftime(TIMEFMT), hostname=util.hostname(config.prefs['hostname-path']), version=logparse.__version__, css=os.path.relpath(config.prefs['css'], os.path.dirname(config.prefs['output'])))
-def writetitle(title): # write title for a section
- if (title == '' or '\n' in title):
- logger.error("Invalid title")
- raise ValueError
- logger.debug("Writing title for " + title)
- return tag('h2', 0, title)
-
-def addtag(l, tag): # add prefix and suffix tags to each item in a list
- l2 = ['<' + tag + '>' + i + '</' + tag + '>' for i in l]
- return l2
-
def backticks(l):
return ["`" + x + "`" for x in l]
-def plural(noun, quantity): # return "1 noun" or "n nouns"
+
+def plural(noun, quantity):
+ """
+ Return "1 noun" or "n nouns"
+ """
if (quantity == 1):
return(str(quantity) + " " + noun)
else:
return(str(quantity) + " " + noun + "s")
-def parsesize(num, suffix='B'): # return human-readable size from number of bytes
+
+def parsesize(num, suffix='B'):
+ """
+ Return human-readable size from number of bytes
+ """
for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']:
if abs(num) < 1024.0:
return "%3.1f %s%s" % (num, unit, suffix)
num /= 1024.0
return "%.1f%s%s" % (num, 'Yi', suffix)
-def fsubject(template): # Replace variables in the title template provided in config
+
+def fsubject(template):
+ """
+ Replace variables in the title template provided in config
+ """
r = varpattern.sub(lambda m: varfilter[re.escape(m.group(0))], template)
logger.debug("Returning subject line " + r)
return r
from telnetlib import Telnet
from typing import List, Dict, NamedTuple
+from logparse import formatting
from ..formatting import *
from ..util import readlog, resolve
from ..config import *
sensors.init()
- coretemps = []
- pkgtemp = 0
- systemp = 0
-
- systemp_data = Data("Sys")
- coretemp_data = Data("Cores")
- pkgtemp_data = Data("Processor")
+ systemp = Data("Sys", [])
+ coretemp = Data("Cores", [])
+ pkgtemp = Data("Processor", [])
try:
-
for chip in sensors.iter_detected_chips():
for feature in chip:
if "Core" in feature.label:
- coretemp_data.items.append([feature.label, feature.get_value()])
- logger.debug("Found core " + feature.label + " at temp " + str(feature.get_value()))
+ coretemp.items.append([feature.label, float(feature.get_value())])
+ continue
if "CPUTIN" in feature.label:
- pkgtem_data.items.append([feature.label, str(feature.get_value())])
- logger.debug("Found CPU package at temp" + str(feature.get_value()))
+ pkgtemp.items.append([feature.label, float(feature.get_value())])
+ continue
if "SYS" in feature.label:
- systemp_data.items.append([feature.label, str(feature.get_value())])
- logger.debug("Found sys input " + feature.label + " at temp " + str(feature.get_value()))
-
- for temp_data in [systemp_data, coretemp_data, pkgtemp_data]:
+ systemp.items.append([feature.label, float(feature.get_value())])
+ continue
+
+ logger.debug("Core data is {0}".format(str(coretemp.items)))
+ logger.debug("Sys data is {0}".format(str(systemp.items)))
+ logger.debug("Pkg data is {0}".format(str(pkgtemp.items)))
+ for temp_data in [systemp, coretemp, pkgtemp]:
+ logger.debug("Looking at temp data {0}".format(str(temp_data.items)))
if len(temp_data.items) > 1:
- avg = sum(feature[1] for feature in temp_data.items) / len(temp_data.items)
+ avg = float(sum(feature[1] for feature in temp_data.items)) / len(temp_data.items)
logger.debug("Avg temp for {0} is {1} {2}{3}".format(temp_data.subtitle, str(avg), DEG, CEL))
- temp_data.subtitle += " (avg {0}{1}{2}):".format(str(avg), DEG, CEL)
- temp_data.items = ["{0}: {1}{2}{3}".format(feature[0], feature[1], DEG, CEL) for feature in temp_data]
+ temp_data.subtitle += " (avg {0}{1}{2})".format(str(avg), DEG, CEL)
+ temp_data.items = ["{0}: {1}{2}{3}".format(feature[0], str(feature[1]), DEG, CEL) for feature in temp_data.items]
else:
- temp_data.items = temp_data[0][1] + DEG + CEL
+ temp_data.items = [str(temp_data.items[0][1]) + DEG + CEL]
section.append_data(temp_data)
finally: