Friday, September 7, 2012

Summer Project: Reservation System

This is the first part of my Summer Project series. My current job is working the help desk at a Linux lab for the College of Engineering at the University of Utah. During the school year, I am expected to help student with problems they incur when using a Linux. During the Summer, I do some actual programming that my boss needs to get done at the time. I know that this isn't games, but it is programming and the experience will help greatly in my endeavors.

Each of these projects that I did were Web Applications written in PHP. I don't like PHP. It is a poorly written language that doesn't know when to die. Half the time is me fighting PHP when it doesn't do what I need it to do and when it doesn't crash when any other language would crash. Its too lenient when the programmer makes a mistake and the array that is an abomination of a hash map. For all the problems I have with PHP, the reason I still choose to program with it instead of JSM or Ruby on Rails is that first, I know PHP; and second, PHP has built in functions to connect to LDAP.

 The first project I did was a Lab Reservation System. The lab I work for (CADE) is one in seven labs, either windows, linux, or mac. Not only are these for students to get work for, but some are teaching labs and used by professors to help teach their engineering class. These labs can also be used for other purposes, such as summer camps or orientation, if they ask permission first. Now enter in the reservation system. When my boss approached me, there was already a reservation system in place. The layout was horrible and the person approving the request would have to enter in the entry into the SQL server by hand. My job was to completely rebuild it.

I started out by turning my computer into a temporary apache server and database for testing purposes. Getting the layout and css was a lot easier than I imagined because the College of Engineering already has a layout ready to use. From there, I set up the index page to list all the reservations that were approved. A page for a forum to submit a request, and a page that only admins could access in order to approve/deny requests.

After all that was set up, I created a separate script that would check to see if the request had any conflicts. If it did, it would let the user know; else, send the request through to be approved. Checking for conflicts proved to be the most nightmarish part of the whole experience and had flaws in it even past release. The last bug report I got about it was three months after the reservation system was released.

The way I did check for conflicts was to start out general and keep shaving off requirements that did conflict until the program was certain that there was a conflict. Here is the overview of it in pseudocode:

$requestorsInfo

foreach(person in the database as $databaseInfo)
{
     //both want same lab
     if($databaseInfo->lab == $requestorInfo->lab)
     {
            //if the range of dates they want for overlaps with the other
            if($databaseInfo->dateRange conflicts with $requestorInfo->dateRange)
            {
                    //Even though the person specifies a range of dates Aug 8, 2012 - Dec 10, 2012
                    //They can only have it on a certain day of the week such as Tuesday, Thursday
                    //Therefore this will check to see if they both want the lab on the same day of the week
                   if($databaseInfo->dayWeek conflicts with $requestorInfo->dayWeek)
                   {
                           //If they both want it at overlapping times, then that seals the deal. There is a conflict
                          if($databaseInfo->timeRange conflicts with $requestorInfo->timeRange)
                          {
                                  return true //there is a conflict
                          }
                   }
            }
     }
}
return false //there isn't a conflict.

Finding a date conflict took a bit of thinking, but after picturing all the different kinds of overlap might look like, I was able to form a boolean expression to determine conflicts in time.

s1: start date for the requester
e1: end date for the requester

s2: start date for database entry
e2: end date for database entry

Conflict 1:
s1                              e1
|--------------------------|
                 |--------------------------|
                s2                                    e2

Conflict 2:
                   s1                          e1
                    |------------------------|
|-------------------------|
s2                                 e2

Conflict 3:
s1                                       e1
|-------------------------------------|
           |--------------------|
          s2                           e2

Conflict 4:
           s1                        e1
            |------------------|
|---------------------------------|
s2                                               e2

Conflict 3 is true if Conflict 1 AND Conflict 2 are true, so we can remove it from the boolean expression since it is enough if Conflict 1 OR Conflict 2 is true.

