Commits

Chris Mulligan committed d53a3b6

Lots of misc checkins - including better base_path support, README/SETUP and more.

  • Participants
  • Parent commits 3867ee5

Comments (0)

Files changed (13)

 
 Requirements
 ============
-pmxbot requires python, of course. Probably 2.5+. It also requires a few python packages:
+pmxbot requires python, of course. Probably 2.5+. It also requires a few python
+packages:
 
 * python-irclib
 * pyyaml
 
 Optionally nosetests for the test suite.
 
+The web interface requires cherrypy and jinja2.
+
 Testing
 =======
-We have a basic nose test suite that does some functional tests with an included
-TCL IRC daemon, and some basic unit tests as well. Just run them from the pmxbot
-root directory with "nosetests" and it should do it all for you. You'll need
-TCL and nosetests to do it though.
+We have a basic nose test suite that does some functional tests with an
+included TCL IRC daemon, and some basic unit tests as well. Just run them from
+the pmxbot root directory with "nosetests" and it should do it all for you.
+You'll need TCL and nosetests to do it though.
 
 Configuration
 =============
 
 A command (!g) gets the @command deocator::
 
-  @command("tinytear", aliases=('tt', 'tear', 'cry'), doc="I cry a tiny tear for you.")
+  @command("tinytear", aliases=('tt', 'tear', 'cry'), doc="I cry a tiny tear \
+for you.")
   def tinytear(client, event, channel, nick, rest):
   	if rest:
   		return "/me sheds a single tear for %s" % rest
   	else:
-  		return "/me sits and cries as a single tear slowly trickles down its cheek"
+  		return "/me sits and cries as a single tear slowly trickles down its \
+  		cheek"
 
 A response (when someone says something) uses the @contains decorator::
 
   	karmaChange(botbase.logger.db, 'sql on rails', 1)
   	return "Only 76,417 lines..."
 
+Web Interface
+=============
+pmxbot includes a web server for allowing users to view the logs, read the
+help, and check karma. You specify the host, port, base path, logo, title,
+etc with the same YAML config file. Just run like ``pmxbotweb config.yaml``
+and it will start up. I suggest using a tool like supervisor, daemontools,
+cherryd or somesuch to keep it running.
 
+
       url     : "http://search.twitter.com/search.atom?q=pmxbot"
 local_extensions: ['local.py']
 librarypaste: http://a.libpa.st/
+
+web_base: /
 web_port: 8080
-web_host: 0.0.0.0
+web_host: 0.0.0.0
+#logo: /pmxbot.png

File pmxbot/botbase.py

 		INSERT_LOG_SQL = 'INSERT INTO logs (datetime, channel, nick, message) VALUES (?, ?, ?, ?)'
 		now = datetime.datetime.now()
 		channel = channel.replace('#', '')
-		self.db.execute(INSERT_LOG_SQL, [now, channel, nick, msg])
+		self.db.execute(INSERT_LOG_SQL, [now, channel.lower(), nick, msg])
 		self.db.commit()
 
 	def last_seen(self, nick):
 		DELETE_LINE_SQL = '''delete from logs where channel = ? and nick = ? and id = ?'''
 		channel = channel.replace('#', '')
 		
-		ids_to_delete = self.db.execute(LAST_N_IDS_SQL, [channel, nick, count]).fetchall()
+		ids_to_delete = self.db.execute(LAST_N_IDS_SQL, [channel.lower(), nick, count]).fetchall()
 		if ids_to_delete:
 			deleted = self.db.executemany(DELETE_LINE_SQL, ids_to_delete)
 			self.db.commit()

File pmxbotweb/pmxbotweb.py

 		config.web_port
 	except AttributeError:
 		config.web_port = 8080
+	try:
+		config.logo
+	except AttributeError:
+		config.logo = '%s/pmxbot.png' % config.web_base
 
     # Cherrypy configuration here
     app_conf = {
             'server.socket_port': config.web_port,
             'server.socket_host': config.web_host,
         },
+		'/pmxbot.png' : {
+			'tools.staticfile.on' : True,
+			'tools.staticfile.filename' : os.path.join(os.path.dirname(__file__), 'templates/pmxbot.png'),
+		},
         'db' : {'database' : os.path.join(config.database_dir, 'pmxbot.sqlite')},
 		'botconf' : {'config' : config},
     }

