# Richard Darst, June 2009
###
# Copyright (c) 2009, Richard Darst
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
import os
import re
import textwrap
import time
#from meeting import timeZone, meetBotInfoURL
# Needed for testing with isinstance() for properly writing.
#from items import Topic, Action
import items
# Data sanitizing for various output methods
def html(text):
"""Escape bad sequences (in HTML) in user-generated lines."""
return text.replace("&", "&").replace("<", "<").replace(">", ">")
rstReplaceRE = re.compile('_( |-|$)')
def rst(text):
"""Escapes bad sequences in reST"""
return rstReplaceRE.sub(r'\_\1', text)
def text(text):
"""Escapes bad sequences in text (not implemented yet)"""
return text
# wraping functions (for RST)
class TextWrapper(textwrap.TextWrapper):
wordsep_re = re.compile(r'(\s+)')
def wrapList(item, indent=0):
return TextWrapper(width=72, initial_indent=' '*indent,
subsequent_indent= ' '*(indent+2),
break_long_words=False).fill(item)
def replaceWRAP(item):
re_wrap = re.compile(r'sWRAPs(.*)eWRAPe', re.DOTALL)
def repl(m):
return TextWrapper(width=72, break_long_words=False).fill(m.group(1))
return re_wrap.sub(repl, item)
def MeetBotVersion():
import meeting
if hasattr(meeting, '__version__'):
return ' '+meeting.__version__
else:
return ''
class _BaseWriter(object):
def __init__(self, M, **kwargs):
self.M = M
def format(self, extension=None):
"""Override this method to implement the formatting.
For file output writers, the method should return a unicode
object containing the contents of the file to write.
The argument 'extension' is the key from `writer_map`. For
file writers, this can (and should) be ignored. For non-file
outputs, this can be used to This can be used to pass data,
"""
raise NotImplementedError
@property
def pagetitle(self):
if self.M._meetingTopic:
return "%s: %s"%(self.M.channel, self.M._meetingTopic)
return "%s Meeting"%self.M.channel
def replacements(self):
return {'pageTitle':self.pagetitle,
'owner':self.M.owner,
'starttime':time.strftime("%H:%M:%S", self.M.starttime),
'endtime':time.strftime("%H:%M:%S", self.M.endtime),
'timeZone':self.M.config.timeZone,
'fullLogs':self.M.config.basename+'.log.html',
'fullLogsFullURL':self.M.config.filename(url=True)+'.log.html',
'MeetBotInfoURL':self.M.config.MeetBotInfoURL,
'MeetBotVersion':MeetBotVersion(),
}
def iterNickCounts(self):
nicks = [ (n,c) for (n,c) in self.M.attendees.iteritems() ]
nicks.sort(key=lambda x: x[1], reverse=True)
return nicks
def iterActionItemsNick(self):
for nick in sorted(self.M.attendees.keys(), key=lambda x: x.lower()):
def nickitems():
for m in self.M.minutes:
# The hack below is needed because of pickling problems
if m.itemtype != "ACTION": continue
if m.line.find(nick) == -1: continue
m.assigned = True
yield m
yield nick, nickitems()
def iterActionItemsUnassigned(self):
for m in self.M.minutes:
if m.itemtype != "ACTION": continue
if getattr(m, 'assigned', False): continue
yield m
class _CSSmanager(object):
_css_head = textwrap.dedent('''\
''')
def getCSS(self, name):
cssfile = getattr(self.M.config, 'cssFile_'+name, '')
if cssfile.lower() == 'none':
# special string 'None' means no style at all
return ''
elif cssfile in ('', 'default'):
# default CSS file
css_fname = os.path.join(os.path.dirname(__file__),
'css-'+name+'-default.css')
else:
css_fname = cssfile
try:
# Stylesheet specified
if getattr(self.M.config, 'cssEmbed_'+name, True):
# external stylesheet
css = file(css_fname).read()
return self._css_head%css
else:
# linked stylesheet
css_head = (''''''%cssfile)
return css_head
except Exception, exc:
if not self.M.config.safeMode:
raise
import traceback
traceback.print_exc()
print "(exception above ignored, continuing)"
try:
css_fname = os.path.join(os.path.dirname(__file__),
'css-'+name+'-default.css')
css = open(css_fname).read()
return self._css_head%css
except:
if not self.M.config.safeMode:
raise
import traceback
traceback.print_exc()
return ''
class TextLog(_BaseWriter):
def format(self, extension=None):
M = self.M
"""Write raw text logs."""
return "\n".join(M.lines)
update_realtime = True
class HTMLlog(_BaseWriter):
def format(self, extension=None):
"""Write pretty HTML logs."""
M = self.M
# pygments lexing setup:
# (pygments HTML-formatter handles HTML-escaping)
import pygments
from pygments.lexers import IrcLogsLexer
from pygments.formatters import HtmlFormatter
import pygments.token as token
from pygments.lexer import bygroups
# Don't do any encoding in this function with pygments.
# That's only right before the i/o functions in the Config
# object.
formatter = HtmlFormatter(lineanchors='l',
full=True, style=M.config.pygmentizeStyle,
outencoding=self.M.config.output_codec)
Lexer = IrcLogsLexer
Lexer.tokens['msg'][1:1] = \
[ # match: #topic commands
(r"(\#topic[ \t\f\v]*)(.*\n)",
bygroups(token.Keyword, token.Generic.Heading), '#pop'),
# match: #command (others)
(r"(\#[^\s]+[ \t\f\v]*)(.*\n)",
bygroups(token.Keyword, token.Generic.Strong), '#pop'),
]
lexer = Lexer()
#from rkddp.interact import interact ; interact()
out = pygments.highlight("\n".join(M.lines), lexer, formatter)
# Hack it to add "pre { white-space: pre-wrap; }", which make
# it wrap the pygments html logs. I think that in a newer
# version of pygmetns, the "prestyles" HTMLFormatter option
# would do this, but I want to maintain compatibility with
# lenny. Thus, I do these substitution hacks to add the
# format in. Thanks to a comment on the blog of Francis
# Giannaros (http://francis.giannaros.org) for the suggestion
# and instructions for how.
out,n = re.subn(r"(\n\s*pre\s*\{[^}]+;\s*)(\})",
r"\1\n white-space: pre-wrap;\2",
out, count=1)
if n == 0:
out = re.sub(r"(\n\s*)",
r"\npre { white-space: pre-wrap; }\1",
out, count=1)
return out
class HTMLlog2(_BaseWriter, _CSSmanager):
def format(self, extension=None):
"""Write pretty HTML logs."""
M = self.M
lines = [ ]
line_re = re.compile(r"""\s*
(?P