An evening of databases

So after getting home and logging on to an open evening, my mind turns towards selecting a database (a software component) for my hobby software project. Yes, this is really how I spend some of my evenings.

I spent several hours on the activity: reading database comparisons, discussions, and the manuals of some of a couple of the lesser known ones (Firebird and Postgresql) that explained how they worked internally. It was pretty satisfying, in a home-cooked meal way.

Reflecting upon it, I realized that I had revisited my tech roots by both:

  1. Spending an enjoyable evening reading about how databases work.
  2. Selecting databases that I’d only “heard” about, rather than staying with the mainstream.

For at least a night, I really missed the technical challenge of building software. Processes and politics are important, too, depending on the organization, but it’s not as satisfying as making something that works.

Hacking around in Python for Pyblosxom plugins

I decided to remove the calendar from my blog’s sidebar, and replace it with a “recent posts” list. I realized that it’s not very helpful, since readers aren’t so much interested in when I posted as what I posted about.

There was no pyblosxom plugin to do this, so I rolled up my sleeves and learned some more Python, referring to the Pyblosxom API documentation along the way. The resultant plugin is simple, although I had to fill in the gaps in the API docs through trial and error.

The code I wrote is below. Note that I hardcoded the flavour type (1024px), but I believe there’s an value in the entry dictionary I could have gotten it from.

 from Pyblosxom import tools from Pyblosxom import pyblosxom import time, re 

 def cb_prepare(args):   request = args["request"]   data = request.getData()   entry_list = data["entry_list"]   config = request.getConfiguration()   root = config["datadir"]   s=""     txt_dict = {}   for txt in tools.Walk(request, root, pattern=re.compile('.*\.txt$')):     timetuple = tools.filestat(request, txt)     txt_dict[time.strftime("%y%m%d%H%M",timetuple)]=txt    keys = txt_dict.keys()   keys.sort()   keys.reverse()   url_list = []   for k in keys:     path = re.sub(root, "", re.sub(".txt$", "", txt_dict[k]))     title = pyblosxom.blosxom_entry_parser(txt_dict[k],request)['title']     url_list.append("<li><a href='" + path + ".1024px'>" + title + "</a>")    url_list = [url_list[x] for x in range(0,19)]   for url in url_list:     s += url    data['recentPosts'] = s 

Around the same time, I had the idea of adding a “voting” mechanism to allow people who don’t have anything to say (comment) to still indicate which entries are interesting. I figured an easy approach would be to add voting buttons that triggered an AJAX-style HTTP request to special URLs, which would show up in the Apache logs and could later be parsed out. Being unfamiliar with Python, I could just focus on reading data in the plugin and save on code to tally votes (I was feeling lazy at the time).

Shortly thereafter, I got a blog comment:

Name: Gunfus
Entry URL: http://blog.astradele.com/life/Why-blog-.2008-02-01-14-50

I don’t think your yes/no is working to well. If you click it it dissapears (this is fine), if you reload the page it shows again (this is aceptable), once you reload, the counter is at 0/0 (this is expected?)

I thought I tested it properly, but… So I got into it again and wrote the solution completely in Python. At least I’m getting more familiar with the language. The solution still uses a custom URL format that don’t resolve to an actual page, but it’s the Pyblosxom plugin that notes the URI, checks to see if it’s similiar to an existing entry, creates files to track the IP addresses of votes on a per entry basis, and generates tallies.

I considered trying to devise a way to auto-refresh the tally instead of just hiding the voting “bar”, but upon consideration I wasn’t up to the effort it would take on the Python side.

The code:

 import sys, StringIO, os, os.path, re 

 def verify_installation(request):     return 1  def include(req, filename):     """     This takes in one argument--a filename.  It opens the file, reads     in the contents, and returns them as the value of this variable.      (It actually takes in two, but one of them is provided by     PyBlosxom.)     """     if not filename.startswith(os.sep):         data = req.getData()         basedir = data["root_datadir"]         if data.has_key("bl_type") and data["bl_type"] == "file":             basedir = os.path.dirname(basedir)          filename = os.path.normpath(basedir + os.sep + filename)      try:         f = open(filename, "r")         lines = "".join(f.readlines())         f.close()         return eval_python_blocks(req, lines)     except:         return ""  def cb_logrequest(args):   import os, time   http = args['request'].getHttp()   config = args['request'].getConfiguration()   request_uri = http['REQUEST_URI']   remote_addr = http['REMOTE_ADDR']   entry_name = re.compile("^/vote_(yes|no)(/.*)\..*$").sub(r"\2",request_uri)   if (os.path.exists(config['datadir'] + entry_name + ".txt")):     try:       os.makedirs(config['datadir'] +           "/votes" + re.compile("^(.*)/.*$").sub(r"\1", entry_name))     except:       pass      if (re.compile("^/vote_yes").match(request_uri)):       vote_file=config['datadir'] + "/votes" + entry_name + ".yes"     else:       vote_file=config['datadir'] + "/votes" + entry_name + ".no"      is_dupe=False     if (os.path.exists(vote_file)):       f=open(vote_file, "r")       for line in f:         is_dupe = is_dupe or re.compile(remote_addr).match(line)       f.close()      if (not is_dupe):       f=open(vote_file, "a")       f.write(remote_addr + "\n")       f.close()  def cb_story(args):   """   """   entry = args["entry"]   request = args["request"]   config = request.getConfiguration()   http = request.getHttp()    entry_filename = entry.getId()   yes_votes_file = re.compile(".txt$").sub(".yes",re.compile("^" + config['datadir']).sub(config['datadir'] + "/votes",entry_filename))   no_votes_file = re.compile(".txt$").sub(".no",re.compile("^" + config['datadir']).sub(config['datadir'] + "/votes",entry_filename))    entry['vote_yes_url'] = "/vote_yes/" + entry['file_path'] + "." + entry['flavour']   entry['vote_no_url'] = "/vote_no/" + entry['file_path'] + "." + entry['flavour']    request_uri = request.getHttp()["REQUEST_URI"]   remote_addr = request.getHttp()["REMOTE_ADDR"]    count=0   if (os.path.exists(yes_votes_file)):     f = open(yes_votes_file, "r")     for line in f:       count += 1     f.close()   entry["yes_votes_count"]=str(count)    count=0   if (os.path.exists(no_votes_file)):     f = open(no_votes_file, "r")     for line in f:       count += 1     f.close()   entry["no_votes_count"]=str(count) 

Follow

Get every new post delivered to your Inbox.