File pmxbotweb/templates/base.html

 	<body>
 	<div id="header">
 		<h1><img src="{{ logo }}" border="0" alt="PMXBOT" height="50"/>
-		<a href="{{ request.base }}">Log file Viewer</a></h1>
-		<form action="{{ request.base }}/search" method="GET">
+		<a href="{{ base }}">Log file Viewer</a></h1>
+		<form action="{{ base }}/search" method="GET">
 		<h3>
-		    <a href="{{ request.base }}/help">Help</a> | 
-		    <a href="{{ request.base }}/karma">Karma</a> | 
-            <a href="{{ request.base }}/quotes">Quotes</a> | 
+		    <a href="{{ base }}/help">Help</a> | 
+		    <a href="{{ base }}/karma">Karma</a> | 
 				Search: <input type="text" name="term" />
 				<input type="submit" value="Search" />
 		</h3>

File pmxbotweb/templates/channel.html

 
 <h3>#{{ channel }} logs</h3>
 <div class="navsec">
-(<a href="{{ request.base }}">Back to channel listing</a>)
+(<a href="{{ base }}">Back to channel listing</a>)
 </div>
 
 {% for month, days in months %}
 	<div class="monthhead">{{ month }}</div>
 	<ul>
     {% for daylab, day in days %}
-	<li><a href="{{ request.base }}/day/{{ channel }}/{{ day }}">{{ daylab }}</a></li>
+	<li><a href="{{ base }}/day/{{ channel }}/{{ day }}">{{ daylab }}</a></li>
     {% endfor %}
 	</ul>
 </div>

File pmxbotweb/templates/day.html

 
 <h3>#{{ channel }} logs for {{ pdate }}</h3>
 <div class="navsec">
