1 entry tagged picky, updated, and web

Picky Picky Game: minimal voting

(Sunday night.) Still nothing up for you to see yet, I’m afraid. (Apart from anything else, I need to ask my host to install a few Python packages...) But I do do now have the start of the second CGI script, the one that accepts reader’s votes for the current round of pictures. These votes later are used to decide which picture to use for that panel of the comic strip.

At present the script accepts your vote but does not display them in any way. If you vote again, your previous ballot is silently overwritten. I plan to support Approval Voting in future by having a page where you have a checkbox for each candidate picture and can select as many as you like.

The word ‘your’ is a little misleading; we use people’s IP addresses as their identifiers, which sort of works most of the time, but means that people sharing a proxy server will end up sharing a vote. The alternative (requiring users to register in order to vote) is not likely to work because noone will want to register.

Update (Monday night): The voting form now shows you the pictures with checkboxes. When you first visit the page, the picture you cloicked on is ticked, but then you can tick as many more as you like. Because of the way HTML forms are processed, each form parameter is potentially a sequence anyway, so the code for each time around the voting form can be exactly the same. The code that adjusts the totals is very simple:

def vote(self, uid, pns):
    """Register a vote from the user identified by uid.

    uid is an integer, uniquely identifying a voter.
    pns is a list of picture numbers
    """
    oldPns = self.userVotes.get(uid, [])
    if pns == oldPns:
        return
    for pn in oldPns:
        self.pictures[pn].nVotes += -1
    for pn in pns:
        self.pictures[pn].nVotes += 1
    self.userVotes[uid] = pns

The first line retrieves that user’s old ballot, if any. The first for statement reverses the effect (if any) of their former vote, the second counts the new vote. Finally the ‘ballot’ is saved for later. Behind the scenes, ZODB takes care of reading the old data in off disc and (when the transaction is committed) saving the updated data.

My paid job involves writing a web application as well, except this one uses Microsoft ASP .Net linked via ADO .Net to Microsoft SQL Server® 2000. To do a similar job to the above snippet, I would be writing two SQL stored procedures (one to retrieve the exisiting ballot, one to alter the ballot). Invoking a stored procedure is several more lines of code in the C♯ or VB .Net layer as you create a Command object, add parameters to it, execute it, and dispose of the remains. (Or you can create DataSet objects which are even worse, but have specialized wizards to help you draft the code.) The actual algorithm (the encoding of the business logic) would be buried in dozens of lines of boilerplate. By comparison, the Python+ZODB implementation is a miracle of concision and clarity. The ZOPE people deserve much kudos.