# Richard Darst, 2009
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(">", ">")
def rst(text):
return text
class _BaseWriter(object):
pass
class TextLog(object):
def format(self, M):
"""Write raw text logs."""
return "\n".join(M.lines)
class HTMLlog(object):
def format(self, M):
"""Write pretty HTML logs."""
# 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)
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)
return out
class HTML(object):
def format(self, M):
"""Write the minutes summary."""
data = [ ]
if M._meetingTopic:
pageTitle = "%s: %s"%(M.channel, M._meetingTopic)
else:
pageTitle = "%s Meeting"%M.channel
# Header and things stored
data.append(
'''
%s
%s
Meeting started by %s at %s %s. (full logs)
\n\n'''%(pageTitle, pageTitle, M.owner,
time.strftime("%H:%M:%S", M.starttime),
M.config.timeZone,
M.config.basename+'.log.html',
))
# Add all minute items to the table
for m in M.minutes:
data.append(m.html(M))
# End the log portion
data.append("""
Meeting ended at %s %s. (full logs)"""%\
(time.strftime("%H:%M:%S", M.endtime), M.config.timeZone,
M.config.basename+'.log.html'))
data.append("\n
")
# Action Items
data.append("Action Items")
import meeting
for m in M.minutes:
# The hack below is needed because of pickling problems
if m.itemtype != "ACTION": continue
data.append(" - %s
"%m.line) #already escaped
data.append("
\n\n
")
# Action Items, by person (This could be made lots more efficient)
data.append("Action Items, by person\n")
for nick in sorted(M.attendees.keys(), key=lambda x: x.lower()):
headerPrinted = False
for m in M.minutes:
# The hack below is needed because of pickling problems
if m.itemtype != "ACTION": continue
if m.line.find(nick) == -1: continue
if not headerPrinted:
data.append(" - %s
"%nick)
headerPrinted = True
data.append(" - %s
"%m.line) # already escaped
m.assigned = True
if headerPrinted:
data.append("
")
# unassigned items:
data.append(" - UNASSIGNED
")
numberUnassigned = 0
for m in M.minutes:
if m.itemtype != "ACTION": continue
if getattr(m, 'assigned', False): continue
data.append(" - %s
"%m.line) # already escaped
numberUnassigned += 1
if numberUnassigned == 0: data.append(" - (none)
")
data.append('
\n ')
# clean-up
data.append("
\n\n
")
# People Attending
data.append("""People Present (lines said):""")
# sort by number of lines spoken
nicks = [ (n,c) for (n,c) in M.attendees.iteritems() ]
nicks.sort(key=lambda x: x[1], reverse=True)
for nick in nicks:
data.append(' - %s (%s)
'%(nick[0], nick[1]))
data.append("
\n\n
")
data.append("""Generated by MeetBot."""%
M.config.MeetBotInfoURL)
data.append("")
return "\n".join(data)
class RST(object):
def __init__(self):
pass
def format(self, M):
"""Return a ReStructured Text minutes summary."""
dedent = textwrap.dedent
wrap = textwrap.wrap
fill = textwrap.fill
def wrapList(item, indent=0):
return fill(item, 72,
initial_indent=' '*indent,
subsequent_indent= ' '*(indent+2))
def replaceWRAP(item):
re_wrap = re.compile('WRAP(.*)WRAP', re.DOTALL)
def repl(m):
return fill(m.group(1), 72,
break_long_words=False)
return re_wrap.sub(repl, item)
if M._meetingTopic:
pageTitle = "%s: %s"%(M.channel, M._meetingTopic)
else:
pageTitle = "%s Meeting"%M.channel
MeetingItems = [ ]
M.rst_urls = [ ]
M.rst_refs = { }
haveTopic = None
for m in M.minutes:
item = "* "+m.rst(M)
if m.itemtype == "TOPIC":
item = wrapList(item, 0)
haveTopic = True
else:
if haveTopic: item = wrapList(item, 2)
else: item = wrapList(item, 1)
MeetingItems.append(item)
MeetingItems = '\n\n'.join(MeetingItems)
MeetingURLs = "\n".join(M.rst_urls)
del M.rst_urls, M.rst_refs
MeetingItems = MeetingItems + '\n\n'+MeetingURLs
# Action Items
ActionItems = [ ]
for m in M.minutes:
# The hack below is needed because of pickling problems
if m.itemtype != "ACTION": continue
#already escaped
ActionItems.append(wrapList("* %s"%m.line, indent=0))
ActionItems = "\n\n".join(ActionItems)
# Action Items, by person (This could be made lots more efficient)
ActionItemsPerson = [ ]
for nick in sorted(M.attendees.keys(), key=lambda x: x.lower()):
headerPrinted = False
for m in M.minutes:
# The hack below is needed because of pickling problems
if m.itemtype != "ACTION": continue
if m.line.find(nick) == -1: continue
if not headerPrinted:
ActionItemsPerson.append("* %s"%nick)
headerPrinted = True
# already escaped
ActionItemsPerson.append(wrapList("* %s"%m.line, 2))
m.assigned = True
#if headerPrinted:
# ActionItemsPerson.append(" ")
# unassigned items:
ActionItemsPerson.append("* **UNASSIGNED**")
numberUnassigned = 0
for m in M.minutes:
if m.itemtype != "ACTION": continue
if getattr(m, 'assigned', False): continue
# already escaped
ActionItemsPerson.append(wrapList("* %s"%m.line, 2))
numberUnassigned += 1
if numberUnassigned == 0: ActionItemsPerson.append(" * (none)")
#ActionItemsPerson.append(' \n')
# clean-up
#ActionItemsPerson.append("\n\n
")
ActionItemsPerson = "\n\n".join(ActionItemsPerson)
# People Attending
PeoplePresent = [ ]
# sort by number of lines spoken
nicks = [ (n,c) for (n,c) in M.attendees.iteritems() ]
nicks.sort(key=lambda x: x[1], reverse=True)
for nick in nicks:
PeoplePresent.append('* %s (%s)'%(nick[0], nick[1]))
PeoplePresent = "\n\n".join(PeoplePresent)
# End the log portion
body = """\
%(titleBlock)s
%(pageTitle)s
%(titleBlock)s
WRAPMeeting started by %(owner)s at %(starttime)s %(timeZone)s.
The `full logs`_ are available.WRAP
.. _`full logs`: %(fullLogs)s
Meeting log
-----------
%(MeetingItems)s
Meeting ended at %(endtime)s %(timeZone)s.
Action Items
------------
%(ActionItems)s
Action Items, by person
-----------------------
%(ActionItemsPerson)s
People Present (lines said)
---------------------------
%(PeoplePresent)s
Generated by `MeetBot`_
.. _`MeetBot`: %(MeetBotInfoURL)s
"""
body = replaceWRAP(dedent(body))
body = body%{'titleBlock':('='*len(pageTitle)),
'pageTitle':pageTitle,
'owner':M.owner,
'starttime':time.strftime("%H:%M:%S", M.starttime),
'timeZone':M.config.timeZone,
'fullLogs':M.config.basename+'.log.html',
'endtime':time.strftime("%H:%M:%S", M.endtime),
'timeZone':M.config.timeZone,
'ActionItems': ActionItems,
'ActionItemsPerson': ActionItemsPerson,
'MeetBotInfoURL':M.config.MeetBotInfoURL,
'PeoplePresent':PeoplePresent,
'MeetingItems':MeetingItems,
}
#print body
#from fitz import interactnow
#osys.exit()
return body
class HTMLfromRST(object):
def format(self, M):
import docutils.core
rst = RST().format(M)
rstToHTML = docutils.core.publish_string(rst, writer_name='html',
settings_overrides={'file_insertion_enabled': 0,
'raw_enabled': 0})
return rstToHTML