logger_src/LICENSE0000644000175000001440000000300210613137260013055 0ustar gogousersCopyright (c) 2005, Georg Bauer All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. Neither the name of the project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 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. logger_src/__init__.py0000644000175000001440000000000010613137260014154 0ustar gogouserslogger_src/bin/0000755000175000001440000000000010613137456012634 5ustar gogouserslogger_src/bin/loggerbot.py0000755000175000001440000001735410613137456015207 0ustar gogousers#!/usr/bin/python import os import md5 import sys import time import socket if not sys.path.count('/home/webstrana/django/python-local/'): sys.path.insert(0, '/home/webstrana/django/python-local/') if not sys.path.count('/home/webstrana/django/'): sys.path.insert(0, '/home/webstrana/django/') if not sys.path.count('/home/webstrana/django/django_src/'): sys.path.insert(0, '/home/webstrana/django/django_src/') os.environ['DJANGO_SETTINGS_MODULE'] = 'logger.settings' import irclib import getopt import logging import logging.handlers from logger.logviewer.models import Message, Channel def become_daemon(ourHomeDir='.',outLog='/dev/null',errLog='/dev/null'): """ Robustly turn us into a UNIX daemon, running in ourHomeDir. Modelled after the original code of this module and some sample code from the net. """ # first fork try: if os.fork() > 0: sys.exit(0) # kill off parent except OSError, e: sys.stderr.write("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror)) sys.exit(1) os.setsid() os.chdir(ourHomeDir) os.umask(0) # second fork try: if os.fork() > 0: sys.exit(0) except OSError, e: sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror)) sys.exit(1) si = open('/dev/null', 'r') so = open(outLog, 'a+', 0) se = open(errLog, 'a+', 0) os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) host = None port = 6667 nick = 'logger_' secret = '' logfile = None delay = 60 verbose = False quiet = False daemon = False test = False (opts, args) = getopt.getopt(sys.argv[1:], 'tvqbh:p:s:n:l:d:') for o, v in opts: if o == '-h': host = v elif o == '-p': port = int(v) elif o == '-s': secret = v elif o == '-n': nick = v elif o == '-v': verbose = True quiet = False elif o == '-q': verbose = False quiet = True elif o == '-b': daemon = True elif o == '-l': logfile = v elif o == '-d': delay = int(v) elif o == '-t': test = True if host is None: print "usage: loggerbot.py [-v] [-q] [-b] [-n ] -h [-p ] [-s ] [-l ] [-d ]" sys.exit(1) if daemon and logfile is None: logfile = os.path.expanduser('~/loggerbot.log') if daemon: become_daemon() fmt = logging.Formatter('%(asctime)s %(levelname)s %(message)s') hdl = logging.StreamHandler() if logfile is not None: hdl = logging.handlers.RotatingFileHandler(logfile, 'a', 1000000, 7) hdl.setFormatter(fmt) logging.getLogger().addHandler(hdl) if quiet: logging.getLogger().setLevel(logging.WARN) elif verbose: logging.getLogger().setLevel(logging.DEBUG) else: logging.getLogger().setLevel(logging.INFO) def refresh_channels(): channels = [] channelobjs = {} for channel in Channel.objects.all(): channels.append(channel.name) channelobjs[channel.name] = channel return channels, channelobjs channels, channelobjs = refresh_channels() class Client(irclib.SimpleIRCClient): def __init__(self, host, port, nick, password): irclib.SimpleIRCClient.__init__(self) self.inmotd = 0 self.host = host self.port = port self.nick = nick self.password = password self.alternate = '' ctx = md5.new(password or '') ctx.update(host) ctx.update(str(time.time())) self.ticket = ctx.hexdigest() self.last_check = 0 def connect(self): try: irclib.SimpleIRCClient.connect(self, self.host, self.port, self.nick+self.alternate, self.password) return True except irclib.ServerConnectionError: logging.error('problems connecting to %s:%d', self.host, self.port) return False def check_nick(self, connect): if connect.get_nickname() != self.nick: connect.ison([self.nick]) def join_channels(self, chans): global channels, channelobjs chandict = dict([(el, True) for el in chans]) channels, channelobjs = refresh_channels() for c in chans: if c not in channelobjs: logging.info('parting removed channel %s', c) self.connection.part('#'+c) for c in channels: if c not in chandict: logging.info('joining missing channel %s', c) self.connection.join('#'+c) self.last_check = time.time() def on_ison(self, connect, event): if self.nick not in [el.strip() for el in event.arguments()]: logging.warn('nick %s released, fetching it', self.nick) self.alternate = '' connect.nick(self.nick) def on_nick(self, connect, event): if event.target() == self.nick and connect.get_nickname() == self.nick: connect.privmsg('nickserv', 'identify %s' % self.password) self.check_nick(connect) def on_whoisuser(self, connect, event): if time.time() > self.last_check + 3*delay: logging.warn('not joined to any channel, rejoining them') self.join_channels([]) def on_whoischannels(self, connect, event): chans = [el[1:].strip() for el in event.arguments()[1].split() if el.startswith('#')] self.join_channels(chans) def check_online(self): self.connection.privmsg(self.connection.get_nickname(), self.ticket) self.connection.whois([self.connection.get_nickname()]) self.connection.execute_delayed(delay, self.check_online) def on_privmsg(self, connect, event): if ''.join(event.arguments()) == self.ticket: logging.debug('onlinecheck successfull') else: logging.warn('PRIVMSG %s', ''.join(event.arguments())) def on_motd(self, connect, event): if not self.inmotd: logging.info('MOTD started') self.inmotd = 1 def on_endofmotd(self, connect, event): self.inmotd = 0 logging.info('MOTD ended, join channels') connect.ison([self.nick]) self.check_online() def on_nicknameinuse(self, connect, event): self.alternate += '_' connect.nick(self.nick+self.alternate) def on_join(self, connect, event): if event.source().split('!')[0] == self.nick+self.alternate: logging.info('JOINed %s', event.target()) def on_privnotice(self, connect, event): logging.info('PRIVNOTICE %s', ''.join(event.arguments())) def on_pubmsg(self, connect, event): if event.target().startswith('#') and event.target()[1:] in channels: self.log_message(event.source().split('!')[0], event.target()[1:], ''.join(event.arguments())) self.check_nick(connect) def on_action(self, connect, event): if event.target().startswith('#') and event.target()[1:] in channels: self.log_message(event.source().split('!')[0], event.target()[1:], '\x01ACTION %s\x01' % ''.join(event.arguments())) self.check_nick(connect) def log_message(self, source, target, message): if test: print target, source, message else: msg = Message(channel=channelobjs[target], nick=source, line=message) msg.save() socket.setdefaulttimeout(60) irc = irclib.IRC() server = Client(host, port, nick, secret) try: while 1: if server.connect(): logging.info('connecting to server') try: server.start() except irclib.ServerNotConnectedError: pass logging.info('dropped out of server loop') time.sleep(delay) finally: logging.info('SYSTEM bot shutdown') logging.shutdown() logger_src/bin/django-scgi.py0000755000175000001440000001267610613137260015403 0ustar gogousers#!/usr/bin/python import os import sys if os.name == 'posix': def become_daemon(ourHomeDir='.',outLog='/dev/null',errLog='/dev/null'): """ Robustly turn us into a UNIX daemon, running in ourHomeDir. Modelled after the original code of this module and some sample code from the net. """ # first fork try: if os.fork() > 0: sys.exit(0) # kill off parent except OSError, e: sys.stderr.write("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror)) sys.exit(1) os.setsid() os.chdir(ourHomeDir) os.umask(0) # second fork try: if os.fork() > 0: sys.exit(0) except OSError, e: sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror)) sys.exit(1) si = open('/dev/null', 'r') so = open(outLog, 'a+', 0) se = open(errLog, 'a+', 0) os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) else: def become_daemon(ourHomeDir='.',outLog=None,errLog=None): """ If we are not running under a POSIX system, just simulate the daemon mode by doing redirections and directory changeing """ os.chdir(ourHomeDir) os.umask(0) sys.stdin.close() sys.stdout.close() sys.stderr.close() if errLog and outLog: sys.stderr=open (errLog, 'a', 0) sys.stdout=open (outLog, 'a', 0) elif errLog: sys.stderr=open (errLog, 'a', 0) sys.stdout=NullDevice () elif outLog: sys.stdout=open (outLog, 'a', 0) sys.stderr=NullDevice () else: sys.stdout = NullDevice() sys.stderr = NullDevice() class NullDevice: """ A substitute for stdout and stderr that writes to nowhere. This is a substitute for /dev/null """ def write(self, s): pass def main(): from flup.server.scgi_fork import WSGIServer from django.core.handlers.wsgi import WSGIHandler import getopt (opts, args) = getopt.getopt(sys.argv[1:], 'f:s:h:p:', ['settings=','socket=','host=','port=', 'minspare=', 'maxspare=', 'maxchildren=', 'daemon', 'etclog=', 'errorlog=', 'workdir=', 'projects=']) socket = None host = None port = None minspare = 1 maxspare = 5 maxchildren = 50 daemon = None workdir = '.' etclog = '/dev/null' errorlog = '/dev/null' projects = [] for (o, v) in opts: if o in ('-s', '--socket'): socket = v elif o in ('-h', '--host'): host = v elif o in ('-p', '--port'): port = int(v) elif o in ('-f', '--settings'): os.environ['DJANGO_SETTINGS_MODULE'] = v elif o in ('--minspare',): minspare = int(v) elif o in ('--maxspare',): maxspare = int(v) elif o in ('--maxchildren',): maxchildren = int(v) elif o in ('--daemon',): daemon = 1 elif o in ('--errorlog',): errorlog = v elif o in ('--etclog',): etclog = v elif o in ('--workdir',): workdir = v elif o in ('--projects',): projects.append(v) # add projects to path for p in projects: if p not in sys.path: sys.path.insert(0, p) # if we should run as a daemon, use the above function to turn us # into one reliably. This should correctly detach from the tty. if daemon: become_daemon(ourHomeDir=workdir, outLog=etclog, errLog=errorlog) if socket and not host and not port: WSGIServer(WSGIHandler(), minSpare=minspare, maxSpare=maxspare, maxChildren=maxchildren, bindAddress=socket).run() elif not socket and host and port: WSGIServer(WSGIHandler(), minSpare=minspare, maxSpare=maxspare, maxChildren=maxchildren, bindAddress=(host, port)).run() else: print "usage: django-scgi.py [--settings=] --socket=" print " or: django-scgi.py [--settings=] --host== --port=" print print " additional options are:" print " --minspare=" print " --maxspare=" print " --maxchildren=" print print " --daemon" print " --etclog=" print " --errorlog=" print " --workdir=" print print " --projects=" if __name__ == '__main__': # first patch our own version of socketpair into the sockets module # if we don't have it already (comes with Python 2.4) import socket if not hasattr(socket, 'socketpair'): import eunuchs.socketpair def socketpair(): (p,c) = eunuchs.socketpair.socketpair() (pp, cc) = (socket.fromfd(p,1,1), socket.fromfd(c,1,1)) os.close(p) os.close(c) return (pp, cc) socket.socketpair = socketpair main() logger_src/bin/django-swap.py0000755000175000001440000001032610613137260015416 0ustar gogousers#!/usr/bin/python import os import sys if os.name == 'posix': def become_daemon(ourHomeDir='.',outLog='/dev/null',errLog='/dev/null'): """ Robustly turn us into a UNIX daemon, running in ourHomeDir. Modelled after the original code of this module and some sample code from the net. """ # first fork try: if os.fork() > 0: sys.exit(0) # kill off parent except OSError, e: sys.stderr.write("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror)) sys.exit(1) os.setsid() os.chdir(ourHomeDir) os.umask(0) # second fork try: if os.fork() > 0: sys.exit(0) except OSError, e: sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror)) sys.exit(1) si = open('/dev/null', 'r') so = open(outLog, 'a+', 0) se = open(errLog, 'a+', 0) os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) else: def become_daemon(ourHomeDir='.',outLog=None,errLog=None): """ If we are not running under a POSIX system, just simulate the daemon mode by doing redirections and directory changeing """ os.chdir(ourHomeDir) os.umask(0) sys.stdin.close() sys.stdout.close() sys.stderr.close() if errLog and outLog: sys.stderr=open (errLog, 'a', 0) sys.stdout=open (outLog, 'a', 0) elif errLog: sys.stderr=open (errLog, 'a', 0) sys.stdout=NullDevice () elif outLog: sys.stdout=open (outLog, 'a', 0) sys.stderr=NullDevice () else: sys.stdout = NullDevice() sys.stderr = NullDevice() class NullDevice: """ A substitute for stdout and stderr that writes to nowhere. This is a substitute for /dev/null """ def write(self, s): pass def main(): from scgiserver import SWAP from scgi.scgi_server import SCGIServer from django.core.handlers.wsgi import WSGIHandler class SCGIAppHandler(SWAP): def __init__(self, *args, **kwargs): self.prefix = '' self.app_obj = WSGIHandler() SWAP.__init__(self, *args, **kwargs) import getopt (opts, args) = getopt.getopt(sys.argv[1:], 'f:h:p:', ['settings=','host=','port=', 'daemon', 'etclog=', 'errorlog=', 'workdir=', 'projects=']) host = None port = None daemon = None workdir = '.' etclog = '/dev/null' errorlog = '/dev/null' projects = [] for (o, v) in opts: if o in ('-h', '--host'): host = v elif o in ('-p', '--port'): port = int(v) elif o in ('-f', '--settings'): os.environ['DJANGO_SETTINGS_MODULE'] = v elif o in ('--daemon',): daemon = 1 elif o in ('--errorlog',): errorlog = v elif o in ('--etclog',): etclog = v elif o in ('--workdir',): workdir = v elif o in ('--projects',): projects.append(v) # add projects to path for p in projects: if p not in sys.path: sys.path.insert(0, p) # if we should run as a daemon, use the above function to turn us # into one reliably. This should correctly detach from the tty. if daemon: become_daemon(ourHomeDir=workdir, outLog=etclog, errLog=errorlog) if host and port: SCGIServer(SCGIAppHandler, host=host, port=port).serve() else: print "usage: django-scgi.py [--settings=] --host== --port=" print print " additional options are:" print " --daemon" print " --etclog=" print " --errorlog=" print " --workdir=" print print " --projects=" if __name__ == '__main__': main() logger_src/logviewer/0000755000175000001440000000000010613137421014057 5ustar gogouserslogger_src/logviewer/models.py0000644000175000001440000000247410613137260015724 0ustar gogousers# -*- coding: utf-8 -*- import re from django.db import models from django.utils.html import escape # Create your models here. class Channel(models.Model): name = models.CharField('Channel', maxlength=60, unique=True) tracurl = models.CharField('Trac URL', maxlength=200, null=True, blank=True) def __str__(self): return self.name def get_absolute_url(self): return '/logger/%s/' % self.name class Meta: ordering = ['name'] class Admin: fields = ( (None, { 'fields': ( 'name', 'tracurl', ) }), ) list_display = ('name', 'tracurl') class Message(models.Model): channel = models.ForeignKey(Channel, db_index=True) time = models.DateTimeField('Time', auto_now=True, db_index=True) nick = models.CharField('User', maxlength=30, db_index=True) line = models.TextField('Message') def __str__(self): return self.nick + ': ' + self.line def get_absolute_url(self): return "%s%s/#R%s" % (self.channel.get_absolute_url(), self.time.strftime("%Y/%m/%d"), self.id) def isaction(self): return self.line.startswith('\x01ACTION ') and self.line.endswith('\x01') class Admin: pass logger_src/logviewer/templatetags/0000755000175000001440000000000010613137421016551 5ustar gogouserslogger_src/logviewer/templatetags/logviewer_taglib.py0000644000175000001440000003021410613137260022451 0ustar gogousers# -*- coding: utf-8 -*- """ These are template tags and filters for the logviewer. """ import datetime import calendar import re from django import template from django.utils.html import escape from django.template import loader from logger.logviewer.models import Message class CalendarMonthNode(template.Node): """ This tag renders a list of day links for a given month and channel. """ def __init__(self, month, channel): self.month = month self.channel = channel def render(self, context): month = template.resolve_variable(self.month, context) nextyear = month.year nextmonth = month.month+1 if nextmonth > 12: nextmonth = 1 nextyear += 1 channel = template.resolve_variable(self.channel, context) l = Message.objects.dates('time', 'day').filter( time__gte=datetime.datetime(month.year,month.month,1), time__lt=datetime.datetime(nextyear, nextmonth, 1), channel__id__exact=channel.id ).order_by('time') h = dict([(el.day,1) for el in l]) output = template.NodeList() output.append(template.TextNode('
    ')) (wd, maxday) = calendar.monthrange(month.year, month.month) pmyear = month.year pmmonth = month.month - 1 if pmmonth == 0: pmmonth = 12 pmyear -= 1 try: hasprev = Message.objects.filter( time__lte=datetime.datetime(pmyear, pmmonth, calendar.monthrange(pmyear, pmmonth)[1]), channel__id__exact=channel.id ).order_by('time')[0] except Message.DoesNotExist: hasprev = None if hasprev: pmmonth = hasprev.time.month pmyear = hasprev.time.year nmyear = month.year nmmonth = month.month + 1 if nmmonth == 13: nmmonth = 1 nmyear += 1 try: hasnext = Message.objects.filter( time__gte=datetime.datetime(nmyear, nmmonth, 1), channel__id__exact=channel.id ).order_by('time')[0] except Message.DoesNotExist: hasnext = None if hasnext: nmmonth = hasnext.time.month nmyear = hasnext.time.year if hasprev: output.append(template.TextNode('' % ( channel.name, pmyear, pmmonth, pmmonth, pmyear))) else: output.append(template.TextNode('' % (pmmonth, pmyear))) for day in range(1, maxday+1): if h.has_key(day): output.append(template.TextNode('
  • %d
  • ' % ( channel.name, month.year, month.month, day, day))) else: output.append(template.TextNode('
  • %d
  • ' % day)) if hasnext: output.append(template.TextNode('' % ( channel.name, nmyear, nmmonth, nmmonth, nmyear))) else: output.append(template.TextNode('' % (nmmonth, nmyear))) output.append(template.TextNode('
