« Previous day (Aug 21, 2008) | Main | Next day (Aug 22, 2008) »
http://blogs.sun.com/paulie/date/20080822 Friday August 22, 2008

Odd Perl Hacks

For my senior project in my last two semesters at the University of Colorado myself and two others were tasked to create a web-based scheduling system for the Conference on World Affairs. This seemed like a trivial task, except we were lacking some serious tools. For one, we had no access to a database (no MySQL, Oracle, etc.) and the only language we could could use was Perl 5.6 with no modules. This was due to the fact that the system had to reside on the main university server, an old Solaris 8 V880, which is shared by most of the departments across the campus. Consequently, we had to build a database from scratch using text files while maintaining relational database concepts such as primary/foreign key constraints, atomic writes, and encryption. It also needed to support multiple years for each new conference, up to 200 panels a year, up to 100 panelists, along with coordinators and moderators for each panel. A conflict detection mechanism was necessary to discover problems with the schedule (one person in two places at the same time sort of thing). Additionally, a user account system was needed to allow only authorized people to use the scheduler.

It was an immensly complicated project that took about 10k lines of Perl to accomplish, which in the mind of ESR was probably way too much. It did, however, give me great insight onto why existing tools are so important along with a deep appreciation of modern relational databases. And our team even won an award for the project. Along the way, I also learned and created some neat Perl hacks to solve the problems with writing (and rewriting) a development library. Let me show you some tricks.


Round Up A Number

With the large amount of panels and panelists (exceeding 100), it was a good idea to create a paging scheme when searching through all of them to prevent really long web pages. So let's say you have 193 panels and you want 10 panels displayed per page. The obvious answer is to divide the ten, but you end up with a decimal of 19.3 pages when you actually need 20 pages to display everything. This function will round it up for you.
sub roundUp()
{
    # Calculates number of pages needed when a decimal place screws things up

    ($n)=@_;
    return(($n == int($n)) ? $n : int($n + 1))
}

Isolate Calendar Week

The Conference on World Affairs is always held the second week of April. Anytime a user wants to create a new conference, the system should be able to calculate the exact date when supplied a day of the week (M-F). This function takes care of it with UNIX cal.
sub convertDaytoDate()
{
    # Used to determine conference day of week without need
    # of unnecessary db storage.

    ($day,$year)=@_;

    # Isolate April using UNIX cal
    $cal=`cal 4 $year`;
    @cal = split("\n",$cal);

    # Grab the 2nd week of April
    $interest = $cal[3];
    $interest =~ s/(^....)//;
    $interest =~ s/\ \ /\ /g;
    @days = split("\ ",$interest);

    if ($day =~ /Monday/) { $DD = $days[0]; }
    if ($day =~ /Tuesday/) { $DD = $days[1]; }
    if ($day =~ /Wednesday/) { $DD = $days[2]; }
    if ($day =~ /Thursday/) { $DD = $days[3]; }
    if ($day =~ /Friday/) { $DD = $days[4]; }

    return("April $DD, $year");
}

Convert Non-ASCII Characters

Some panelists were foreign and had characters in their name that were outside of ASCII, such as the umlaut ö or accent á. When inputted into the text database, things went well. However, when read back into a web browser, a question mark or exclamation point was displayed instead. The trick is to convert these characters into XML form by isolating its ASCII value with ord() and playing with those greater than 127.
foreach $entry (@cwaparttable)
{
    chomp($entry);
    $entry =~ s/(.)/checkForNonASCII($1)/eg;
}

sub checkForNonASCII()
{
    # Eliminates foreign letters, character by character

    if (ord($_[0]) < 128){
        # ASCII
        return $_[0];
    } else {
        # Non-ASCII
        return sprintf('&#x%04X;', ord($_[0]));
    }
}


Posted by Paul Johnson [Sun] ( August 22, 2008 09:48 AM ) Permalink | Comments[2]