kill version magic from setup.py, rename scripts to global names and move package data into the package
--- a/bin/index.cgi Sun Sep 13 18:57:48 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-#!/usr/bin/python2.5
-
-"""
- CGI mode using qmsk.web.cgi
-"""
-
-def error () :
- """
- Dumps out a raw traceback of the current exception to stdout, call from except.
-
- Used for low-level ImportError's
- """
-
- import sys
-
- # if this import fails, we're doomed
- from qmsk.irclogs import error
-
- # format info
- status, content_type, body = error.build_error()
-
- # HTTP headers+body
- sys.stdout.write('Status: %s\r\n' % status)
- sys.stdout.write('Content-type: %s\r\n' % content_type)
- sys.stdout.write('\r\n')
- sys.stdout.write(body)
-
-def main () :
- """
- Build our wsgi.Application and run
- """
-
- try :
- from qmsk.web import cgi_main
- from qmsk.irclogs import wsgi
-
- # create app
- app = wsgi.Application()
-
- # run once
- cgi_main.run(app)
-
- except :
- # display error on stdout
- error()
-
-if __name__ == '__main__' :
- main()
-
--- a/bin/index.fcgi Sun Sep 13 18:57:48 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
-#!/usr/bin/python2.5
-# :set filetype=py
-
-"""
- FastCGI mode using qmsk.web.fastcgi_main
-"""
-
-from qmsk.web import fastcgi_main
-
-# XXX: error handling for imports? Lighttp sucks hard at this
-from qmsk.irclogs import wsgi
-
-def main () :
- """
- Build our WSGIApplication and run
- """
-
- # create app
- app = wsgi.Application()
-
- # run once
- fastcgi_main.run(app)
-
-if __name__ == '__main__' :
- main()
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/qmsk-irclogs-search-index Sun Sep 13 20:08:16 2009 +0300
@@ -0,0 +1,640 @@
+#!/usr/bin/env python2.5
+
+"""
+ Tool for accessing the search index
+"""
+
+# XXX: fix path
+import sys; sys.path.insert(0, '.'); sys.path.insert(0, '..')
+
+import os, os.path, fcntl
+import datetime, pytz
+import optparse
+
+# configuration and the LogSearchIndex module
+from qmsk.irclogs import config, utils, log_search, channels
+
+def _open_index (options, open_mode) :
+ """
+ Opens the LogSearchIndex
+ """
+
+ return log_search.LogSearchIndex(config.LOG_CHANNELS, options.index_path, open_mode)
+
+
+def _open_index_and_channel (options, channel_name, open_mode) :
+ """
+ Opens+returns a LogSearchIndex and a LogChannel
+ """
+
+ # open the LogSearchIndex
+ index = _open_index(options, open_mode)
+
+ # open the channel
+ channel = config.LOG_CHANNELS.lookup(channel_name)
+
+ # return
+ return index, channel
+
+def _iter_insert_stats (index, channel, lines) :
+ """
+ Insert the given lines into the index.
+
+ Assumes the lines will be in time-order, and yields a series of (date, count) tuples for every date that lines
+ are inserted for
+ """
+
+ # last date
+ date = None
+
+ # count
+ count = 0
+
+ # iter lines
+ for line in lines :
+ # next day?
+ if not date or line.timestamp.date() != date :
+ if date :
+ # yield stats
+ yield date, count
+
+ # reset count
+ count = 0
+
+ # timestamp's date
+ date = line.timestamp.date()
+
+ # insert
+ index.insert_line(channel, line)
+
+ # count
+ count += 1
+
+ # final count?
+ if date and count :
+ yield date, count
+
+def _insert_lines (index, options, channel, lines) :
+ """
+ Insert the given lines into the index.
+
+ Assumes the lines will be in time-order, and prints out as status messages the date and count for the inserted lines
+ """
+
+ # iterate insert stats
+ for date, count in _iter_insert_stats(index, channel, lines) :
+ # output date header?
+ if not options.quiet :
+ print "%s: %s" % (date.strftime('%Y-%m-%d'), count),
+
+def _load_channel_date (index, options, channel, date) :
+ """
+ Loads the logs for the given date from the channel's LogSource into the given LogSearchIndex
+ """
+
+ if not options.quiet :
+ print "Loading date for channel %s" % channel.id
+
+ try :
+ # load lines for date
+ lines = channel.source.get_date(date)
+
+ except Exception, e :
+ if not options.skip_missing :
+ raise
+
+ if not options.quiet :
+ print "\tSkipped: %s" % (e, )
+
+ else :
+ # insert
+ _insert_lines(index, options, channel, lines)
+
+def _parse_date (options, date_str, tz=None, fmt='%Y-%m-%d') :
+ """
+ Parse the given datetime, using the given timezone(defaults to options.tz) and format
+ """
+
+ # default tz
+ if not tz :
+ tz = options.timezone
+
+ try :
+ # parse
+ return datetime.datetime.strptime(date_str, fmt).replace(tzinfo=tz)
+
+ except Exception, e :
+ raise CommandError("[ERROR] Invalid date: %s: %s" % (date_str, e))
+
+def _output_lines (options, lines) :
+ """
+ Display the formatted LogLines
+ """
+
+ # display as plaintext
+ for line, txt_data in options.formatter.format_txt(lines, full_timestamps=True) :
+ print txt_data
+
+class CommandError (Exception) :
+ """
+ Error with command-line arguments
+ """
+
+ pass
+
+def cmd_create (options) :
+ """
+ Creates a new index
+ """
+
+ # open index
+ index = _open_index(options, 'ctrunc' if options.force else 'c')
+
+ # that's all
+ pass
+
+def cmd_load (options, channel_name, *dates) :
+ """
+ Loads the logs for a specific channel for the given dates (in terms of the channe logs' timezone) into the index
+ """
+
+ # open index/channel
+ index, channel = _open_index_and_channel(options, channel_name, 'c' if options.create else 'a')
+
+ # handle each date
+ for date_str in dates :
+ # prase date
+ try :
+ date = _parse_date(options, date_str, channel.source.tz)
+
+ # handle errors
+ except CommandError, e :
+ if options.skip_missing :
+ print "[ERROR] %s" % (date_name, e)
+
+ else :
+ raise
+
+ # otherwise, load
+ else :
+ _load_channel_date(index, options, channel, date)
+
+def cmd_load_month (options, channel_name, *months) :
+ """
+ Loads the logs for a specific channel for the given months (in terms of the channel's timezone) into the index
+ """
+
+ # open index/channel
+ index, channel = _open_index_and_channel(options, channel_name, 'c' if options.create else 'a')
+
+ # handle each date
+ for month_str in months :
+ # prase date
+ try :
+ month = _parse_date(options, month_str, channel.source.tz, '%Y-%m')
+
+ # handle errors
+ except CommandError, e :
+ # skip?
+ if options.skip_missing :
+ if not options.quiet :
+ print "[ERROR] %s" % (date_name, e)
+ continue
+
+ else :
+ raise
+
+ # get the set of days
+ days = list(channel.source.get_month_days(month))
+
+ if not options.quiet :
+ print "Loading %d days of logs:" % (len(days))
+
+ # load each day
+ for date in days :
+ # convert to datetime
+ dt = datetime.datetime.combine(date, datetime.time(0)).replace(tzinfo=channel.source.tz)
+
+ # load
+ _load_channel_date(index, options, channel, dt)
+
+def cmd_search (options, channel_name, query) :
+ """
+ Search the index for events on a specific channel with the given query
+ """
+
+ # sanity-check
+ if options.create :
+ raise Exception("--create doesn't make sense for 'search'")
+
+ # open index/channel
+ index, channel = _open_index_and_channel(options, channel_name, 'r')
+
+ # search
+ lines = index.search_simple(channel, query)
+
+ # display
+ _output_lines(options, lines)
+
+def cmd_list (options, channel_name, *dates) :
+ """
+ List the indexed events for a specific date
+ """
+
+ # sanity-check
+ if options.create :
+ raise Exception("--create doesn't make sense for 'search'")
+
+ # open index/channel
+ index, channel = _open_index_and_channel(options, channel_name, 'r')
+
+ # ...for each date
+ for date_str in dates :
+ # parse date
+ date = _parse_date(options, date_str)
+
+ # list
+ lines = index.list(channel, date)
+
+ # display
+ _output_lines(options, lines)
+
+def _autoload_reset (options, channels) :
+ """
+ Reset old autoload state
+ """
+
+ # warn
+ if not options.quiet :
+ print "[WARN] Resetting autoload state for: %s" % ', '.join(channel.id for channel in channels)
+
+ # iter
+ for channel in channels :
+ # statefile path
+ statefile_path = os.path.join(options.autoload_state_path, 'chan-%s' % channel.id)
+
+ # is it present?
+ if not os.path.exists(statefile_path) :
+ if not options.quiet :
+ print "[WARN] No statefile found at %s" % statefile_path
+
+ else :
+ if not options.quiet :
+ print "\t%s: " % channel.id,
+
+ # remove the statefile
+ os.remove(statefile_path)
+
+ if not options.quiet :
+ print "OK"
+
+def cmd_autoload (options, *channel_names) :
+ """
+ Automatically loads all channel logs that have not been indexed yet (by logfile mtime)
+ """
+
+ # open index, nonblocking
+ index = _open_index(options, 'c?' if options.create else 'a?')
+
+ # default to all channels
+ if not channel_names :
+ channels = config.LOG_CHANNELS
+
+ else :
+ channels = [config.LOG_CHANNELS.lookup(channel_name) for channel_name in channel_names]
+
+ # reset autoload state?
+ if options.reset :
+ _autoload_reset(options, channels)
+ if not options.quiet :
+ print
+
+ # iterate channels
+ for channel in channels :
+ if not options.quiet :
+ print "Channel %s:" % channel.id
+
+ # no 'from' by default
+ after = None
+
+ # path to our state file
+ statefile_path = os.path.join(options.autoload_state_path, 'chan-%s' % channel.id)
+ statefile_tmppath = statefile_path + '.tmp'
+
+ # does it exist?
+ have_tmpfile = os.path.exists(statefile_tmppath)
+
+ # do we have a tempfile from a previous crash?
+ if have_tmpfile and not options.ignore_resume :
+ # first, open it...
+ statefile_tmp = open(statefile_tmppath, 'r+')
+
+ # ... then lock it
+ fcntl.lockf(statefile_tmp, fcntl.LOCK_EX | fcntl.LOCK_NB)
+
+ # read after timestamp
+ after_str = statefile_tmp.read().rstrip()
+
+ if after_str :
+ # parse timestamp
+ after = utils.from_utc_timestamp(int(after_str))
+
+ if not options.quiet :
+ print "\tContinuing earlier progress from %s" % after
+
+ else :
+ # ignore
+ if not options.quiet :
+ print "\t[WARN] Ignoring empty temporary statefile"
+
+ else :
+ # warn about old tmpfile that was ignored
+ if have_tmpfile and not options.quiet :
+ print "\t[WARN] Ignoring old tmpfile state"
+
+ # open new tempfile
+ statefile_tmp = open(statefile_tmppath, 'w')
+
+ # lock
+ fcntl.lockf(statefile_tmp, fcntl.LOCK_EX | fcntl.LOCK_NB)
+
+ # override?
+ if options.reload :
+ # load all
+ mtime = None
+
+ if not options.quiet :
+ print "\tForcing reload!"
+
+ # stat for mtime
+ else :
+ # stat for mtime, None if unknown
+ mtime = utils.mtime(statefile_path, ignore_missing=True)
+
+ if mtime and not options.quiet :
+ print "\tLast load time was %s" % mtime
+
+ elif not options.quiet :
+ print "\t[WARN] No previous load state! Loading full logs"
+
+ # only after some specific date?
+ if options.after :
+ # use unless read from tempfile
+ if not after :
+ after = options.after
+
+ if not options.quiet :
+ print "\tOnly including dates from %s onwards" % after
+
+ else :
+ if not options.quiet :
+ print "\t[WARN] Ignoring --from because we found a tempfile"
+
+ # only up to some specific date?
+ if options.until :
+ until = options.until
+
+ if not options.quiet :
+ print "\tOnly including dates up to (and including) %s" % until
+ else :
+ # default to now
+ until = None
+
+ # get lines
+ lines = channel.source.get_modified(mtime, after, until)
+
+ # insert
+ if not options.quiet :
+ print "\tLoading and inserting..."
+ print
+
+ # iterate insert() per day to display info and update progress
+ for date, count in _iter_insert_stats(index, channel, lines) :
+ # output date header?
+ if not options.quiet :
+ print "\t%10s: %d" % (date.strftime('%Y-%m-%d'), count)
+
+ # write temp state
+ statefile_tmp.seek(0)
+ statefile_tmp.write(str(utils.to_utc_timestamp(datetime.datetime.combine(date, datetime.time(0)))))
+ statefile_tmp.flush()
+
+ # write autoload state
+ open(statefile_path, 'w').close()
+
+ # close+delete tempfile
+ statefile_tmp.close()
+ os.remove(statefile_tmppath)
+
+ if not options.quiet :
+ print
+
+ # done
+ return
+
+def cmd_help (options, *args) :
+ """
+ Help about commands
+ """
+
+ import inspect
+
+ # general help stuff
+ options._parser.print_help()
+
+ # specific command?
+ if args :
+ # the command name
+ command, = args
+
+ # XXX: display info about specific command
+ xxx
+
+ # general
+ else :
+ print
+ print "Available commands:"
+
+ # build list of all cmd_* objects
+ cmd_objects = [(name, obj) for name, obj in globals().iteritems() if name.startswith('cmd_') and inspect.isfunction(obj)]
+
+ # sort alphabetically
+ cmd_objects.sort()
+
+ # iterate through all cmd_* objects
+ for cmd_func_name, cmd_func in cmd_objects :
+ # remove cmd_ prefix
+ cmd_name = cmd_func_name[4:]
+
+ # inspect
+ cmd_args, cmd_varargs, cmd_varkw, cmd_default = inspect.getargspec(cmd_func)
+ cmd_doc = inspect.getdoc(cmd_func)
+
+ # remove the "options" arg
+ cmd_args = cmd_args[1:]
+
+ # display
+ print "\t%10s %-30s : %s" % (cmd_name, inspect.formatargspec(cmd_args, cmd_varargs, None, cmd_default), cmd_doc)
+
+class MyOption (optparse.Option) :
+ """
+ Our custom types for optparse
+ """
+
+ def check_date (option, opt, value) :
+ """
+ Parse a date
+ """
+
+ try :
+ # parse
+ return datetime.datetime.strptime(value, '%Y-%m-%d')
+
+ # trap -> OptionValueError
+ except Exception, e :
+ raise optparse.OptionValueError("option %s: invalid date value: %r" % (opt, value))
+
+ def check_timezone (option, opt, value) :
+ """
+ Parse a timezone
+ """
+
+ try :
+ # parse
+ return pytz.timezone(value)
+
+ # trap -> OptionValueError
+ except Exception, e :
+ raise optparse.OptionValueError("option %s: invalid timezone: %r" % (opt, value))
+
+ def take_action (self, action, dest, opt, value, values, parser) :
+ """
+ Override take_action to handle date
+ """
+
+ if action == "parse_date" :
+ # get timezone
+ tz = values.timezone
+
+ # set timezone
+ value = value.replace(tzinfo=tz)
+
+ # store
+ return optparse.Option.take_action(self, 'store', dest, opt, value, values, parser)
+
+ else :
+ # default
+ return optparse.Option.take_action(self, action, dest, opt, value, values, parser)
+
+ TYPES = optparse.Option.TYPES + ('date', 'timezone')
+ TYPE_CHECKER = optparse.Option.TYPE_CHECKER.copy()
+ TYPE_CHECKER['date'] = check_date
+ TYPE_CHECKER['timezone'] = check_timezone
+ ACTIONS = optparse.Option.ACTIONS + ('parse_date', )
+ STORE_ACTIONS = optparse.Option.STORE_ACTIONS + ('parse_date', )
+ TYPED_ACTIONS = optparse.Option.TYPED_ACTIONS + ('parse_date', )
+ ACTIONS = optparse.Option.ACTIONS + ('parse_date', )
+
+def main (argv) :
+ """
+ Command-line main, with given argv
+ """
+
+ # define parser
+ parser = optparse.OptionParser(
+ usage = "%prog [options] <command> [ ... ]",
+ add_help_option = False,
+ option_class = MyOption,
+ )
+
+ # general options # # # #
+ general = optparse.OptionGroup(parser, "General Options")
+ general.add_option('-h', "--help", dest="help", help="Show this help message and exit",
+ action="store_true" )
+
+ general.add_option( "--formatter", dest="formatter_name", help="LogFormatter to use",
+ metavar="FMT", type="choice", default=config.PREF_FORMATTER_DEFAULT.name,
+ choices=[fmt_name for fmt_name in config.LOG_FORMATTERS.iterkeys()] )
+
+ general.add_option( "--index", dest="index_path", help="Index database path",
+ metavar="PATH", default=config.SEARCH_INDEX_PATH )
+
+ general.add_option( "--timezone", dest="timezone", help="Timezone for output",
+ metavar="TZ", type="timezone", default=pytz.utc )
+
+ general.add_option( "--force", dest="force", help="Force dangerous operation",
+ action="store_true" )
+
+ general.add_option( "--quiet", dest="quiet", help="Supress status messages",
+ action="store_true" )
+ parser.add_option_group(general)
+
+
+ # cmd_load options # # # #
+ load = optparse.OptionGroup(parser, "Load Options")
+ load.add_option( "--skip-missing", dest="skip_missing", help="Skip missing logfiles",
+ action="store_true" )
+
+ load.add_option( "--create", dest="create", help="Create index database",
+ action="store_true" )
+ parser.add_option_group(load)
+
+
+ # cmd_autoload options # # # #
+ autoload = optparse.OptionGroup(parser, "Autoload Options")
+ autoload.add_option( "--autoload-state", dest="autoload_state_path", help="Path to autoload state dir",
+ metavar="PATH", default=config.SEARCH_AUTOINDEX_PATH)
+
+ autoload.add_option( "--from", dest="after", help="Only autoload logfiles from the given date on",
+ metavar="DATE", type="date", action="parse_date", default=None )
+
+ autoload.add_option( "--until", dest="until", help="Only autoload logfiles up to (and including) the given date",
+ metavar="DATE", type="date", action="parse_date", default=None )
+
+ autoload.add_option( "--reload", dest="reload", help="Force reload lines",
+ action="store_true" )
+
+ autoload.add_option( "--reset", dest="reset", help="Reset old autload state",
+ action="store_true" )
+
+ autoload.add_option( "--ignore-resume", dest="ignore_resume", help="Do not try and resume interrupted autoload",
+ action="store_true" )
+ parser.add_option_group(autoload)
+
+ # parse
+ options, args = parser.parse_args(argv[1:])
+
+ # postprocess stuff
+ options._parser = parser
+ options.formatter = config.LOG_FORMATTERS[options.formatter_name](options.timezone, "%H:%M:%S", None, None)
+
+ # special-case --help
+ if options.help :
+ return cmd_help(options, *args)
+
+ # must have at least the command argument
+ if not args :
+ raise CommandError("Missing command")
+
+ # pop command
+ command = args.pop(0)
+
+ # get func
+ func = globals().get('cmd_%s' % command)
+
+ # unknown command?
+ if not func :
+ raise CommandError("Unknown command: %s" % command)
+
+ # call
+ func(options, *args)
+
+if __name__ == '__main__' :
+ try :
+ main(sys.argv)
+ sys.exit(0)
+
+ except CommandError, e :
+ print e
+ sys.exit(1)
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/qmsk-irclogs.cgi Sun Sep 13 20:08:16 2009 +0300
@@ -0,0 +1,49 @@
+#!/usr/bin/python2.5
+
+"""
+ CGI mode using qmsk.web.cgi
+"""
+
+def error () :
+ """
+ Dumps out a raw traceback of the current exception to stdout, call from except.
+
+ Used for low-level ImportError's
+ """
+
+ import sys
+
+ # if this import fails, we're doomed
+ from qmsk.irclogs import error
+
+ # format info
+ status, content_type, body = error.build_error()
+
+ # HTTP headers+body
+ sys.stdout.write('Status: %s\r\n' % status)
+ sys.stdout.write('Content-type: %s\r\n' % content_type)
+ sys.stdout.write('\r\n')
+ sys.stdout.write(body)
+
+def main () :
+ """
+ Build our wsgi.Application and run
+ """
+
+ try :
+ from qmsk.web import cgi_main
+ from qmsk.irclogs import wsgi
+
+ # create app
+ app = wsgi.Application()
+
+ # run once
+ cgi_main.run(app)
+
+ except :
+ # display error on stdout
+ error()
+
+if __name__ == '__main__' :
+ main()
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/qmsk-irclogs.fcgi Sun Sep 13 20:08:16 2009 +0300
@@ -0,0 +1,26 @@
+#!/usr/bin/python2.5
+# :set filetype=py
+
+"""
+ FastCGI mode using qmsk.web.fastcgi_main
+"""
+
+from qmsk.web import fastcgi_main
+
+# XXX: error handling for imports? Lighttp sucks hard at this
+from qmsk.irclogs import wsgi
+
+def main () :
+ """
+ Build our WSGIApplication and run
+ """
+
+ # create app
+ app = wsgi.Application()
+
+ # run once
+ fastcgi_main.run(app)
+
+if __name__ == '__main__' :
+ main()
+
--- a/bin/search-index Sun Sep 13 18:57:48 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,640 +0,0 @@
-#!/usr/bin/env python2.5
-
-"""
- Tool for accessing the search index
-"""
-
-# XXX: fix path
-import sys; sys.path.insert(0, '.'); sys.path.insert(0, '..')
-
-import os, os.path, fcntl
-import datetime, pytz
-import optparse
-
-# configuration and the LogSearchIndex module
-from qmsk.irclogs import config, utils, log_search, channels
-
-def _open_index (options, open_mode) :
- """
- Opens the LogSearchIndex
- """
-
- return log_search.LogSearchIndex(config.LOG_CHANNELS, options.index_path, open_mode)
-
-
-def _open_index_and_channel (options, channel_name, open_mode) :
- """
- Opens+returns a LogSearchIndex and a LogChannel
- """
-
- # open the LogSearchIndex
- index = _open_index(options, open_mode)
-
- # open the channel
- channel = config.LOG_CHANNELS.lookup(channel_name)
-
- # return
- return index, channel
-
-def _iter_insert_stats (index, channel, lines) :
- """
- Insert the given lines into the index.
-
- Assumes the lines will be in time-order, and yields a series of (date, count) tuples for every date that lines
- are inserted for
- """
-
- # last date
- date = None
-
- # count
- count = 0
-
- # iter lines
- for line in lines :
- # next day?
- if not date or line.timestamp.date() != date :
- if date :
- # yield stats
- yield date, count
-
- # reset count
- count = 0
-
- # timestamp's date
- date = line.timestamp.date()
-
- # insert
- index.insert_line(channel, line)
-
- # count
- count += 1
-
- # final count?
- if date and count :
- yield date, count
-
-def _insert_lines (index, options, channel, lines) :
- """
- Insert the given lines into the index.
-
- Assumes the lines will be in time-order, and prints out as status messages the date and count for the inserted lines
- """
-
- # iterate insert stats
- for date, count in _iter_insert_stats(index, channel, lines) :
- # output date header?
- if not options.quiet :
- print "%s: %s" % (date.strftime('%Y-%m-%d'), count),
-
-def _load_channel_date (index, options, channel, date) :
- """
- Loads the logs for the given date from the channel's LogSource into the given LogSearchIndex
- """
-
- if not options.quiet :
- print "Loading date for channel %s" % channel.id
-
- try :
- # load lines for date
- lines = channel.source.get_date(date)
-
- except Exception, e :
- if not options.skip_missing :
- raise
-
- if not options.quiet :
- print "\tSkipped: %s" % (e, )
-
- else :
- # insert
- _insert_lines(index, options, channel, lines)
-
-def _parse_date (options, date_str, tz=None, fmt='%Y-%m-%d') :
- """
- Parse the given datetime, using the given timezone(defaults to options.tz) and format
- """
-
- # default tz
- if not tz :
- tz = options.timezone
-
- try :
- # parse
- return datetime.datetime.strptime(date_str, fmt).replace(tzinfo=tz)
-
- except Exception, e :
- raise CommandError("[ERROR] Invalid date: %s: %s" % (date_str, e))
-
-def _output_lines (options, lines) :
- """
- Display the formatted LogLines
- """
-
- # display as plaintext
- for line, txt_data in options.formatter.format_txt(lines, full_timestamps=True) :
- print txt_data
-
-class CommandError (Exception) :
- """
- Error with command-line arguments
- """
-
- pass
-
-def cmd_create (options) :
- """
- Creates a new index
- """
-
- # open index
- index = _open_index(options, 'ctrunc' if options.force else 'c')
-
- # that's all
- pass
-
-def cmd_load (options, channel_name, *dates) :
- """
- Loads the logs for a specific channel for the given dates (in terms of the channe logs' timezone) into the index
- """
-
- # open index/channel
- index, channel = _open_index_and_channel(options, channel_name, 'c' if options.create else 'a')
-
- # handle each date
- for date_str in dates :
- # prase date
- try :
- date = _parse_date(options, date_str, channel.source.tz)
-
- # handle errors
- except CommandError, e :
- if options.skip_missing :
- print "[ERROR] %s" % (date_name, e)
-
- else :
- raise
-
- # otherwise, load
- else :
- _load_channel_date(index, options, channel, date)
-
-def cmd_load_month (options, channel_name, *months) :
- """
- Loads the logs for a specific channel for the given months (in terms of the channel's timezone) into the index
- """
-
- # open index/channel
- index, channel = _open_index_and_channel(options, channel_name, 'c' if options.create else 'a')
-
- # handle each date
- for month_str in months :
- # prase date
- try :
- month = _parse_date(options, month_str, channel.source.tz, '%Y-%m')
-
- # handle errors
- except CommandError, e :
- # skip?
- if options.skip_missing :
- if not options.quiet :
- print "[ERROR] %s" % (date_name, e)
- continue
-
- else :
- raise
-
- # get the set of days
- days = list(channel.source.get_month_days(month))
-
- if not options.quiet :
- print "Loading %d days of logs:" % (len(days))
-
- # load each day
- for date in days :
- # convert to datetime
- dt = datetime.datetime.combine(date, datetime.time(0)).replace(tzinfo=channel.source.tz)
-
- # load
- _load_channel_date(index, options, channel, dt)
-
-def cmd_search (options, channel_name, query) :
- """
- Search the index for events on a specific channel with the given query
- """
-
- # sanity-check
- if options.create :
- raise Exception("--create doesn't make sense for 'search'")
-
- # open index/channel
- index, channel = _open_index_and_channel(options, channel_name, 'r')
-
- # search
- lines = index.search_simple(channel, query)
-
- # display
- _output_lines(options, lines)
-
-def cmd_list (options, channel_name, *dates) :
- """
- List the indexed events for a specific date
- """
-
- # sanity-check
- if options.create :
- raise Exception("--create doesn't make sense for 'search'")
-
- # open index/channel
- index, channel = _open_index_and_channel(options, channel_name, 'r')
-
- # ...for each date
- for date_str in dates :
- # parse date
- date = _parse_date(options, date_str)
-
- # list
- lines = index.list(channel, date)
-
- # display
- _output_lines(options, lines)
-
-def _autoload_reset (options, channels) :
- """
- Reset old autoload state
- """
-
- # warn
- if not options.quiet :
- print "[WARN] Resetting autoload state for: %s" % ', '.join(channel.id for channel in channels)
-
- # iter
- for channel in channels :
- # statefile path
- statefile_path = os.path.join(options.autoload_state_path, 'chan-%s' % channel.id)
-
- # is it present?
- if not os.path.exists(statefile_path) :
- if not options.quiet :
- print "[WARN] No statefile found at %s" % statefile_path
-
- else :
- if not options.quiet :
- print "\t%s: " % channel.id,
-
- # remove the statefile
- os.remove(statefile_path)
-
- if not options.quiet :
- print "OK"
-
-def cmd_autoload (options, *channel_names) :
- """
- Automatically loads all channel logs that have not been indexed yet (by logfile mtime)
- """
-
- # open index, nonblocking
- index = _open_index(options, 'c?' if options.create else 'a?')
-
- # default to all channels
- if not channel_names :
- channels = config.LOG_CHANNELS
-
- else :
- channels = [config.LOG_CHANNELS.lookup(channel_name) for channel_name in channel_names]
-
- # reset autoload state?
- if options.reset :
- _autoload_reset(options, channels)
- if not options.quiet :
- print
-
- # iterate channels
- for channel in channels :
- if not options.quiet :
- print "Channel %s:" % channel.id
-
- # no 'from' by default
- after = None
-
- # path to our state file
- statefile_path = os.path.join(options.autoload_state_path, 'chan-%s' % channel.id)
- statefile_tmppath = statefile_path + '.tmp'
-
- # does it exist?
- have_tmpfile = os.path.exists(statefile_tmppath)
-
- # do we have a tempfile from a previous crash?
- if have_tmpfile and not options.ignore_resume :
- # first, open it...
- statefile_tmp = open(statefile_tmppath, 'r+')
-
- # ... then lock it
- fcntl.lockf(statefile_tmp, fcntl.LOCK_EX | fcntl.LOCK_NB)
-
- # read after timestamp
- after_str = statefile_tmp.read().rstrip()
-
- if after_str :
- # parse timestamp
- after = utils.from_utc_timestamp(int(after_str))
-
- if not options.quiet :
- print "\tContinuing earlier progress from %s" % after
-
- else :
- # ignore
- if not options.quiet :
- print "\t[WARN] Ignoring empty temporary statefile"
-
- else :
- # warn about old tmpfile that was ignored
- if have_tmpfile and not options.quiet :
- print "\t[WARN] Ignoring old tmpfile state"
-
- # open new tempfile
- statefile_tmp = open(statefile_tmppath, 'w')
-
- # lock
- fcntl.lockf(statefile_tmp, fcntl.LOCK_EX | fcntl.LOCK_NB)
-
- # override?
- if options.reload :
- # load all
- mtime = None
-
- if not options.quiet :
- print "\tForcing reload!"
-
- # stat for mtime
- else :
- # stat for mtime, None if unknown
- mtime = utils.mtime(statefile_path, ignore_missing=True)
-
- if mtime and not options.quiet :
- print "\tLast load time was %s" % mtime
-
- elif not options.quiet :
- print "\t[WARN] No previous load state! Loading full logs"
-
- # only after some specific date?
- if options.after :
- # use unless read from tempfile
- if not after :
- after = options.after
-
- if not options.quiet :
- print "\tOnly including dates from %s onwards" % after
-
- else :
- if not options.quiet :
- print "\t[WARN] Ignoring --from because we found a tempfile"
-
- # only up to some specific date?
- if options.until :
- until = options.until
-
- if not options.quiet :
- print "\tOnly including dates up to (and including) %s" % until
- else :
- # default to now
- until = None
-
- # get lines
- lines = channel.source.get_modified(mtime, after, until)
-
- # insert
- if not options.quiet :
- print "\tLoading and inserting..."
- print
-
- # iterate insert() per day to display info and update progress
- for date, count in _iter_insert_stats(index, channel, lines) :
- # output date header?
- if not options.quiet :
- print "\t%10s: %d" % (date.strftime('%Y-%m-%d'), count)
-
- # write temp state
- statefile_tmp.seek(0)
- statefile_tmp.write(str(utils.to_utc_timestamp(datetime.datetime.combine(date, datetime.time(0)))))
- statefile_tmp.flush()
-
- # write autoload state
- open(statefile_path, 'w').close()
-
- # close+delete tempfile
- statefile_tmp.close()
- os.remove(statefile_tmppath)
-
- if not options.quiet :
- print
-
- # done
- return
-
-def cmd_help (options, *args) :
- """
- Help about commands
- """
-
- import inspect
-
- # general help stuff
- options._parser.print_help()
-
- # specific command?
- if args :
- # the command name
- command, = args
-
- # XXX: display info about specific command
- xxx
-
- # general
- else :
- print
- print "Available commands:"
-
- # build list of all cmd_* objects
- cmd_objects = [(name, obj) for name, obj in globals().iteritems() if name.startswith('cmd_') and inspect.isfunction(obj)]
-
- # sort alphabetically
- cmd_objects.sort()
-
- # iterate through all cmd_* objects
- for cmd_func_name, cmd_func in cmd_objects :
- # remove cmd_ prefix
- cmd_name = cmd_func_name[4:]
-
- # inspect
- cmd_args, cmd_varargs, cmd_varkw, cmd_default = inspect.getargspec(cmd_func)
- cmd_doc = inspect.getdoc(cmd_func)
-
- # remove the "options" arg
- cmd_args = cmd_args[1:]
-
- # display
- print "\t%10s %-30s : %s" % (cmd_name, inspect.formatargspec(cmd_args, cmd_varargs, None, cmd_default), cmd_doc)
-
-class MyOption (optparse.Option) :
- """
- Our custom types for optparse
- """
-
- def check_date (option, opt, value) :
- """
- Parse a date
- """
-
- try :
- # parse
- return datetime.datetime.strptime(value, '%Y-%m-%d')
-
- # trap -> OptionValueError
- except Exception, e :
- raise optparse.OptionValueError("option %s: invalid date value: %r" % (opt, value))
-
- def check_timezone (option, opt, value) :
- """
- Parse a timezone
- """
-
- try :
- # parse
- return pytz.timezone(value)
-
- # trap -> OptionValueError
- except Exception, e :
- raise optparse.OptionValueError("option %s: invalid timezone: %r" % (opt, value))
-
- def take_action (self, action, dest, opt, value, values, parser) :
- """
- Override take_action to handle date
- """
-
- if action == "parse_date" :
- # get timezone
- tz = values.timezone
-
- # set timezone
- value = value.replace(tzinfo=tz)
-
- # store
- return optparse.Option.take_action(self, 'store', dest, opt, value, values, parser)
-
- else :
- # default
- return optparse.Option.take_action(self, action, dest, opt, value, values, parser)
-
- TYPES = optparse.Option.TYPES + ('date', 'timezone')
- TYPE_CHECKER = optparse.Option.TYPE_CHECKER.copy()
- TYPE_CHECKER['date'] = check_date
- TYPE_CHECKER['timezone'] = check_timezone
- ACTIONS = optparse.Option.ACTIONS + ('parse_date', )
- STORE_ACTIONS = optparse.Option.STORE_ACTIONS + ('parse_date', )
- TYPED_ACTIONS = optparse.Option.TYPED_ACTIONS + ('parse_date', )
- ACTIONS = optparse.Option.ACTIONS + ('parse_date', )
-
-def main (argv) :
- """
- Command-line main, with given argv
- """
-
- # define parser
- parser = optparse.OptionParser(
- usage = "%prog [options] <command> [ ... ]",
- add_help_option = False,
- option_class = MyOption,
- )
-
- # general options # # # #
- general = optparse.OptionGroup(parser, "General Options")
- general.add_option('-h', "--help", dest="help", help="Show this help message and exit",
- action="store_true" )
-
- general.add_option( "--formatter", dest="formatter_name", help="LogFormatter to use",
- metavar="FMT", type="choice", default=config.PREF_FORMATTER_DEFAULT.name,
- choices=[fmt_name for fmt_name in config.LOG_FORMATTERS.iterkeys()] )
-
- general.add_option( "--index", dest="index_path", help="Index database path",
- metavar="PATH", default=config.SEARCH_INDEX_PATH )
-
- general.add_option( "--timezone", dest="timezone", help="Timezone for output",
- metavar="TZ", type="timezone", default=pytz.utc )
-
- general.add_option( "--force", dest="force", help="Force dangerous operation",
- action="store_true" )
-
- general.add_option( "--quiet", dest="quiet", help="Supress status messages",
- action="store_true" )
- parser.add_option_group(general)
-
-
- # cmd_load options # # # #
- load = optparse.OptionGroup(parser, "Load Options")
- load.add_option( "--skip-missing", dest="skip_missing", help="Skip missing logfiles",
- action="store_true" )
-
- load.add_option( "--create", dest="create", help="Create index database",
- action="store_true" )
- parser.add_option_group(load)
-
-
- # cmd_autoload options # # # #
- autoload = optparse.OptionGroup(parser, "Autoload Options")
- autoload.add_option( "--autoload-state", dest="autoload_state_path", help="Path to autoload state dir",
- metavar="PATH", default=config.SEARCH_AUTOINDEX_PATH)
-
- autoload.add_option( "--from", dest="after", help="Only autoload logfiles from the given date on",
- metavar="DATE", type="date", action="parse_date", default=None )
-
- autoload.add_option( "--until", dest="until", help="Only autoload logfiles up to (and including) the given date",
- metavar="DATE", type="date", action="parse_date", default=None )
-
- autoload.add_option( "--reload", dest="reload", help="Force reload lines",
- action="store_true" )
-
- autoload.add_option( "--reset", dest="reset", help="Reset old autload state",
- action="store_true" )
-
- autoload.add_option( "--ignore-resume", dest="ignore_resume", help="Do not try and resume interrupted autoload",
- action="store_true" )
- parser.add_option_group(autoload)
-
- # parse
- options, args = parser.parse_args(argv[1:])
-
- # postprocess stuff
- options._parser = parser
- options.formatter = config.LOG_FORMATTERS[options.formatter_name](options.timezone, "%H:%M:%S", None, None)
-
- # special-case --help
- if options.help :
- return cmd_help(options, *args)
-
- # must have at least the command argument
- if not args :
- raise CommandError("Missing command")
-
- # pop command
- command = args.pop(0)
-
- # get func
- func = globals().get('cmd_%s' % command)
-
- # unknown command?
- if not func :
- raise CommandError("Unknown command: %s" % command)
-
- # call
- func(options, *args)
-
-if __name__ == '__main__' :
- try :
- main(sys.argv)
- sys.exit(0)
-
- except CommandError, e :
- print e
- sys.exit(1)
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmsk/irclogs/static/irclogs.css Sun Sep 13 20:08:16 2009 +0300
@@ -0,0 +1,393 @@
+/*
+ * Global styles
+ */
+body {
+ padding: 0px;
+ margin: 0px;
+
+ background-color: #ffffff;
+ color: #000000;
+}
+
+a {
+ color: #454545;
+}
+
+/*
+ * Menu
+ */
+#menu {
+ padding: 0px;
+ margin: 0px;
+}
+
+#menu ul {
+ display: table;
+ list-style-type: none;
+
+ width: 100%;
+
+ padding: 0px;
+ margin: 0px;
+
+ background-color: #f0f0f0;
+ border-bottom: 1px dashed #a5a5a5;
+
+ text-align: center;
+ white-space: nowrap;
+}
+
+#menu li {
+ display: table-cell;
+
+ height: 1.5em;
+
+ padding: 0px;
+ margin: 0px;
+
+ border-left: 1px solid #b0b0b0;
+
+ font-weight: bold;
+}
+
+#menu li.join-left {
+ border-left: none;
+}
+
+#menu li a,
+#menu li form {
+ height: 1.5em;
+}
+
+#menu li:first-child {
+ border-left: none;
+}
+
+#menu li a {
+ display: block;
+
+ padding: 6px;
+ margin: 0px;
+
+ height: 100%;
+
+ color: #494949;
+ text-decoration: none;
+}
+
+#menu li a:hover {
+ background-color: #d0d0d0;
+ text-decoration: none;
+}
+
+#menu form {
+ display: block;
+
+ padding: 0px;
+ margin: 0px;
+}
+
+#menu form input[type=submit] {
+ padding: 9px;
+ margin: 0px;
+
+ border: none;
+ background-color: inherit;
+
+ cursor: pointer;
+}
+
+#menu form input[type=submit]:hover {
+ background-color: #d0d0d0;
+}
+
+/*
+ * Content
+ */
+div#content {
+ padding: 25px;
+}
+
+/*
+ * Footer
+ */
+div#footer {
+ padding: 10px;
+
+ border-top: 1px dashed #a5a5a5;
+
+ font-size: x-small;
+ font-style: italic;
+}
+
+div#footer-left {
+ float: left;
+}
+
+div#footer-right {
+ float: right;
+ text-align: right;
+}
+
+div#footer-center {
+ text-align: center;
+}
+
+/*
+ * General
+ */
+/* Channel view */
+div#title {
+ text-align: center;
+ font-size: x-large;
+ font-weight: bold;
+
+ margin-bottom: 20px;
+}
+
+/* Calendar */
+div.calendar-list {
+ text-align: center;
+}
+
+table.calendar {
+ display: inline-table;
+}
+
+table.calendar th {
+ background-color: #c8c8c8;
+}
+
+/* month header */
+table.calendar tr.month-header th {
+
+}
+
+table.calendar tr.month-header form {
+ padding: 5px;
+}
+
+table.calendar tr.month-header a,
+table.calendar span.prev-month,
+table.calendar span.next-month {
+ display: block;
+
+ color: inherit;
+ text-decoration: none;
+
+ padding: 3px;
+ padding-left: 8px;
+ padding-right: 8px;
+
+ font-size: x-large;
+}
+
+table.calendar th.this-month a.next-month,
+table.calendar span.prev-month,
+table.calendar span.next-month {
+ color: #d5d5d5;
+}
+
+table.calendar tr.month-header a:hover {
+ background-color: #b5b5b5;
+}
+
+table.calendar tr.month-header span {
+ margin-top: 5px;
+}
+
+table.calendar .prev-month {
+ float: left;
+}
+
+table.calendar .next-month {
+ float: right;
+}
+
+/* week header */
+table.calendar tr.week-header th {
+ width: 14%
+}
+
+/* cells */
+table.calendar td {
+ padding: 2px;
+ margin: 1px;
+ text-align: center;
+}
+
+table.calendar td a {
+ display: block;
+ padding: 2px;
+
+ background-color: #e0e0e0;
+ text-decoration: none;
+
+ color: inherit;
+}
+
+table.calendar td.empty {
+ color: #d0d0d0;
+}
+
+table.calendar td a:hover {
+ background-color: #d0d0d0;
+}
+
+table.calendar td#today {
+ font-weight: bold;
+}
+
+/* Preferences form */
+fieldset {
+ background-color: #e8e8e8;
+
+ margin-bottom: 1em;
+}
+
+legend {
+ padding: 0.2em 0.5em;
+
+ background-color: inherit;
+ border: inherit;
+}
+
+label {
+ display: block;
+ float: left;
+
+ width: 8em;
+ margin-right: 0.5em;
+
+ text-align: right;
+}
+
+fieldset input,
+fieldset select {
+ width: 15em;
+}
+
+fieldset input[type=submit] {
+ width: 8em;
+}
+
+fieldset input.wide {
+ width: 30em;
+}
+
+fieldset span.example {
+ font-size: x-small;
+
+ margin-left: 1em;
+}
+
+/* Search form */
+div#search-form {
+ padding: 25px;
+
+ text-align: center;
+}
+
+/*
+div#search input[type=text] {
+ display: block;
+
+ width: 40%;
+ margin: 0px auto 5px auto;
+}
+
+div#search div.indent {
+ margin-left: 8.5em;
+}
+*/
+
+div#search-help {
+
+}
+
+div#search-error {
+ margin: 50px;
+
+ text-align: center;
+ font-size: x-large;
+ font-style: italic;
+}
+
+/* Log lines */
+a.more-link {
+ color: black;
+ text-decoration: none;
+}
+
+a.more-link:hover {
+ text-decoration: underline;
+}
+
+div#lines a {
+ color: black;
+}
+
+div#lines a[id] {
+ color: #ffffff;
+ text-decoration: none;
+}
+
+div#lines a[id]:hover,
+div#lines a[id]:target {
+ color: #000000;
+}
+
+/* Pagination */
+div.paginate {
+ height: 50px;
+
+ text-align: center;
+}
+
+div.paginate ul {
+ margin: 0px;
+ padding: 0px;
+
+ line-height: 30px;
+
+ border: 1px solid #aaa;
+
+ white-space: nowrap;
+}
+
+div.paginate li {
+ list-style-type: none;
+ display: inline;
+}
+
+div.paginate li * {
+ padding: 7px 10px;
+}
+
+div.paginate li a {
+ color: black;
+ text-decoration: none;
+}
+
+div.paginate li span {
+ color: #d5d5d5;
+}
+
+div.paginate li a:hover {
+ background-color: #d0d0d0;
+}
+
+li.paginate-left {
+ float: left;
+}
+
+li.paginate-right {
+ float: right;
+}
+
+/* Lastlog */
+div#other-formats {
+ font-size: small;
+ text-align: center;
+ font-style: italic;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmsk/irclogs/templates/channel.tmpl Sun Sep 13 20:08:16 2009 +0300
@@ -0,0 +1,39 @@
+<%inherit file="layout.tmpl" />
+
+<%def name="menu()">
+<ul>
+ <li><a href="${urls.index.build(req)}">Home</a></li>
+ <li><a href="${urls.preferences.build(req)}">Preferences</a></li>
+
+ <li>
+ <a href="${urls.channel.build(req, channel=channel)}">Channel:</a>
+ </li><li class="join-left">
+ <form action="${urls.channel_select.build(req)}" method="GET">
+ <select name="channel">
+ ${h.select_options(((ch.id, ch.title) for ch in channel_list), channel.id)}
+ </select><input type="submit" value="Go »" />
+ </form>
+ </li>
+
+ <li><a href="${urls.channel_calendar.build(req, channel=channel)}">Browse by Date</a></li>
+
+ <li>
+ <a href="${h.build_url(urls.channel_search, channel=channel)}">Search:</a>
+ </li><li class="join-left">
+ <form action="${urls.channel_search.build(req, channel=channel)}" method="GET">
+ <input type="hidden" name="count" value="100" />
+ <input type="text" name="q" value="${search_query if search_query else ''}" />
+ <input type="submit" value="Go »" />
+ </form>
+ </li>
+<!--
+ <li><a href="${h.build_url(urls.channel_last, channel=channel, count=count or None, type='rss')}">[RSS]</a></li>
+-->
+</ul>
+</%def>
+
+${next.body()}
+
+<%def name="footer_right()">
+ All times are in <a href="${h.build_url(urls.preferences)}">${h.tz_name(prefs['timezone'])}</a>
+</%def>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmsk/irclogs/templates/channel_calendar.tmpl Sun Sep 13 20:08:16 2009 +0300
@@ -0,0 +1,69 @@
+<%inherit file="channel.tmpl" />
+
+<%def name="month_table(month, is_center=True)">
+## the set of available days
+<% log_dates = h.set(channel.source.get_month_days(month)) %>
+## the calendar table
+<table class="calendar">
+## table header - month name
+ <tr class="month-header">
+ <th colspan="7"${' class="this-month"' if h.is_this_month(month) else ''}>
+ % if is_center :
+ <a href="${urls.channel_calendar.build(req, channel=channel, year=h.next_month(month).year, month=h.next_month(month).month)}" class="next-month" title="Next month">»</a>
+ <a href="${urls.channel_calendar.build(req, channel=channel, year=h.prev_month(month).year, month=h.prev_month(month).month)}" class="prev-month" title="Previous month">«</a>
+
+ <form action="${urls.channel_calendar.build(req, channel=channel)}" method="GET">
+ <select name="month">
+ ${h.select_options(((month_num, name) for month_num, name in h.months), month.month)}
+ </select>
+ <select name="year">
+ ${h.select_options(((None, 2006), (None, 2007), (None, 2008), (None, 2009)), month.year)}
+ </select>
+ <input type="submit" value="Go »" />
+ </form>
+ % else :
+ <span id="month-name">${h.fmt_month(month)}</span>
+ % endif
+ </th>
+ </tr>
+## month header - weekday names
+ <tr class="week-header">
+ % for weekday in h.calendar.iterweekdays() :
+ <th>${h.fmt_weekday(weekday)}</th>
+ % endfor
+ </tr>
+## iterate over the weeks
+% for week in h.calendar.monthdays2calendar(month.year, month.month) :
+ <tr>
+ ## iterate over the week's days
+ % for day, weekday in week :
+ ## is it an empty cell?
+ % if not day :
+ <td> </td>
+ % else :
+ ## build date
+ <% date = h.build_date(month, day) %>\
+ ## render cell
+ <td${' id="today"' if h.is_today(date) else ''}${' class="empty"' if date.date() not in log_dates else ''}>\
+ ## link to logs for this day?
+ % if date.date() in log_dates :
+<a href="${urls.channel_date.build(req, channel=channel, date=date)}">${day}</a>\
+ % else :
+${day}\
+ % endif
+</td>
+ % endif
+ % endfor
+ </tr>
+% endfor
+</table>
+</%def>
+
+<div id="title">${channel.title} :: Calendar for ${h.fmt_month(month)}</div>
+
+<div class="calendar-list">
+## three months
+${month_table(h.prev_month(month), is_center=False )}
+${month_table(month, is_center=True )}
+${month_table(h.next_month(month), is_center=False )}
+</div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmsk/irclogs/templates/channel_date.tmpl Sun Sep 13 20:08:16 2009 +0300
@@ -0,0 +1,29 @@
+<%inherit file="channel.tmpl" />
+<%namespace file="inc_paginate.tmpl" name="paginate" />
+
+## special pagination stuff
+<%def name="paginate_date()">
+ <%call expr="paginate.paginate(urls.channel_date, count, page, max, channel=channel, date=date)">
+ <%def name="left()">
+ % if date_prev :
+ <a href="${h.build_url(urls.channel_date, channel=channel, date=date_prev)}">« ${h.fmt_date(date_prev)}</a>
+ % endif
+ </%def>
+ <%def name="right()">
+ % if date_next :
+ <a href="${h.build_url(urls.channel_date, channel=channel, date=date_next)}">${h.fmt_date(date_next)} »</a>
+ % endif
+ </%def>
+ </%call>
+</%def>
+
+<div id="title">${channel.title} :: Logs for ${h.fmt_date(date)}</div>
+
+% if page :
+${paginate_date()}
+% endif
+<%include file="lines.tmpl" />
+% if page :
+${paginate_date()}
+% endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmsk/irclogs/templates/channel_last.tmpl Sun Sep 13 20:08:16 2009 +0300
@@ -0,0 +1,18 @@
+<%inherit file="channel.tmpl" />
+
+<div id="title">${channel.title} :: Last ${count} lines</div>
+
+<form action="${h.url(urls.channel_last, channel=channel)}" method="GET">
+ View last <select name="count">
+ ${h.select_options(((None, cc) for cc in (10, 20, 50, 100, 200, 500, 1000)), count)}
+ </select> lines: <input type="submit" value="Go »" />
+</form>
+
+<%include file="lines.tmpl" />
+
+<div id="other-formats">
+ Other formats available:<br/>
+ <a href="${h.url(urls.channel_last, channel=channel, count=count, type='txt')}">Plaintext</a>
+ | <a href="${h.url(urls.channel_last, channel=channel, count=count, type='png')}">PNG</a>
+ | <a href="${h.url(urls.channel_last, channel=channel, count=count, type='rss')}">RSS</a>
+</div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmsk/irclogs/templates/channel_search.tmpl Sun Sep 13 20:08:16 2009 +0300
@@ -0,0 +1,62 @@
+<%inherit file="channel.tmpl" />
+<%namespace file="inc_paginate.tmpl" import="paginate" />
+
+% if not show_results :
+<div id="title">${channel.title} :: Search</div>
+
+<div id="search">
+ <form action="${h.build_url(urls.channel_search, channel=channel)}" method="GET">
+ <fieldset>
+ <p>
+ <label for="q">Message:</label>
+ <input type="text" name="q" class="wide" />
+ </p>
+
+ <p>
+ <label for="nick">By nickname:</label>
+ <input type="text" name="nick" />
+ <span>(optional)</span>
+ </p>
+
+ <p>
+ <label for="count">Results/page:</label>
+ <select name="count">
+ ${h.select_options(((cc, cc_label) for cc, cc_label in config.SEARCH_LINE_COUNT_OPTIONS), count)}
+ </select>
+ </p>
+
+ <input type="submit" value="Search" />
+ </fieldset>
+ </form>
+
+ <div id="search-help">
+ <p>Search powered by <a href="http://hyperestraier.sourceforge.net/">Hyper Estraier</a>:</p>
+
+ <ul>
+ <li>Group words together using quotes: <tt>"united nations"</tt></li>
+ <li>Searching for multiple words is AND: <tt>internet security</tt></li>
+ <li>To exclude terms, use <strong>!</strong> : <tt>hacker ! cracker</tt></li>
+ <li>Union (i.e. <q>or</q>) using <strong>|</strong> : <tt>proxy | firewall</tt></li>
+ <li>Search is case-insensitive</li>
+ </ul>
+ </div>
+</div>
+
+% else :
+<div id="title">${channel.title} :: Results\
+% if search_query :
+ With '${search_query}'\
+% endif
+% if search_nick :
+ By ${search_nick}\
+% endif
+</div>
+
+${paginate(urls.channel_search, count, page, max, channel=channel, q=search_query, t=search_targets, _more=True, _last=not(bool(lines)))}
+% if lines :
+<%include file="lines.tmpl" />
+% else :
+<div id="search-error">No results found</div>
+% endif
+${paginate(urls.channel_search, count, page, max, channel=channel, q=search_query, t=search_targets, _more=True, _last=not(bool(lines)))}
+% endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmsk/irclogs/templates/inc_paginate.tmpl Sun Sep 13 20:08:16 2009 +0300
@@ -0,0 +1,60 @@
+## special overrides...
+<%def name="paginate_left()">
+
+</%def>
+
+<%def name="paginate(url, count, page_cur, page_max, _more=None, _last=False, **args)">
+ <%doc>
+ Pagination works using page numbers, with a specific number of maximum pages displayed. If _more is True,
+ then instead of a "Next" button, we have a "More" button, which goes to the max+1'th page, unless _last is
+ True, whereupon it's not displayed.
+
+ Can be called using <%call>, whereupon caller.left/caller.right can define additional data to display at the
+ left/right of the pagination ul.
+ </%doc>
+ <div class="paginate">
+ <ul>
+ % if caller and caller.right :
+ <li class="paginate-right">
+ ${caller.right()}
+ </li>
+ % endif
+ % if caller and caller.left :
+ <li class="paginate-left">
+ ${caller.left()}
+ </li>
+ % endif
+ <li>
+ % if page_cur > 1 :
+ <a href="${h.build_url(url, count=count, page=page_cur-1, max=max, **args)}">« Prev</a>
+ % else :
+ <span>« Prev</span>
+ %endif
+ </li>
+ % for page in xrange(1, page_max + 1) :
+ <li>
+ % if page == page_cur :
+ <strong>${page}</strong>
+ % else :
+ <a href="${h.build_url(url, count=count, page=page, max=page_max, **args)}">${page}</a>
+ % endif
+ </li>
+ % endfor
+ % if _more and not _last :
+ <li>…</li>
+ % endif
+ <li>
+ % if _more and _last :
+ <span>More »</span>
+ % elif _more :
+ <a href="${h.build_url(url, count=count, page=page_max+1, **args)}">More »</a>
+ % elif page_cur == page_max : ## last page
+ <span>Next »</span>
+ % else :
+ <a href="${h.build_url(url, count=count, page=page_cur+1, **args)}">Next »</a>
+ % endif
+ </li>
+ </ul>
+ </div>
+</%def>
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmsk/irclogs/templates/index.tmpl Sun Sep 13 20:08:16 2009 +0300
@@ -0,0 +1,10 @@
+<%inherit file="layout.tmpl" />
+
+<div id="title">Available Channels</div>
+
+<ul>
+% for channel in channel_list :
+ <li><a href="${urls.channel.build(req, channel=channel)}">${channel.title}</a></li>
+% endfor
+</ul>
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmsk/irclogs/templates/layout.tmpl Sun Sep 13 20:08:16 2009 +0300
@@ -0,0 +1,84 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<%def name="menu()">
+<ul>
+ <li><a href="${urls.index.build(req)}">Home</a></li>
+ <li><a href="${urls.preferences.build(req)}">Preferences</a></li>
+
+ <li>
+ <span>Channel:</span>
+ </li><li class="join-left">
+ <form action="${urls.channel_select.build(req)}" method="GET">
+ <select name="channel">
+ ${h.select_options(((ch.id, ch.title) for ch in channel_list), channel.id if channel else None)}
+ </select><input type="submit" value="Go »" />
+ </form>
+ </li>
+</ul>
+</%def>
+
+<%def name="footer_right()">
+
+</%def>
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+ <head>
+ <title>irclogs.qmsk.net${(' :: ' + channel.title) if channel else ''}</title>
+ <link rel="Stylesheet" type="text/css" href="${req.site_root}/static/irclogs.css" />
+ ## timezone-autodetect
+ % if prefs and prefs.is_default('timezone') :
+ <script language="Javascript" type="text/javascript">
+/*
+ * Set a preference cookie
+ */
+function set_pref (name, value) {
+ // XXX: expire?
+ document.cookie = (name + "=" + value + "; path=/");
+}
+
+/*
+ * Set the timezone_offset cookie to the current Date's timezone offset
+ */
+function autodetect_tz_offset () {
+ // current datetime
+ var now = new Date();
+
+ // timezone offset from UTC in minutes
+ var timezone_offset = -now.getTimezoneOffset()
+
+ // store cookie with offset in minutes
+ set_pref('timezone_offset', timezone_offset);
+}
+
+/*
+ * Autodetect at load
+ */
+window.onload = autodetect_tz_offset;
+ </script>
+ % endif
+ </head>
+ <body>
+ <div id="menu">
+ ${next.menu()}
+ </div>
+
+ <div id="content">
+ ${next.body()}
+ </div>
+
+ <div id="footer">
+ <div id="footer-right">
+ ${next.footer_right()}
+ </div>
+
+ <div id="footer-left">
+ <a href="http://projects.qmsk.net/irclogs2">irclogs2</a> version ${h.version_link()}
+ </div>
+
+ <div id="footer-center">
+ ${h.validation_notice(req.site_host)}
+ </div>
+ </div>
+ </body>
+</html>
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmsk/irclogs/templates/lines.tmpl Sun Sep 13 20:08:16 2009 +0300
@@ -0,0 +1,13 @@
+<% formatter = prefs['formatter'] %>
+
+<div id="lines">
+% if formatter.html_fixedwidth :
+<pre>
+% endif
+% for line, html in lines:
+<a href="${h.build_url(urls.channel_link, channel=channel, timestamp=line.timestamp)}#${h.utc_timestamp(line.timestamp)}" id="${h.utc_timestamp(line.timestamp)}">»» </a>${html}
+% endfor
+% if formatter.html_fixedwidth :
+</pre>
+% endif
+</div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmsk/irclogs/templates/preferences.tmpl Sun Sep 13 20:08:16 2009 +0300
@@ -0,0 +1,67 @@
+<%inherit file="layout.tmpl" />
+
+<div id="title">Edit Preferences</div>
+
+<form action="${urls.preferences.build(req)}" method="POST">
+ <fieldset>
+ <legend>Dates / Times</legend>
+
+ <p>
+ <label for="timezone">Timezone:</label>
+ <select name="timezone">
+ ${h.select_options(prefs.pref('timezone').OPTIONS, prefs.build('timezone'))}
+ </select>
+ <span class="example">(${h.tz_name(prefs['timezone'])})</span>
+ </p>
+
+ <p>
+ <label for="date_format">Date format:</label>
+ <input type="text" name="date_format" value="${prefs['date_format']}" />
+ <span class="example">(${h.fmt_date()})</span>
+ </p>
+
+ <p>
+ <label for="time_format">Time format:</label>
+ <input type="text" name="time_format" value="${prefs['time_format']}" />
+ <span class="example">(${h.fmt_time()})</span>
+ </p>
+
+ </fieldset>
+
+ <fieldset>
+ <legend>Log Output</legend>
+
+ <p>
+ <label for="formatter">Formatter:</label>
+ <select name="formatter">
+ ${h.select_options(((fmt_name, fmt.title) for fmt_name, fmt in preferences.formatter.formatters.iteritems()), prefs['formatter'].name)}
+ </select>
+ </p>
+
+ <p>
+ <label for="count">Lines / Page:</label>
+ <input type="text" name="count" value="${prefs['count']}" />
+<!-- <span class="example">(Blank for infinite)</span> -->
+ </p>
+ </fieldset>
+
+ <fieldset>
+ <legend>Image Options</legend>
+
+ <p>
+ <label for="image_font">Font:</label>
+ <select name="image_font">
+ ${h.select_options(((name, title) for name, (path, title) in config.FORMATTER_IMAGE_FONTS.iteritems()), prefs['image_font'][0])}
+ </select>
+ </p>
+
+ <p>
+ <label for="image_font_size">Font size:</label>
+ <input type="text" name="image_font_size" value="${prefs['image_font_size']}" />
+ <span class="example">pt</span>
+ </p>
+ </fieldset>
+
+ <input type="submit" value="Save" />
+</form>
+
--- a/setup.py Sun Sep 13 18:57:48 2009 +0300
+++ b/setup.py Sun Sep 13 20:08:16 2009 +0300
@@ -2,16 +2,16 @@
from distutils.core import setup
-from qmsk.irclogs.version import version_string
-
# version magic...
-import os.path
-version = version_string(os.path.dirname(__file__))
+#from qmsk.irclogs.version import version_string
+#import os.path
+#version = version_string(os.path.dirname(__file__))
setup(
name = "qmsk.irclogs",
- version = version,
+ # version = version,
+ version = '0.2.0-rc1',
description = "Irclogs2",
author = "Tero Marttila",
author_email= "terom@fixme.fi",
@@ -22,9 +22,13 @@
],
scripts = [
- 'bin/index.cgi',
- 'bin/index.fcgi',
- 'bin/search-index'
+ 'bin/qmsk-irclogs.cgi',
+ 'bin/qmsk-irclogs.fcgi',
+ 'bin/qmsk-irclogs-search-index'
],
+
+ package_data = {
+ 'qmsk.irclogs': ['templates/*.tmpl', 'static/*'],
+ },
)
--- a/static/irclogs.css Sun Sep 13 18:57:48 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,393 +0,0 @@
-/*
- * Global styles
- */
-body {
- padding: 0px;
- margin: 0px;
-
- background-color: #ffffff;
- color: #000000;
-}
-
-a {
- color: #454545;
-}
-
-/*
- * Menu
- */
-#menu {
- padding: 0px;
- margin: 0px;
-}
-
-#menu ul {
- display: table;
- list-style-type: none;
-
- width: 100%;
-
- padding: 0px;
- margin: 0px;
-
- background-color: #f0f0f0;
- border-bottom: 1px dashed #a5a5a5;
-
- text-align: center;
- white-space: nowrap;
-}
-
-#menu li {
- display: table-cell;
-
- height: 1.5em;
-
- padding: 0px;
- margin: 0px;
-
- border-left: 1px solid #b0b0b0;
-
- font-weight: bold;
-}
-
-#menu li.join-left {
- border-left: none;
-}
-
-#menu li a,
-#menu li form {
- height: 1.5em;
-}
-
-#menu li:first-child {
- border-left: none;
-}
-
-#menu li a {
- display: block;
-
- padding: 6px;
- margin: 0px;
-
- height: 100%;
-
- color: #494949;
- text-decoration: none;
-}
-
-#menu li a:hover {
- background-color: #d0d0d0;
- text-decoration: none;
-}
-
-#menu form {
- display: block;
-
- padding: 0px;
- margin: 0px;
-}
-
-#menu form input[type=submit] {
- padding: 9px;
- margin: 0px;
-
- border: none;
- background-color: inherit;
-
- cursor: pointer;
-}
-
-#menu form input[type=submit]:hover {
- background-color: #d0d0d0;
-}
-
-/*
- * Content
- */
-div#content {
- padding: 25px;
-}
-
-/*
- * Footer
- */
-div#footer {
- padding: 10px;
-
- border-top: 1px dashed #a5a5a5;
-
- font-size: x-small;
- font-style: italic;
-}
-
-div#footer-left {
- float: left;
-}
-
-div#footer-right {
- float: right;
- text-align: right;
-}
-
-div#footer-center {
- text-align: center;
-}
-
-/*
- * General
- */
-/* Channel view */
-div#title {
- text-align: center;
- font-size: x-large;
- font-weight: bold;
-
- margin-bottom: 20px;
-}
-
-/* Calendar */
-div.calendar-list {
- text-align: center;
-}
-
-table.calendar {
- display: inline-table;
-}
-
-table.calendar th {
- background-color: #c8c8c8;
-}
-
-/* month header */
-table.calendar tr.month-header th {
-
-}
-
-table.calendar tr.month-header form {
- padding: 5px;
-}
-
-table.calendar tr.month-header a,
-table.calendar span.prev-month,
-table.calendar span.next-month {
- display: block;
-
- color: inherit;
- text-decoration: none;
-
- padding: 3px;
- padding-left: 8px;
- padding-right: 8px;
-
- font-size: x-large;
-}
-
-table.calendar th.this-month a.next-month,
-table.calendar span.prev-month,
-table.calendar span.next-month {
- color: #d5d5d5;
-}
-
-table.calendar tr.month-header a:hover {
- background-color: #b5b5b5;
-}
-
-table.calendar tr.month-header span {
- margin-top: 5px;
-}
-
-table.calendar .prev-month {
- float: left;
-}
-
-table.calendar .next-month {
- float: right;
-}
-
-/* week header */
-table.calendar tr.week-header th {
- width: 14%
-}
-
-/* cells */
-table.calendar td {
- padding: 2px;
- margin: 1px;
- text-align: center;
-}
-
-table.calendar td a {
- display: block;
- padding: 2px;
-
- background-color: #e0e0e0;
- text-decoration: none;
-
- color: inherit;
-}
-
-table.calendar td.empty {
- color: #d0d0d0;
-}
-
-table.calendar td a:hover {
- background-color: #d0d0d0;
-}
-
-table.calendar td#today {
- font-weight: bold;
-}
-
-/* Preferences form */
-fieldset {
- background-color: #e8e8e8;
-
- margin-bottom: 1em;
-}
-
-legend {
- padding: 0.2em 0.5em;
-
- background-color: inherit;
- border: inherit;
-}
-
-label {
- display: block;
- float: left;
-
- width: 8em;
- margin-right: 0.5em;
-
- text-align: right;
-}
-
-fieldset input,
-fieldset select {
- width: 15em;
-}
-
-fieldset input[type=submit] {
- width: 8em;
-}
-
-fieldset input.wide {
- width: 30em;
-}
-
-fieldset span.example {
- font-size: x-small;
-
- margin-left: 1em;
-}
-
-/* Search form */
-div#search-form {
- padding: 25px;
-
- text-align: center;
-}
-
-/*
-div#search input[type=text] {
- display: block;
-
- width: 40%;
- margin: 0px auto 5px auto;
-}
-
-div#search div.indent {
- margin-left: 8.5em;
-}
-*/
-
-div#search-help {
-
-}
-
-div#search-error {
- margin: 50px;
-
- text-align: center;
- font-size: x-large;
- font-style: italic;
-}
-
-/* Log lines */
-a.more-link {
- color: black;
- text-decoration: none;
-}
-
-a.more-link:hover {
- text-decoration: underline;
-}
-
-div#lines a {
- color: black;
-}
-
-div#lines a[id] {
- color: #ffffff;
- text-decoration: none;
-}
-
-div#lines a[id]:hover,
-div#lines a[id]:target {
- color: #000000;
-}
-
-/* Pagination */
-div.paginate {
- height: 50px;
-
- text-align: center;
-}
-
-div.paginate ul {
- margin: 0px;
- padding: 0px;
-
- line-height: 30px;
-
- border: 1px solid #aaa;
-
- white-space: nowrap;
-}
-
-div.paginate li {
- list-style-type: none;
- display: inline;
-}
-
-div.paginate li * {
- padding: 7px 10px;
-}
-
-div.paginate li a {
- color: black;
- text-decoration: none;
-}
-
-div.paginate li span {
- color: #d5d5d5;
-}
-
-div.paginate li a:hover {
- background-color: #d0d0d0;
-}
-
-li.paginate-left {
- float: left;
-}
-
-li.paginate-right {
- float: right;
-}
-
-/* Lastlog */
-div#other-formats {
- font-size: small;
- text-align: center;
- font-style: italic;
-}
--- a/templates/channel.tmpl Sun Sep 13 18:57:48 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-<%inherit file="layout.tmpl" />
-
-<%def name="menu()">
-<ul>
- <li><a href="${urls.index.build(req)}">Home</a></li>
- <li><a href="${urls.preferences.build(req)}">Preferences</a></li>
-
- <li>
- <a href="${urls.channel.build(req, channel=channel)}">Channel:</a>
- </li><li class="join-left">
- <form action="${urls.channel_select.build(req)}" method="GET">
- <select name="channel">
- ${h.select_options(((ch.id, ch.title) for ch in channel_list), channel.id)}
- </select><input type="submit" value="Go »" />
- </form>
- </li>
-
- <li><a href="${urls.channel_calendar.build(req, channel=channel)}">Browse by Date</a></li>
-
- <li>
- <a href="${h.build_url(urls.channel_search, channel=channel)}">Search:</a>
- </li><li class="join-left">
- <form action="${urls.channel_search.build(req, channel=channel)}" method="GET">
- <input type="hidden" name="count" value="100" />
- <input type="text" name="q" value="${search_query if search_query else ''}" />
- <input type="submit" value="Go »" />
- </form>
- </li>
-<!--
- <li><a href="${h.build_url(urls.channel_last, channel=channel, count=count or None, type='rss')}">[RSS]</a></li>
--->
-</ul>
-</%def>
-
-${next.body()}
-
-<%def name="footer_right()">
- All times are in <a href="${h.build_url(urls.preferences)}">${h.tz_name(prefs['timezone'])}</a>
-</%def>
--- a/templates/channel_calendar.tmpl Sun Sep 13 18:57:48 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-<%inherit file="channel.tmpl" />
-
-<%def name="month_table(month, is_center=True)">
-## the set of available days
-<% log_dates = h.set(channel.source.get_month_days(month)) %>
-## the calendar table
-<table class="calendar">
-## table header - month name
- <tr class="month-header">
- <th colspan="7"${' class="this-month"' if h.is_this_month(month) else ''}>
- % if is_center :
- <a href="${urls.channel_calendar.build(req, channel=channel, year=h.next_month(month).year, month=h.next_month(month).month)}" class="next-month" title="Next month">»</a>
- <a href="${urls.channel_calendar.build(req, channel=channel, year=h.prev_month(month).year, month=h.prev_month(month).month)}" class="prev-month" title="Previous month">«</a>
-
- <form action="${urls.channel_calendar.build(req, channel=channel)}" method="GET">
- <select name="month">
- ${h.select_options(((month_num, name) for month_num, name in h.months), month.month)}
- </select>
- <select name="year">
- ${h.select_options(((None, 2006), (None, 2007), (None, 2008), (None, 2009)), month.year)}
- </select>
- <input type="submit" value="Go »" />
- </form>
- % else :
- <span id="month-name">${h.fmt_month(month)}</span>
- % endif
- </th>
- </tr>
-## month header - weekday names
- <tr class="week-header">
- % for weekday in h.calendar.iterweekdays() :
- <th>${h.fmt_weekday(weekday)}</th>
- % endfor
- </tr>
-## iterate over the weeks
-% for week in h.calendar.monthdays2calendar(month.year, month.month) :
- <tr>
- ## iterate over the week's days
- % for day, weekday in week :
- ## is it an empty cell?
- % if not day :
- <td> </td>
- % else :
- ## build date
- <% date = h.build_date(month, day) %>\
- ## render cell
- <td${' id="today"' if h.is_today(date) else ''}${' class="empty"' if date.date() not in log_dates else ''}>\
- ## link to logs for this day?
- % if date.date() in log_dates :
-<a href="${urls.channel_date.build(req, channel=channel, date=date)}">${day}</a>\
- % else :
-${day}\
- % endif
-</td>
- % endif
- % endfor
- </tr>
-% endfor
-</table>
-</%def>
-
-<div id="title">${channel.title} :: Calendar for ${h.fmt_month(month)}</div>
-
-<div class="calendar-list">
-## three months
-${month_table(h.prev_month(month), is_center=False )}
-${month_table(month, is_center=True )}
-${month_table(h.next_month(month), is_center=False )}
-</div>
--- a/templates/channel_date.tmpl Sun Sep 13 18:57:48 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-<%inherit file="channel.tmpl" />
-<%namespace file="inc_paginate.tmpl" name="paginate" />
-
-## special pagination stuff
-<%def name="paginate_date()">
- <%call expr="paginate.paginate(urls.channel_date, count, page, max, channel=channel, date=date)">
- <%def name="left()">
- % if date_prev :
- <a href="${h.build_url(urls.channel_date, channel=channel, date=date_prev)}">« ${h.fmt_date(date_prev)}</a>
- % endif
- </%def>
- <%def name="right()">
- % if date_next :
- <a href="${h.build_url(urls.channel_date, channel=channel, date=date_next)}">${h.fmt_date(date_next)} »</a>
- % endif
- </%def>
- </%call>
-</%def>
-
-<div id="title">${channel.title} :: Logs for ${h.fmt_date(date)}</div>
-
-% if page :
-${paginate_date()}
-% endif
-<%include file="lines.tmpl" />
-% if page :
-${paginate_date()}
-% endif
-
--- a/templates/channel_last.tmpl Sun Sep 13 18:57:48 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-<%inherit file="channel.tmpl" />
-
-<div id="title">${channel.title} :: Last ${count} lines</div>
-
-<form action="${h.url(urls.channel_last, channel=channel)}" method="GET">
- View last <select name="count">
- ${h.select_options(((None, cc) for cc in (10, 20, 50, 100, 200, 500, 1000)), count)}
- </select> lines: <input type="submit" value="Go »" />
-</form>
-
-<%include file="lines.tmpl" />
-
-<div id="other-formats">
- Other formats available:<br/>
- <a href="${h.url(urls.channel_last, channel=channel, count=count, type='txt')}">Plaintext</a>
- | <a href="${h.url(urls.channel_last, channel=channel, count=count, type='png')}">PNG</a>
- | <a href="${h.url(urls.channel_last, channel=channel, count=count, type='rss')}">RSS</a>
-</div>
--- a/templates/channel_search.tmpl Sun Sep 13 18:57:48 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-<%inherit file="channel.tmpl" />
-<%namespace file="inc_paginate.tmpl" import="paginate" />
-
-% if not show_results :
-<div id="title">${channel.title} :: Search</div>
-
-<div id="search">
- <form action="${h.build_url(urls.channel_search, channel=channel)}" method="GET">
- <fieldset>
- <p>
- <label for="q">Message:</label>
- <input type="text" name="q" class="wide" />
- </p>
-
- <p>
- <label for="nick">By nickname:</label>
- <input type="text" name="nick" />
- <span>(optional)</span>
- </p>
-
- <p>
- <label for="count">Results/page:</label>
- <select name="count">
- ${h.select_options(((cc, cc_label) for cc, cc_label in config.SEARCH_LINE_COUNT_OPTIONS), count)}
- </select>
- </p>
-
- <input type="submit" value="Search" />
- </fieldset>
- </form>
-
- <div id="search-help">
- <p>Search powered by <a href="http://hyperestraier.sourceforge.net/">Hyper Estraier</a>:</p>
-
- <ul>
- <li>Group words together using quotes: <tt>"united nations"</tt></li>
- <li>Searching for multiple words is AND: <tt>internet security</tt></li>
- <li>To exclude terms, use <strong>!</strong> : <tt>hacker ! cracker</tt></li>
- <li>Union (i.e. <q>or</q>) using <strong>|</strong> : <tt>proxy | firewall</tt></li>
- <li>Search is case-insensitive</li>
- </ul>
- </div>
-</div>
-
-% else :
-<div id="title">${channel.title} :: Results\
-% if search_query :
- With '${search_query}'\
-% endif
-% if search_nick :
- By ${search_nick}\
-% endif
-</div>
-
-${paginate(urls.channel_search, count, page, max, channel=channel, q=search_query, t=search_targets, _more=True, _last=not(bool(lines)))}
-% if lines :
-<%include file="lines.tmpl" />
-% else :
-<div id="search-error">No results found</div>
-% endif
-${paginate(urls.channel_search, count, page, max, channel=channel, q=search_query, t=search_targets, _more=True, _last=not(bool(lines)))}
-% endif
--- a/templates/inc_paginate.tmpl Sun Sep 13 18:57:48 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-## special overrides...
-<%def name="paginate_left()">
-
-</%def>
-
-<%def name="paginate(url, count, page_cur, page_max, _more=None, _last=False, **args)">
- <%doc>
- Pagination works using page numbers, with a specific number of maximum pages displayed. If _more is True,
- then instead of a "Next" button, we have a "More" button, which goes to the max+1'th page, unless _last is
- True, whereupon it's not displayed.
-
- Can be called using <%call>, whereupon caller.left/caller.right can define additional data to display at the
- left/right of the pagination ul.
- </%doc>
- <div class="paginate">
- <ul>
- % if caller and caller.right :
- <li class="paginate-right">
- ${caller.right()}
- </li>
- % endif
- % if caller and caller.left :
- <li class="paginate-left">
- ${caller.left()}
- </li>
- % endif
- <li>
- % if page_cur > 1 :
- <a href="${h.build_url(url, count=count, page=page_cur-1, max=max, **args)}">« Prev</a>
- % else :
- <span>« Prev</span>
- %endif
- </li>
- % for page in xrange(1, page_max + 1) :
- <li>
- % if page == page_cur :
- <strong>${page}</strong>
- % else :
- <a href="${h.build_url(url, count=count, page=page, max=page_max, **args)}">${page}</a>
- % endif
- </li>
- % endfor
- % if _more and not _last :
- <li>…</li>
- % endif
- <li>
- % if _more and _last :
- <span>More »</span>
- % elif _more :
- <a href="${h.build_url(url, count=count, page=page_max+1, **args)}">More »</a>
- % elif page_cur == page_max : ## last page
- <span>Next »</span>
- % else :
- <a href="${h.build_url(url, count=count, page=page_cur+1, **args)}">Next »</a>
- % endif
- </li>
- </ul>
- </div>
-</%def>
-
--- a/templates/index.tmpl Sun Sep 13 18:57:48 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-<%inherit file="layout.tmpl" />
-
-<div id="title">Available Channels</div>
-
-<ul>
-% for channel in channel_list :
- <li><a href="${urls.channel.build(req, channel=channel)}">${channel.title}</a></li>
-% endfor
-</ul>
-
--- a/templates/layout.tmpl Sun Sep 13 18:57:48 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-
-<%def name="menu()">
-<ul>
- <li><a href="${urls.index.build(req)}">Home</a></li>
- <li><a href="${urls.preferences.build(req)}">Preferences</a></li>
-
- <li>
- <span>Channel:</span>
- </li><li class="join-left">
- <form action="${urls.channel_select.build(req)}" method="GET">
- <select name="channel">
- ${h.select_options(((ch.id, ch.title) for ch in channel_list), channel.id if channel else None)}
- </select><input type="submit" value="Go »" />
- </form>
- </li>
-</ul>
-</%def>
-
-<%def name="footer_right()">
-
-</%def>
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
- <head>
- <title>irclogs.qmsk.net${(' :: ' + channel.title) if channel else ''}</title>
- <link rel="Stylesheet" type="text/css" href="${req.site_root}/static/irclogs.css" />
- ## timezone-autodetect
- % if prefs and prefs.is_default('timezone') :
- <script language="Javascript" type="text/javascript">
-/*
- * Set a preference cookie
- */
-function set_pref (name, value) {
- // XXX: expire?
- document.cookie = (name + "=" + value + "; path=/");
-}
-
-/*
- * Set the timezone_offset cookie to the current Date's timezone offset
- */
-function autodetect_tz_offset () {
- // current datetime
- var now = new Date();
-
- // timezone offset from UTC in minutes
- var timezone_offset = -now.getTimezoneOffset()
-
- // store cookie with offset in minutes
- set_pref('timezone_offset', timezone_offset);
-}
-
-/*
- * Autodetect at load
- */
-window.onload = autodetect_tz_offset;
- </script>
- % endif
- </head>
- <body>
- <div id="menu">
- ${next.menu()}
- </div>
-
- <div id="content">
- ${next.body()}
- </div>
-
- <div id="footer">
- <div id="footer-right">
- ${next.footer_right()}
- </div>
-
- <div id="footer-left">
- <a href="http://projects.qmsk.net/irclogs2">irclogs2</a> version ${h.version_link()}
- </div>
-
- <div id="footer-center">
- ${h.validation_notice(req.site_host)}
- </div>
- </div>
- </body>
-</html>
-
--- a/templates/lines.tmpl Sun Sep 13 18:57:48 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-<% formatter = prefs['formatter'] %>
-
-<div id="lines">
-% if formatter.html_fixedwidth :
-<pre>
-% endif
-% for line, html in lines:
-<a href="${h.build_url(urls.channel_link, channel=channel, timestamp=line.timestamp)}#${h.utc_timestamp(line.timestamp)}" id="${h.utc_timestamp(line.timestamp)}">»» </a>${html}
-% endfor
-% if formatter.html_fixedwidth :
-</pre>
-% endif
-</div>
--- a/templates/preferences.tmpl Sun Sep 13 18:57:48 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-<%inherit file="layout.tmpl" />
-
-<div id="title">Edit Preferences</div>
-
-<form action="${urls.preferences.build(req)}" method="POST">
- <fieldset>
- <legend>Dates / Times</legend>
-
- <p>
- <label for="timezone">Timezone:</label>
- <select name="timezone">
- ${h.select_options(prefs.pref('timezone').OPTIONS, prefs.build('timezone'))}
- </select>
- <span class="example">(${h.tz_name(prefs['timezone'])})</span>
- </p>
-
- <p>
- <label for="date_format">Date format:</label>
- <input type="text" name="date_format" value="${prefs['date_format']}" />
- <span class="example">(${h.fmt_date()})</span>
- </p>
-
- <p>
- <label for="time_format">Time format:</label>
- <input type="text" name="time_format" value="${prefs['time_format']}" />
- <span class="example">(${h.fmt_time()})</span>
- </p>
-
- </fieldset>
-
- <fieldset>
- <legend>Log Output</legend>
-
- <p>
- <label for="formatter">Formatter:</label>
- <select name="formatter">
- ${h.select_options(((fmt_name, fmt.title) for fmt_name, fmt in preferences.formatter.formatters.iteritems()), prefs['formatter'].name)}
- </select>
- </p>
-
- <p>
- <label for="count">Lines / Page:</label>
- <input type="text" name="count" value="${prefs['count']}" />
-<!-- <span class="example">(Blank for infinite)</span> -->
- </p>
- </fieldset>
-
- <fieldset>
- <legend>Image Options</legend>
-
- <p>
- <label for="image_font">Font:</label>
- <select name="image_font">
- ${h.select_options(((name, title) for name, (path, title) in config.FORMATTER_IMAGE_FONTS.iteritems()), prefs['image_font'][0])}
- </select>
- </p>
-
- <p>
- <label for="image_font_size">Font size:</label>
- <input type="text" name="image_font_size" value="${prefs['image_font_size']}" />
- <span class="example">pt</span>
- </p>
- </fieldset>
-
- <input type="submit" value="Save" />
-</form>
-