All pastes #770927 Raw Edit

silan-0.0.2 Final

public python v1 · immutable
·published 2007-11-12 18:24 UTC
rendered paste body
#!/usr/bin/python -tt## SILAN# Simple IRC Log Analyzer# http://slackforge.com/silan/## Version 0.0.2# An extremely simple IRC log analyzer --;# Only supports dircproxy logs, because that's what I use## Copyright (c) 2007 Shidayu <shidayu@gmail.com>## Permission is hereby granted, free of charge, to any person obtaining a copy# of this software and associated documentation files (the "Software"), to deal# in the Software without restriction, including without limitation the rights# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell# copies of the Software, and to permit persons to whom the Software is# furnished to do so, subject to the following conditions:## The above copyright notice and this permission notice shall be included in# all copies or substantial portions of the Software.## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN# THE SOFTWARE.##### Import Other Modules#import os, re, stat, sys, glob, datetime, string, time, random, math, cgifrom ConfigParser import SafeConfigParser### Globals## Regular Expressions used by SILANre_rawlog = re.compile('^\[(\d\d) ([A-Z][a-z][a-z]) (\d\d):(\d\d)\] ([^\n\r\f\v]*)$')re_ownnick = re.compile('--- You changed your nickname to (\S*)')re_nickchange = re.compile('--- (\S*) \([^\t\n\r\f\v)]*\) changed nickname to (\S*)')re_quit = re.compile('<-- (\S*) \([^\t\n\r\f\v)]*\) quit from IRC: ([^\n\r\f\v]*)$')re_join = re.compile('--> (\S*) \([^\t\n\r\f\v)]*\) joined the channel$')re_part = re.compile('<-- (\S*) \([^\t\n\r\f\v)]*\) left the channel$')re_message = re.compile('<([^ \t\n\r\f\v!]*)![^\t\n\r\f\v>]*> ([^\n\r\f\v]*)$')re_describe = re.compile('\* (\S*) ([^\n\r\f\v]*)')re_topic = re.compile('--- (\S*) \([^\t\n\r\f\v)]*\) changed topic: ([^\t\n\r\f\v]*)$')re_kick = re.compile('<-- (\S*) kicked off by (\S*) \([^\t\n\r\f\v)]*\): ([^\n\r\f\v]*)$')re_ownkick = re.compile('<-- Kicked off by (\S*) \([^\t\n\r\f\v)]*\): ([^\n\r\f\v]*)$')re_nonword = re.compile('[^a-zA-Z0-9]')re_channame = re.compile('^(\S*).log$')re_tpltag = re.compile('{{silan:([^\t\n\r\f\v}]*)}}')	# Month conversion Dictionaryfmt_month = {	"Jan": 1, "Feb": 2, "Mar": 3, "Apr": 4,				"May": 5, "Jun": 6, "Jul": 7, "Aug": 8,				"Sep": 9, "Oct": 10, "Nov": 11, "Dec": 12	}						### Class Definitions#class Silan(object):	""" Simple IRC Log Analyzer """		def __init__(self):		self.config = SafeConfigParser()		self.config.read("/Users/shidayu/Applications/silan-0.0.2/silan.ini")				self.networks = []				for network in self.config.get("global", "networks").split():			print "Added network:", network			self.networks.append(Network(self.config, network))				for network in self.networks:			for channel in network.channels:				outfile = open(self.config.get(network.name + "." + channel.name, "output"), "w")				outfile.write(Report(channel).output_string())				outfile.close()class UserEvent(object):	""" Line of log data represented only with a a user and a time """	def __init__(self, dttm, user):		self.dttm = dttm		self.user = user		class Join(UserEvent):	def __init__(self, dttm, user):		UserEvent.__init__(self, dttm, user)		class Part(UserEvent):	def __init__(self, dttm, user):		UserEvent.__init__(self, dttm, user)		class Line(object):	""" Line of log data with date and time """		def __init__(self, dttm, text):		self.dttm = dttm		self.text = text			def word_count(self):		return len(self.text.split())class UserLine(Line):	""" Line of log data that includes user instance """		def __init__(self, dttm, user, text):		Line.__init__(self, dttm, text)		self.user = userclass Quit(Line):	def __init__(self, dttm, text):		Line.__init__(self, dttm, text)		class Message(UserLine):	""" Standard IRC message from a user """		def __init__(self, dttm, user, text):		UserLine.__init__(self, dttm, user, text)				class Describe(UserLine):	""" Describe or action from a user """		def __init__(self, dttm, user, text):		UserLine.__init__(self, dttm, user, text)				class Topic(UserLine):	""" Channel topic """							def __init__(self, dttm, user, text):		UserLine.__init__(self, dttm, user, text)class Kick(UserLine):	""" Channel kick """		def __init__(self, dttm, target, user, text):		UserLine.__init__(self, dttm, user, text)				self.target = targetclass Progress(object):	def __init__(self, min, max, ratchet):		self.min = min		self.max = max		self.ratchet = ratchet		self.progress = min				self.crank = -1			def tock(self, amount):		self.progress += amount				if int(math.floor(float(self.progress)/float(self.max) * 100)) > self.crank:				self.crank = int(math.floor(float(self.progress)/float(self.max) * 100))								if self.crank % self.ratchet == 0:					print str(self.crank) + "%",					sys.stdout.flush()class Log(object):	""" Log recorded by IRC client/proxy """		def __init__(self, filename):		self.filename = filename		self.lines = []				self._read_log()			def _read_log(self):				print "Reading lines from", self.filename + "...",		sys.stdout.flush()				loghandler = open(self.filename, 'r')		loglist = loghandler.readlines()		loghandler.close()				print "done"				print "Parsing timestamps and text from", self.filename + ":",		sys.stdout.flush()				creationdttm = datetime.datetime.fromtimestamp(os.stat(self.filename).st_ctime)				firstday = int(re_rawlog.match(loglist[0]).group(1))		firstmonth = fmt_month[re_rawlog.match(loglist[0]).group(2)]		firsthour = int(re_rawlog.match(loglist[0]).group(3))		firstminute = int(re_rawlog.match(loglist[0]).group(4))				prevdttm = datetime.datetime(creationdttm.year, firstmonth, firstday, firsthour, firstminute)				meter = Progress(0, len(loglist), 5)				for logtext in loglist:			day = int(re_rawlog.match(logtext).group(1))			month = fmt_month[re_rawlog.match(logtext).group(2)]			hour = int(re_rawlog.match(logtext).group(3))			minute = int(re_rawlog.match(logtext).group(4))						text = re_rawlog.match(logtext).group(5)						if month < prevdttm.month or (month == prevdttm.month and day < prevdttm.day):				# The preceding: Does it look like we've gone back in time?				prevdttm = datetime.datetime(prevdttm.year + 1, month, day, hour, minute)			else:				prevdttm = datetime.datetime(prevdttm.year, month, day, hour, minute)			self.lines.append(Line(prevdttm, text))						meter.tock(1)		class Network(Log):	""" IRC network """		def __init__(self, config, name="default"):		self.name = name		self.channels = []		self.users = []		self.ownuser = None		self.config = config		self.no_protips = []				Log.__init__(self, self.config.get(self.name + ".preferences", "file"))				print "Processing manual nick configuration for", self.name + "...",		sys.stdout.flush()				for nick in self.config.options(self.name + ".alternates"):			for alt in self.config.get(self.name + ".alternates", nick).split():				self.add_alternate(nick, alt)					for nick in self.config.options(self.name + ".separates"):			for sep in self.config.get(self.name + ".separates", nick).split():				self.add_separate(nick, sep)						for nick in self.config.options(self.name + ".propercase"):			self.get_user(nick).name = self.config.get(self.name + ".propercase", nick)		for nick in self.config.get(self.name + ".preferences", "no_protips").split():			self.no_protips.append(self.get_user(nick))				print "done"					self._parse()				for channel in self.config.get(self.name + ".preferences", "channels").split():			print "Added channel to network", self.name + ":", channel			self.channels.append(Channel(channel, self, self.config.get(self.name + "." + channel, "file")))			def get_channel(self, channame):		for netchannel in self.channels:			if channame.lower() == netchannel.name.lower():				return netchannel				return None			def get_user(self, nickname):		netuser = self.find_user(nickname)				if not netuser:			newuser = User(nickname)			self.users.append(newuser)			return newuser		else:			return netuser			def find_user(self, nickname):		for netuser in self.users:			if nick_belongs_to_user(nickname, netuser):				return netuser		def add_separate(self, nick1, nick2):		netuser1 = self.get_user(nick1)		netuser2 = self.get_user(nick2)				if netuser1 == netuser2:			if netuser1.name.lower() == nick1.lower():				netuser1.alternates.remove(nick2)				newuser = self.get_user(nick2)				newuser.separates.append(nick1)				netuser1.separates.append(nick2)			if netuser2.name.lower() == nick2.lower():				netuser2.alternates.remove(nick1)				newuser = self.get_user(nick1)				newuser.separates.append(nick2)				netuser2.separates.append(nick1)		else:			netuser1.separates.append(nick2)			netuser2.separates.append(nick1)				def add_alternate(self, nick1, nick2):		netuser1 = self.find_user(nick1)		netuser2 = self.find_user(nick2)				if not netuser1 and not netuser2:			newuser = self.get_user(nick1)			newuser.alternates.append(nick2)		elif netuser1 and not netuser2:			if nick2.lower() not in [sep.lower() for sep in netuser1.separates]:				netuser1.alternates.append(nick2)			else:				self.get_user(nick2)		elif netuser2 and not netuser1:			if nick1.lower() not in [sep.lower() for sep in netuser2.separates]:				netuser2.alternates.append(nick1)			else:				self.get_user(nick1)		elif netuser1 and netuser2 and netuser1 != netuser2 and not are_separate(netuser1, netuser2):			if self.users.index(netuser1) < self.users.index(netuser2):				netuser1.alternates.append(netuser2.name)				netuser1.alternates.extend(netuser2.alternates)				self.users.remove(netuser2)				if self.ownuser == netuser2:					self.ownuser = netuser1				for netchan in self.channels:					netchan.reassign_events(netuser2, netuser1)				del netuser2				else:				netuser2.alternates.append(netuser1.name)				netuser2.alternates.extend(netuser1.alternates)				self.users.remove(netuser1)				if self.ownuser == netuser1:					self.ownuser = netuser2				for netchan in self.channels:					netchan.reassign_events(netuser1, netuser2)				del netuser1			def _parse(self):		meter = Progress(0, len(self.lines), 5)				print "Parsing events from log file for network", self.name + ":",				for line in self.lines:			if re_nickchange.match(line.text):				oldnick = re_nickchange.match(line.text).group(1)				newnick = re_nickchange.match(line.text).group(2)				self.add_alternate(oldnick, newnick)						if re_ownnick.match(line.text):				newnick = re_ownnick.match(line.text).group(1)				if not self.ownuser:					self.ownuser = self.get_user(newnick)				else:					self.add_alternate(self.ownuser.name, newnick)								if re_quit.match(line.text):				quitnick = re_quit.match(line.text).group(1)				quittext = re_quit.match(line.text).group(2)				self.get_user(quitnick).quits.append(Quit(line.dttm, cgi.escape(quittext)))							meter.tock(1)			class Channel(Log):	""" IRC channel """		def __init__(self, name, network, filename):		Log.__init__(self, filename)			self.name = name		self.network = network		self.topics = []		self.messages = []		self.describes = []		self.kicks = []		self.joins = []		self.parts = []		self._parse()			def reassign_events(self, userfrom, userto):		for message in self.get_messages_by_user(userfrom):			message.user = userto				for describe in self.get_describes_by_user(userfrom):			describe.user = userto				for kick in self.get_kicks_by_user(userfrom):			kick.user = userto				for kick in self.get_kicks_by_target(userfrom):			kick.target = userto					for topic in self.get_topics_by_user(userfrom):			topic.user = userto	def get_line_count_by_hour(self, hour):		return len([line for line in self.lines if line.dttm.hour == hour])			def get_line_count_by_hour_range(self, starthour, endhour):		return len([line for line in self.lines if line.dttm.hour >= starthour and line.dttm.hour <= endline])			def get_line_count_by_weekday(self, weekday):		return len([line for line in self.lines if line.dttm.weekday() == weekday])			def get_line_count_by_user(self, user):		return len(self.get_messages_by_user(user)) + len(self.get_describes_by_user(user)) + len(self.get_kicks_by_user(user))	def get_messages_by_hour(self, hour):		return [msg for msg in self.messages if msg.dttm.hour == hour]	def get_messages_by_hour_range(self, starthour, endhour):		return [msg for msg in self.messages if msg.dttm.hour >= starthour and msg.dttm.hour <= endhour]			def get_messages_by_weekday(self, weekday):		return [msg for msg in self.messages if msg.dttm.weekday() == weekday]			def get_messages_by_user(self, user):		return [msg for msg in self.messages if msg.user == user]			def get_describes_by_user(self, user):		return [dsc for dsc in self.describes if dsc.user == user]			def get_kicks_by_user(self, user):		return [kck for kck in self.kicks if kck.user == user]			def get_kicks_by_target(self, user):		return [kck for kck in self.kicks if kck.target == user]		def get_topics_by_user(self, user):		return [tpc for tpc in self.topics if tpc.user == user]			def get_messages_that_match(self, regex):		return [msg for msg in self.messages if regex.match(msg.text)]			def get_users(self):		users = []			for message in self.messages:			if message.user not in users:				users.append(message.user)		for describe in self.describes:			if describe.user not in users:				users.append(describe.user)		for kick in self.kicks:			if kick.user not in users:				users.append(kick.user)			if kick.target not in users:				users.append(kick.target)		for topic in self.topics:			if message.user not in users:				users.append(topic.user)				return users			def _parse(self):		meter = Progress(0, len(self.lines), 5)				print "Parsing events from log file for channel", self.name, "on network", self.network.name + ":",				for line in self.lines:			if re_message.match(line.text):				user = self.network.get_user(re_message.match(line.text).group(1))				text = re_message.match(line.text).group(2)				self.messages.append(Message(line.dttm, user, cgi.escape(text)))						elif re_describe.match(line.text):				user = self.network.get_user(re_describe.match(line.text).group(1))				text = re_describe.match(line.text).group(2)				self.describes.append(Describe(line.dttm, user, cgi.escape(text)))						elif re_kick.match(line.text):				target = self.network.get_user(re_kick.match(line.text).group(1))				user = self.network.get_user(re_kick.match(line.text).group(2))				text = re_kick.match(line.text).group(3)				self.kicks.append(Kick(line.dttm, target, user, cgi.escape(text)))						elif re_ownkick.match(line.text):				user = self.network.get_user(re_ownkick.match(line.text).group(1))				text = re_ownkick.match(line.text).group(2)				self.kicks.append(Kick(line.dttm, self.network.ownuser, user, cgi.escape(text)))						elif re_topic.match(line.text):				user = self.network.get_user(re_topic.match(line.text).group(1))				text = re_topic.match(line.text).group(2)				self.topics.append(Topic(line.dttm, user, cgi.escape(text)))							elif re_join.match(line.text):				user = self.network.get_user(re_join.match(line.text).group(1))				self.joins.append(Join(line.dttm, user))						elif re_part.match(line.text):				user = self.network.get_user(re_part.match(line.text).group(1))				self.parts.append(Part(line.dttm, user))							meter.tock(1)			class ReportTemplate(object):	def __init__(self, filename, theme="default"):		self.filename = filename		self.theme = theme		self.contents = None		self.registry = {}				self._read()		def _read(self):		tplhandler = open("/Users/shidayu/Applications/silan-0.0.2/themes/" + self.theme + "/" + self.filename)		self.contents = tplhandler.read()		tplhandler.close()		def output_string(self):		dough = self.contents				for key in self.registry:			dough = dough.replace("{{silan:" + key + "}}", self.registry[key])					return dough			def register_tag(self, tag, something):		self.registry[tag] = something			def kill_registry(self):		self.registry = {}		class Report(ReportTemplate):	def __init__(self, channel, theme="default"):		self.channel = channel				ReportTemplate.__init__(self, "main.tpl", theme)				self._register()			def _register(self):		self.register_tag("channel", cgi.escape(self.channel.name))		self.register_tag("ownusername", cgi.escape(self.channel.network.ownuser.name))		self.register_tag("daycount", cgi.escape(str((self.channel.lines[-1].dttm - self.channel.lines[0].dttm).days)))		self.register_tag("startdate", cgi.escape(self.channel.lines[0].dttm.strftime("%A %B %d, %Y")))		self.register_tag("enddate", cgi.escape(self.channel.lines[-1].dttm.strftime("%A %B %d, %Y")))		self.register_tag("usercount", cgi.escape(str(len(self.channel.get_users()))))		self.register_tag("messagecount", cgi.escape(str(len(self.channel.messages))))		self.register_tag("describecount", cgi.escape(str(len(self.channel.describes))))		self.register_tag("kickcount", cgi.escape(str(len(self.channel.kicks))))		self.register_tag("modules", self._modules())		def _modules(self):		return ActivityByWeekday(self.channel, self.theme).output_string() + ActivityByHour(self.channel, self.theme).output_string() + UserActivity(self.channel, self.theme).output_string() + Protips(self.channel, self.theme).output_string() + TheStorySoFar(self.channel, self.theme).output_string()class ReportModule(ReportTemplate):	def __init__(self, name, theme):		self.name = name		self.theme = theme				ReportTemplate.__init__(self, "module.tpl", theme)				self._register()			def _register(self):		self.register_tag("title", self.name)		class User(object):	""" IRC user """		def __init__(self, name):		self.name = name		self.alternates = []		self.separates = []		self.quits = []		class TheStorySoFar(ReportModule):	def __init__(self, channel, theme):		self.channel = channel		self.theme = theme				ReportModule.__init__(self, "The Story So Far", self.theme)				self.re_endofsentence = re.compile(".*[?.!\"]+ ?$")		self.re_targetted = re.compile("(\S*) ?[:,-;!.]+ ?(.+)")				self.register_tag("contents", self._contents())			def _contents(self):		print "Reporting The Story So Far for", self.channel.name, "on network", self.channel.network.name + ":",		sys.stdout.flush()				jacket = ReportTemplate("story.tpl", self.theme)		paraformat = ReportTemplate("story.paragraph.tpl", self.theme)				internal = ""				textpool = []				print "gathering",		sys.stdout.flush()				alldescribes = [cgi.escape(dsc.user.name) + " " + dsc.text for dsc in self.channel.describes]				random.shuffle(alldescribes)				for describe in alldescribes:			if not self.re_endofsentence.match(describe):				describe += "."			textpool.append(describe)				for topic in [tpc.text for tpc in self.channel.topics]:			if not self.re_endofsentence.match(topic):				topic += "."			textpool.append(topic)						lastmessager = None					for message in self.channel.messages:			worktext = message.text						if self.re_targetted.match(worktext):				if self.channel.network.find_user(self.re_targetted.match(worktext).group(1)):					worktext = self.re_targetted.match(worktext).group(2)						if lastmessager == None:				lastmessager = message.user				textpool.append(worktext)			elif message.user == lastmessager:				if not self.re_endofsentence.match(textpool[-1]):					textpool[-1] += ", " + worktext				else:					textpool.append(worktext)			else:				if not self.re_endofsentence.match(textpool[-1]):					textpool[-1] += "."									lastmessager = message.user				textpool.append(worktext)						for kick in [kck.text for kck in self.channel.kicks]:			if not self.re_endofsentence.match(kick):				kick += "."			textpool.append(kick)				para = ""				w1 = "\n"		w2 = "\n"		# GENERATE TABLE		table = {}				print "chaining",				readmeter = Progress(0, len(textpool), 10)		for line in textpool:			for word in line.split():				table.setdefault( (w1, w2), [] ).append(word)				w1, w2 = w2, word							readmeter.tock(1)		table.setdefault( (w1, w2), [] ).append("\n") # Mark the end of the file		# GENERATE OUTPUT		w1 = "\n"		w2 = "\n"		maxwords = 512		minsentperpara = 4		maxsentperpara = 12				cursent = 0		for i in xrange(maxwords):			newword = random.choice(table[(w1, w2)])			if newword == "\n": 				break			para += newword + " "			w1, w2 = w2, newword						if self.re_endofsentence.match(para):				cursent += 1								if cursent >= minsentperpara and (cursent >= maxsentperpara or random.randint(minsentperpara, maxsentperpara) == cursent):						paraformat.register_tag("paragraph", para)						internal += paraformat.output_string()						para = ""						cursent = 0											if para != "":			paraformat.register_tag("paragraph", para)			internal += paraformat.output_string()				jacket.register_tag("paragraphs", internal)				return jacket.output_string()		class Protips(ReportModule):	def __init__(self, channel, theme):		self.channel = channel		self.theme = theme				self.re_protip = re.compile("^protip.*[:.,>-]+.+")				ReportModule.__init__(self, "Protips", self.theme)				self.register_tag("contents", self._contents())		def _contents(self):		print "Reporting Protips for", self.channel.name, "on network", self.channel.network.name + "...",		sys.stdout.flush()				jacket = ReportTemplate("generalmatches.tpl", self.theme)		tipformat = ReportTemplate("generalmatches.match.tpl", self.theme)				internal = ""				targetmessages = self.channel.get_messages_that_match(self.re_protip)				for protip in targetmessages:			if protip.user not in self.channel.network.no_protips:				tipformat.register_tag("dttm", cgi.escape(protip.dttm.strftime("%Y %b %d %I:%M %p")))				tipformat.register_tag("name", cgi.escape(protip.user.name))				tipformat.register_tag("quote", protip.text)							internal += tipformat.output_string()							tipformat.kill_registry()					jacket.register_tag("matches", internal)				print "done"				return jacket.output_string()		class UserActivity(ReportModule):	def __init__(self, channel, theme):		self.channel = channel		self.theme = theme				ReportModule.__init__(self, "User Activity", self.theme)				self.register_tag("contents", self._contents())			def _contents(self):		print "Reporting User Activity for", self.channel.name, "on network", self.channel.network.name + ":",		sys.stdout.flush()				jacket = ReportTemplate("useractivity.tpl", self.theme)		userformat = ReportTemplate("useractivity.user.tpl", self.theme)				internal = ""				print "counting",		sys.stdout.flush()		bymsgct = [(self.channel.get_line_count_by_user(user), user) for user in self.channel.get_users()]				print "sorting",		sys.stdout.flush()		bymsgct.sort()		bymsgct.reverse()				print "outputting",		sys.stdout.flush()		meter = Progress(0, min(25, len(bymsgct)), 10)				for index in xrange(0, min(25, len(bymsgct))):						userandct = bymsgct[index]			quoteminwords = 4					userformat.register_tag("rank", str(bymsgct.index(userandct) + 1))			userformat.register_tag("name", cgi.escape(userandct[1].name))			userformat.register_tag("count", str(userandct[0]))			if userandct[0] > 0:				usermessages = self.channel.get_messages_by_user(userandct[1])				quotestring = None								while quotestring == None and quoteminwords > 0:					usablemessages = [msg for msg in usermessages if msg.word_count() >= quoteminwords]										if len(usablemessages) > 0:						quotestring = usablemessages[random.randint(0, len(usablemessages) - 1)].text					else:						quoteminwords -= 1								userformat.register_tag("quote", quotestring)			else:				userformat.register_tag("quote", "")									internal += userformat.output_string()						userformat.kill_registry()						meter.tock(1)				jacket.register_tag("users", internal)				return jacket.output_string()		class ActivityByHour(ReportModule):	def __init__(self, channel, theme):		self.channel = channel		self.theme = theme				ReportModule.__init__(self, "Activity by Hour", self.theme)				self.register_tag("contents", self._contents())			def _contents(self):		print "Reporting Activity by Hour for", self.channel.name, "on network", self.channel.network.name + "...",		sys.stdout.flush()				jacket = ReportTemplate("activitybyhour.tpl", self.theme)		hourformat = ReportTemplate("activitybyhour.hour.tpl", self.theme)				internal = ""				hourbox = []				for hour in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]:			hourbox.append((self.channel.get_line_count_by_hour(hour), hour))					hourmax = max([inhour[0] for inhour in hourbox])					for envelope in hourbox:			if hourmax > 0:				hourformat.register_tag("gapsize", str(150 - (int(float(envelope[0]) / float(hourmax) * 150))))				hourformat.register_tag("barsize", str(int(float(envelope[0]) / float(hourmax) * 150)))			else:				hourformat.register_tag("gapsize", "150")				hourformat.register_tag("barsize", "0")							hourformat.register_tag("count", str(envelope[0]))			hourformat.register_tag("hour", str(envelope[1]))						internal += hourformat.output_string()						hourformat.kill_registry()				jacket.register_tag("hours", internal)				print "done"				return jacket.output_string()		class ActivityByWeekday(ReportModule):	def __init__(self, channel, theme):		self.channel = channel		self.theme = theme				ReportModule.__init__(self, "Activity by Day of the Week", self.theme)				self.register_tag("contents", self._contents())			def _contents(self):		print "Reporting Activity by Day of the Week for", self.channel.name, "on network", self.channel.network.name + "...",		sys.stdout.flush()				jacket = ReportTemplate("activitybyweekday.tpl", self.theme)		dayformat = ReportTemplate("activitybyweekday.day.tpl", self.theme)				internal = ""				daybox = []				for day in [6, 0, 1, 2, 3, 4, 5]:			daybox.append((self.channel.get_line_count_by_weekday(day), day))					daymax = max([inday[0] for inday in daybox])					for envelope in daybox:			if daymax > 0:				dayformat.register_tag("gapsize", str(150 - (int(float(envelope[0]) / float(daymax) * 150))))				dayformat.register_tag("barsize", str(int(float(envelope[0]) / float(daymax) * 150)))			else:				dayformat.register_tag("gapsize", "150")				dayformat.register_tag("barsize", "0")							dayformat.register_tag("count", str(envelope[0]))			dayformat.register_tag("day", {6: "Sun", 0: "Mon", 1: "Tue", 2: "Wed", 3: "Thu", 4: "Fri", 5: "Sat"}[envelope[1]])						internal += dayformat.output_string()						dayformat.kill_registry()				jacket.register_tag("days", internal)				print "done"				return jacket.output_string()			### Functions#def are_separate(user1, user2):	for sep in user1.separates:		if nick_belongs_to_user(sep, user2):			return 1	for sep in user2.separates:		if nick_belongs_to_user(sep, user1):			return 1	return 0	def nick_belongs_to_user(nick, user):		return nick.lower() == user.name.lower() or nick.lower() in [alt.lower() for alt in user.alternates]	def main():	appversion = "0.0.2"		print '\n\nSILAN', appversion, '- Simple IRC Log Analyzer - http://slackforge.com/silan/'	print 'Copyright (c) 2007 Shidayu <shidayu@gmail.com>'	try:		appinstance = Silan()						except:		print "An unexpected error has occurred:", sys.exc_info()[0]		raise### Begin Here!#if __name__=="__main__":	main()