')) return output.render(context) def do_calendar_month(parser, token): """ This tag generates a list of days for a given month with links to day pages if there are pictures for that day. sample usage:: {{ calendar_month day for channel }} You pass in a datetime object from which the month is taken for the calendar and a channel for which the calendar is drawn. """ bits = token.contents.split() if len(bits) != 4: raise template.TemplateSyntaxError, "'calendar_month' statements must be 'calendar_month for ': %s" % token.contents if bits[2] != 'for': raise template.TemplateSyntaxError, "'calendar_month' statements must be 'calendar_month for ': %s" % token.contents return CalendarMonthNode(bits[1], bits[3]) class ClassByValue: """ This class captures a counter that will count up for every different value and produce class names. Same values will get same class names. If there are more values than classnames, it will start from the startvalue. You initialize a intance with a count of different class names to produce (it counts from 0 to count excludig, like range(count)) and a pattern to use to produce the actual class names. """ def __init__(self, minvalue, maxvalue, pattern): self.minvalue = minvalue self.maxvalue = maxvalue self.counter = minvalue self.colors = {} self.pattern = pattern def __repr__(self): return '' % ( self.minvalue, self.maxvalue, repr(self.pattern) ) def class_name(self, counter): """ This method generates a classname from a pattern and the counter. It can be overloaded to produce more complex classnames. """ return self.pattern % counter def class_by_value(self, value): """ This method does the actual class name pulling - it counts up different values and assigns those class names each. """ if not self.colors.has_key(value): self.colors[value] = self.class_name(self.counter) self.counter += 1 if self.counter > self.maxvalue: self.counter = self.minvalue return self.colors[value] class ClassByValueNode(template.Node): def __init__(self, object, classbyvalue): self.object = object self.classbyvalue = classbyvalue def render(self, context): object = template.resolve_variable(self.object, context) classbyvalue = template.resolve_variable(self.classbyvalue, context) if not hasattr(classbyvalue, 'class_by_value'): raise template.TemplateSyntaxError, "'class_by_value' statements require a ClassByValue object: %s" % repr(classbyvalue) return classbyvalue.class_by_value(str(object)) def do_class_by_value(parser, token): """ This tag produces a class name for a given value in a given list of possible values. The same value will allways get the same class. New values will get new classes until the number of different classes in the ClassByValue object are taken - then it will redo from start. Usage is like this::
  • {{ object.nick }}
  • Here object.nick is the value that is styled and the classbyvalue object needs to be a ClassByValue instance. This tag is usefull to colorize different values in a way that gives users an easy hint to recognize same values by giving those values the same styling. """ bits = token.contents.split() if len(bits) != 4: raise template.TemplateSyntaxError, "'class_by_value' statements must be 'class_by_value in ': %s" % token.contents if bits[2] != 'in': raise template.TemplateSyntaxError, "'class_by_value' statements must be 'class_by_value in ': %s" % token.contents return ClassByValueNode(bits[1], bits[3]) class FormattedCounterNode(template.Node): def __init__(self, minvalue, maxvalue, variable, format): self._minvalue = minvalue self._maxvalue = maxvalue self._format = format self._variable = variable def render(self, context): if self._minvalue.isdigit(): minvalue = int(self._minvalue) else: minvalue = template.resolve_variable(self._minvalue, context) if self._maxvalue.isdigit(): maxvalue = int(self._maxvalue) else: maxvalue = template.resolve_variable(self._maxvalue, context) format = template.resolve_variable(self._format, context) context[self._variable] = ClassByValue(minvalue, maxvalue, format) return '' def do_formatted_counter(parser, token): """ This tag creates a counter object that produces formatted output for usage in the class_by_value tag. Usage is like this:: {% formatted_counter 1 to 27 as counter with "user%d" %} This will create a variable counter that produces values from user1 to user27 for every different value passed in in the class_by_value tag. """ bits = token.contents.split(None, 7) if len(bits) != 8: raise template.TemplateSyntaxError, "'formatted_counter' statements must be 'formatted_counter to as with ': %s" % token.contents if bits[2] != 'to' or bits[4] != 'as' or bits[6] != 'with': raise template.TemplateSyntaxError, "'formatted_counter' statements must be 'formatted_counter to as with ': %s" % token.contents return FormattedCounterNode(bits[1], bits[3], bits[5], bits[7]) # some regular expressions used in the code url_re = re.compile(r'(https?://[a-zA-Z0-9\-\.]+/[^ \t\n,]*)') ticket_re = re.compile(r'#\d+') changeset_re = re.compile(r'\[\d+\]') report_re = re.compile(r'\{\d+\}') class OnlyMessageNode(template.Node): def __init__(self, obj): self._obj = obj def render(self, context): obj = template.resolve_variable(self._obj, context) if not context.has_key('channel_cache'): context['channel_cache'] = {} if not context['channel_cache'].has_key(obj.channel_id): context['channel_cache'][obj.channel_id] = obj.channel.tracurl turl = context['channel_cache'][obj.channel_id] def normalize(s): if s.startswith('\x01ACTION '): s = s[8:] l = url_re.findall(s) if l: for el in l: shorty = el if len(shorty) > 50: shorty = shorty[:47] + ' ...' s = s.replace(el, '%s' % (el, shorty)) else: if turl: for el in ticket_re.findall(s): s = s.replace(el, '%s' % (turl, el[1:], el)) for el in changeset_re.findall(s): s = s.replace(el, '%s' % (turl, el[1:-1], el)) for el in report_re.findall(s): s = s.replace(el, '%s' % (turl, el[1:-1], el)) return s if obj.isaction(): return normalize(escape(obj.line[8:-1])) else: return normalize(escape(obj.line)) def do_onlymessage(parser, token): """ This tag pulls out only the message of a line. It will cache some database access so that it reduces the database load. Usage is like this:: {% onlymessage object %} This will output the text with hyperlinked trac references. """ bits = token.contents.split() if len(bits) != 2: raise template.TemplateSyntaxError, "'onlymessage' statements must be 'onlymessage ': %s" % token.contents return OnlyMessageNode(bits[1]) from django.template.defaultfilters import stringfilter def urlizetrunc(value, limit): """ Мое што не става nofollow. Converts URLs into clickable links, truncating URLs to the given character limit, and adding 'rel=nofollow' attribute to discourage spamming. Argument: Length to truncate URLs to. """ from django.utils.html import urlize return urlize(value, trim_url_limit=int(limit), nofollow=False) urlizetrunc = stringfilter(urlizetrunc) #register = template.Library() #register.tag('formatted_counter', do_formatted_counter) #register.tag('class_by_value', do_class_by_value) #register.tag('calendar_month', do_calendar_month) register = template.Library() register.tag('formatted_counter', do_formatted_counter) register.tag('class_by_value', do_class_by_value) register.tag('calendar_month', do_calendar_month) register.tag('onlymessage', do_onlymessage) register.filter('urlizetrunc', urlizetrunc) logger_src/logviewer/templatetags/__init__.py0000644000175000001440000000000010613137260020651 0ustar gogouserslogger_src/logviewer/__init__.py0000644000175000001440000000000010613137260016157 0ustar gogouserslogger_src/logviewer/views.py0000644000175000001440000000756010613137260015577 0ustar gogousers# -*- coding: utf-8 -*- "wrapped generic views for viewing the logs" import datetime import time from django.http import Http404 from django.template import RequestContext from django.shortcuts import get_object_or_404, render_to_response from logger.logviewer.models import Channel, Message from logger.logviewer.templatetags import logviewer_taglib def index(request, kanal): # Направи datetime објект од внесените податоци или денеска ако нема ништо. kanalot = get_object_or_404(Channel,name=kanal) kolku = Message.objects.filter(channel=kanalot).count() poraki = Message.objects.filter(channel=kanalot).order_by('time')[kolku-100:kolku] ovojmesec = datetime.datetime.today().month # zemi za koi datumi ima poraki datumi = Message.objects.filter(channel=kanalot,time__month=ovojmesec).dates('time','day') return render_to_response( 'index.html', {'poraki':poraki, 'kanal':kanalot.name, 'datumi':datumi}) def podatum(request, kanal, year, month, day): # Направи datetime објект од внесените податоци или денеска ако нема ништо. kanalot = get_object_or_404(Channel,name=kanal) try: datum_poc = datetime.datetime(*time.strptime(year+month+day+'0000', '%Y%m%d%H%M')[:3]) except ValueError: raise Http404 datum_kraj = datum_poc + datetime.timedelta(1) # zemi gi porakite deneska poraki = Message.objects.filter(channel=kanalot,time__range=(datum_poc,datum_kraj)).order_by('time') # zemi za koi datumi ima poraki datumi = Message.objects.filter(channel=kanalot,time__month=datum_poc.month).dates('time','day') return render_to_response( 'podatum.html', {'poraki':poraki, 'kanal':kanalot.name, 'datum':datum_poc.date(),'datumi':datumi}) def arhiva(request, kanal): kanalot = get_object_or_404(Channel, name__exact=kanal) denovi = Message.objects.filter(channel=kanalot).dates('time','day') return render_to_response('arhiva.html',{'channel':kanalot,'denovi':denovi}) def channel_search(request, kanal): channel = get_object_or_404(Channel, name__exact=kanal) query = request.GET.get('q', '').split() if not query: return render_to_response('search_error.html', { 'channel': channel, 'query': ' '.join(query), 'message': 'Се пребарува барем по еден збор!', }, context_instance=RequestContext(request)) objects = channel.message_set for word in query: if word.startswith('nick:'): objects = objects.filter(nick__icontains=word[6:]) else: objects = objects.filter(line__icontains=word) objects = objects.order_by('time') dolzina = objects.count() if not objects: return render_to_response('search_error.html', { 'channel': channel, 'query': ' '.join(query), 'message': "Не најдов пораки кои содржат '%s'!" % ' '.join(query), }, context_instance=RequestContext(request)) if dolzina > 500: return render_to_response('search.html', { 'object_list': objects[-500:], 'channel': channel, 'query': ' '.join(query), 'message': "Најдов %d резултати пребарувајќи за '%s', еве ги последните 500." % (len(objects), ' '.join(query)), }, context_instance=RequestContext(request)) else: return render_to_response('search.html', { 'object_list': objects, 'channel': channel, 'query': ' '.join(query), 'message': "Најдов %d резултати пребарувајќи за '%s'." % (len(objects), ' '.join(query)), }, context_instance=RequestContext(request)) logger_src/manage.py0000755000175000001440000000071710613137260013667 0ustar gogousers#!/usr/bin/env python from django.core.management import execute_manager try: import settings # Assumed to be in the same directory. except ImportError: import sys sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__) sys.exit(1) if __name__ == "__main__": execute_manager(settings) logger_src/media/0000755000175000001440000000000010613137260013134 5ustar gogouserslogger_src/settings.py0000644000175000001440000000275110613137356014302 0ustar gogousers# Django settings for logger project. DEBUG = True TEMPLATE_DEBUG = DEBUG DEFAULT_CHARSET = 'utf-8' TIME_ZONE = 'Europe/Skopje' LANGUAGE_CODE = 'mk' USE_I18N = True ADMINS = ( ('Georgi Stanojevski', 'glisha@gmail.com'), ) MANAGERS = ADMINS DEFAULT_FROM_EMAIL = 'glisha@gmail.com' DATABASE_ENGINE = 'mysql' # 'postgresql', 'mysql', or 'sqlite3'. DATABASE_NAME = '' # Or path to database file if using sqlite3. DATABASE_USER = '' # Not used with sqlite3. DATABASE_PASSWORD = '' # Not used with sqlite3. DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3. SITE_ID = 1 # Absolute path to the directory that holds media. # Example: "/home/media/media.lawrence.com/" MEDIA_ROOT = '/home/webstrana/django/logger/' # URL that handles the media served from MEDIA_ROOT. # Example: "http://media.lawrence.com" MEDIA_URL = '/' # Make this unique, and don't share it with anybody. SECRET_KEY = 'asdasdadsads' MIDDLEWARE_CLASSES = ( "django.contrib.sessions.middleware.SessionMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.locale.LocaleMiddleware", ) ROOT_URLCONF = 'logger.urls' TEMPLATE_DIRS = ( '/home/webstrana/django/logger/templates', ) INSTALLED_APPS = ( 'django.contrib.sites', 'django.contrib.auth', 'logger.logviewer', 'django.contrib.sessions', 'django.contrib.contenttypes', 'django.contrib.admin', ) logger_src/templates/0000755000175000001440000000000010613137260014053 5ustar gogouserslogger_src/templates/404.html0000644000175000001440000000004510613137260015247 0ustar gogousersE ne ja najdov stranata. Greshka si. logger_src/templates/arhiva.html0000644000175000001440000000052010613137260016210 0ustar gogousers{% extends "base.html" %} {% block sodrzhina %}

    Архива за #{{ channel }}

    {% for den in denovi %} {% ifchanged den.month %}

    {{ den|date:"m.Y" }}


    {% endifchanged %} {{ den|date:"d m Y" }} {% endfor %} {% endblock %} logger_src/templates/index.html0000644000175000001440000000247110613137260016054 0ustar gogousers{% extends "base.html" %} {% load logviewer_taglib %} {% block sodrzhina %}
    {% formatted_counter 0 to 26 as userclasses with ' col-%d"' %}

    Последните 100 пораки на #{{ kanal }}

    (Месецов: {% for den in datumi %} {{ den.day }} {% endfor %} цела архива)

    {% regroup poraki by time|date:"Y-m-d" as grouped %} {% for group in grouped %} {% endfor %} {% endblock %} logger_src/templates/search_error.html0000644000175000001440000000103110613137260017412 0ustar gogousers{% extends "base.html" %} {% load logviewer_taglib %} {% block sodrzhina %}

    {{ message|escape }}

    Може да пребарувате по зборови. Мора сите зборови да ги има во пораката за да биде најдена.

    Ако впишете „nick:zbor“ ќе ги барате пораките од тој nick

    {% endblock %} logger_src/templates/podatum.html0000644000175000001440000000244010613137260016412 0ustar gogousers{% extends "base.html" %} {% load logviewer_taglib %} {% block sodrzhina %}
    {% formatted_counter 0 to 26 as userclasses with ' col-%d"' %}

    #{{ kanal }} (Месецов: {% for den in datumi %} {{ den.day }} {% endfor %} цела архива)

    {% regroup poraki by time|date:"Y-m-d" as grouped %} {% for group in grouped %} {% endfor %} {% endblock %} logger_src/templates/500.html0000644000175000001440000000002410613137260015241 0ustar gogousersUh se sjeba neshto. logger_src/templates/search.html0000644000175000001440000000200110613137260016177 0ustar gogousers{% extends "base.html" %} {% load logviewer_taglib %} {% block sodrzhina %}

    #{{ channel }}

    {% formatted_counter 0 to 26 as userclasses with ' col-%d"' %} {% regroup object_list by time|date:"Y-m-d" as grouped %}
    {{ message|escape }}
    {% for group in grouped %}
      {% for poraka in group.list %} {% if forloop.first %}
      {% endif %}
    • {{ poraka.time.time }} {{ poraka.nick|escape }}:  {{ poraka.line|escape|urlizetrunc:30 }}
    • {% endfor %}
    {% endfor %} {% endblock %} logger_src/templates/base.html0000644000175000001440000000515510613137260015661 0ustar gogousers IRC записник за #{{ kanal }} {% block sodrzhina %}{% endblock %} logger_src/urls.py0000644000175000001440000000074410613137260013421 0ustar gogousers# -*- coding: utf-8 -*- from django.conf.urls.defaults import * from logger.logviewer.views import * urlpatterns = patterns('', (r'^admin/', include('django.contrib.admin.urls')), ('^$', 'django.views.generic.simple.redirect_to', {'url': '/lugola/'}), (r'^(?P[a-z]+)/$',index), (r'^(?P[a-z]+)/(?P\d{4})/(?P\d{1,2})/(?P\w{1,2})/$',podatum), (r'^(?P\S+)/search/$',channel_search), (r'^(?P\S+)/arhiva/$',arhiva), )