The boolean expression is:
($s2 <= $e1 && $e1 <= $e2) || ($s2 <= $s1 && $s1 <= $s2) || ($s1 <= $s2 && $e2 <= $e1)

When calculating the date, I make a gaffe early on. The database takes it in the from Month_Day_Year and stores each part in an array. The database also returns it likewise. When the project was "complete" and I was taking it into beta testing, I read about the Date field for SQL and that it could do such comparisons. However, since the project was so complete, I choose to stick with what I had and work out the bugs instead of choosing to rewrite my code and rearrange my database in an attempt to do the least amount of work possible. I still keep it as is after doing more work debugging because it does work and I am really lazy about rewriting code and reworking the database.

Since I choose to stick with what I had, I needed to represent the dates as value in order to compare them. I could have used a lot of conditionals if the months were the same and if the years were the same, but that felt like more work that was needed. I then decided to split the month, day and year. That of course did not work. After some more thought, I decided to represent each date as a unique integer that could be compared a lot easier. By turning the month into how many days until that event (January would be worth 31 and February would be 59 or 60 depending on the year. Then summing each value up (i.e. January 1, 2012 would be 31 + 1 + 2012). This will produce a unique integer that can compare any date of any year.


To check the conflict of the day of the week actually gave me more problems than I had expected and was the last part that was responsible for bugs even three months after release. A conflict happens when there is at least one day of the week that both people want (i.e. a wants Tuesday Thursday and b wants Wednesday Thursday). The day of the week that a person wants is stored in a array of bytes.

$dayWeek = array(
            'Monday' => 0 | 1
            'Tuesday' => 0 | 1
            'Wednesday' => 0 | 1
            'Thursday' => 0 | 1
            'Friday' => 0 | 1
            'Saturday' => 0 | 1
            'Sunday' => 0 | 1
);

1 if they want it, 0 if not.

A conflict should return true if you go through each day of the week for each comparing the two and ANDing them together. If any AND returns true, then there is a conflict. There were two things I tried before I go (I believe) the correct solution.

Try 1:
 return ($requestor->$dayWeek['Monday'] == $database->$dayWeek['Monday']) || ($requestor->$dayWeek['Tuesday'] == $database->$dayWeek['Tuesday']) || ($requestor->$dayWeek['Wednesday'] == $database->$dayWeek['Wednesday']) || ($requestor->$dayWeek['Thursday'] == $database->$dayWeek['Thursday']) || ($requestor->$dayWeek['Friday'] == $database->$dayWeek['Friday']) || ($requestor->$dayWeek['Saturday'] == $database->$dayWeek['Saturday']) || ($requestor->$dayWeek['Sunday'] == $database->$dayWeek['Sunday'])

The reason this didn't work is because I was treating the 1 and 0 as booleans and not integers. False == False is False, but 0 == 0 is true.

Try 2:
return $requestor->$dayWeek === $database->$dayWeek

This would only work if the user and database wanted the same days of the week and were clones of each other. I should have figured this out at first, but I was pretty lazy.

Implementation:

foreach(dayof the week as $i)
{
      if($requestor->dayWeek[$i] == 1 && $requestor->dayWeek[$i] == 1)
             return true;
}
return false;

This was basically the 1st implementation (Try 1) had the compiler treated 1 and 0 as bools and not integers.

The last conflict check was for the time. This works the same way as the Date checker. The only exception is how it compares times. The time that is inputted is in the 12-hour format. The program takes the the time and converts it into the 24-hour format and then converts the hours into minutes and sums the two up. This creates a unique integer that can be compared easily.

That is the algorithm that checks for conflict in a request. I would like to see how a more complicated system would check for it (like hotel reservations). The rest of the code is basically a bunch of calls to the database to either remove entries, add them, or manipulate them to present to the viewer.

That's all for the Reservation System. It was definitely a challenge and really helped my problem solving. Next part int he Summer Project Series will be CoE Network Request which will not be as long as this one.
               

No comments:

Post a Comment