-(<a href="{{ request.base }}/channel/{{ channel }}">Back to #{{ channel }} overview</a>)
-(<a href="{{ request.base }}">Back to channel listing</a>)
+(<a href="{{ base }}/channel/{{ channel }}">Back to #{{ channel }} overview</a>)
+(<a href="{{ base }}">Back to channel listing</a>)
 </div>
 
 <div class="logsection">

File pmxbotweb/templates/index.html

 {% block body %}
 
 <h3>Current Channels</h3>
-<ol>
+<ul>
 {% for chan, dt, day, time, nick, msg, marker in chans %}
-	<li><a href="{{ request.base }}/channel/{{ chan }}">{{ chan }}</a>&nbsp;&mdash;&nbsp; <span style="font-family: fixed;">[<a href="{{ request.base }}/day/{{ chan }}/{{ day }}#{{ marker }}" class="comment-link">{{ dt }}</a>] &lt;{{ nick }}&gt; {{ msg }}</li>
+	<li><a href="{{ base }}/channel/{{ chan }}">{{ chan }}</a>&nbsp;&mdash;&nbsp; <span style="font-family: fixed;">[<a href="{{ base }}/day/{{ chan }}/{{ day }}#{{ marker }}" class="comment-link">{{ dt }}</a>] &lt;{{ nick }}&gt; {{ msg }}</li>
 {% endfor %}
-</ol>
+</ul>
 
 {% endblock %}

File pmxbotweb/templates/karma.html

 
 <hr/>
 <h3>Karma Lookup</h3>
-<form action="{{ request.base }}/karma" method="GET">
+<form action="{{ base }}/karma" method="GET">
 	Lookup: <input type="text" name="term" value="{{ term }}"/>
 	<input type="submit" value="Lookup" />
 </form>

File pmxbotweb/templates/pmxbot.png

Added
New image

File pmxbotweb/templates/search.html

 
 {% for chan, date, marker, history in search_results %}
 <div class="result_heading">
-<a href="{{ request.base }}/day/{{ chan }}/{{ date }}#{{ marker }}"><i>{{ date }} in {{ chan }}</i></a>
+<a href="{{ base }}/day/{{ chan }}/{{ date }}#{{ marker }}"><i>{{ date }} in {{ chan }}</i></a>
 </div>
 <div class="logsection">
 {% for tm, name, msg in history %}

File pmxbotweb/viewer.py

 #!/usr/bin/env python
 # encoding: utf-8
+# -*- coding: utf-8 -*-
 import os
 import cherrypy
 from sqlite3 import dbapi2 as sqlite
 from cgi import escape
 
 BASE = os.path.abspath(os.path.dirname(__file__))
-jenv = Environment(loader=FileSystemLoader(os.path.join(BASE, 'templates')))
+jenv = Environment(loader=FileSystemLoader(os.path.join(BASE, 'templates'), encoding='utf-8'))
 
 
 
 
 def get_context():
 	c = cherrypy.request.app.config['botconf']['config']
-	d = {'request': cherrypy.request, 'name' : c.bot_nickname,	'config' : c}
+	d = {'request': cherrypy.request, 'name' : c.bot_nickname, 'config' : c, 'base' : c.web_base, }
 	try:
 		d['logo'] = c.logo
 	except AttributeError:
 		context['months'] = sorted(months.items(), key=lambda v: sort_month_key(v[0]),
 		reverse=True) 
 		context['channel'] = channel
-		return page.render(**context)
+		return page.render(**context).encode('utf-8')
 	default.exposed = True
 
 class DayPage(object):
 		dbfile = cherrypy.request.app.config['db']['database']
 		db = sqlite.connect(dbfile)
 		context = get_context()
-		db.text_factory = lambda x: unicode(x, "utf-8", "ignore")
+		#db.text_factory = lambda x: unicode(x, "utf-8", "ignore")
 		DAY_DETAIL_SQL = 'SELECT time(datetime), nick, message from logs where channel = ? and date(datetime) = ? order by datetime'
 		day_logs = db.execute(DAY_DETAIL_SQL, [channel, day])
 		data = [(t, n, make_anchor((t, n)), escape(m)) for (t,n,m) in day_logs]
 		context['history'] = data
 		context['channel'] = channel
 		context['pdate'] = "%s of %s" % (pday(day), pmon(day.rsplit('-', 1)[0]))
-		print context
-		return page.render(**context)
+		return page.render(**context).encode('utf-8')
 	default.exposed = True
 
 
 		context = get_context()
 		dbfile = cherrypy.request.app.config['db']['database']
 		db = sqlite.connect(dbfile)
-		db.text_factory = lambda x: unicode(x, "utf-8", "ignore")
 		term = term.strip()
 		if term:
 			context['lookup'] = []
 				context['lookup'].append(('NO RESULTS FOUND', ''))
 		context['top100'] = karmaList(db, 100)
 		context['bottom100'] = karmaList(db, -100)
-		return page.render(**context)
+		return page.render(**context).encode('utf-8')
 	default.exposed = True
 
 def search_logs(term, db):
 		context['search_results'] = results
 		context['num_results'] = len(results)
 		context['term'] = term
-		return page.render(**context)
+		return page.render(**context).encode('utf-8')
 	default.exposed = True
 		
 	
 			self.run = True
 		context['commands'] = self.commands
 		context['contains'] = self.contains
-		return page.render(**context)
+		return page.render(**context).encode('utf-8')
 	default.exposed = True
 	
 
 		dbfile = cherrypy.request.app.config['db']['database']
 		db = sqlite.connect(dbfile)
 		context = get_context()
-		CHANNEL_LIST_SQL = "SELECT distinct channel from logs order by channel"
+		CHANNEL_LIST_SQL = "SELECT distinct channel from logs order by lower(channel)"
 		LAST_LINE_SQL = '''SELECT strftime("%Y-%m-%d %H:%M", datetime), date(datetime), time(datetime), nick, message from logs where channel = ? order by datetime desc limit 1'''
 		chans = []
 		for chan in db.execute(CHANNEL_LIST_SQL).fetchall():
 			last.append(make_anchor((last[2], last[3])))
 			chans.append([chan] + last)
 		context['chans'] = chans
-		return page.render(**context)
+		return page.render(**context).encode('utf-8')
 	default.exposed = True
 		
 		
 from setuptools import setup
 
 setup(name="pmxbot",
-    version="1001",
+    version="1002-beta",
     packages=["pmxbot"],
     package_data={'pmxbot' : ["popquotes.sqlite"]},
     entry_points={
     It can search the web, quote you, track karma, make decisions,
     and do just about anything else you could want. It logs text in a sqlite3
     database, and eventually we'll write a web interface to it.
+
+    Web
+    ===
+    pmxbotweb is the web interface to view and search all the logs.
     """,
     )