Sunday, May 10, 2009

Final Post

Results

The SalonBook project has continued to progress to the point where it is currently a functional prototype and proof of concept of a low cost, online scheduling system using PHP, MySQL, and HTML. Users can log in and access a variety of useful information all from their personal dashboard portal. Some of these features include peer reviews of salon and stylists, ability to make, view, and cancel appointments instantly on demand, review salons from past visits, accumulate and view points, search salons and contact information, and search salon promotions. The salon side of SalonBook has the same look and feel, but provides a different set of functions. In addition to many important features like making promotions, adding, removing, or editing stylists or beauty services, and viewing user ratings of their salon, salons have full online scheduling functionality. This was accomplished by integrating WebCalendar, a PHP-driven calendaring web application. The integration of WebCalendar was key to SalonBook because it offered important benefits that Google Calendars could not provide. For example, WebCalendar is open source, so all of the source code exists within my SalonBook directory. This makes my application completely stable, whereas outsourcing to Google left my application vulnerable to Google API changes or updates. WebCalendar also uses a MySQL database to store events, users, etc. This allows me to keep the WebCal meta data right next to my database tables for the rest of SalonBook. This gave me easy access to the data for retrieval, editing, and display; all necessary tasks for me to build the user appointment scheduling functionality. Also, I tweaked WebCalendar's HTML in order to give it the SalonBook look and feel. I incorporated the SalonBook banner logo, footer, and "Return to Dashboard" link into the WebCalendar interface to make it look and feel like part of the SalonBook application. For Salons, WebCalendar allows salons to easily visualize the schedule and make appointments manually. This feature of manually inserting appointments was identified from the outset as being critical because there would inevitably be a large percentage of clients that still call the salon directly to make appointments.

Overall, all major goals were accomplished for SalonBook. Below is a series of screenshots taken of the user and salon interfaces that better display the final results of this project.

User Interface:







Salon Interface:








Contributions

The contributions of this project to my knowledge base are extensive. Before starting, I had no previous experience with web development or any of the tools used for this application.

Database Design is an invaluable aspect of the process that I mastered in the early stages of the project. Building many-to-one relationships and robust databases were required in developing SalonBook. Familiarizing myself with SQL and database languages is also very useful going forward.

I also take away the combined understanding of PHP, MYSQL, and HTML, and how they come together to create a rich, database-backed web application.

Finally, web applications can become more than the sum of their parts with the successful integration of open source tools. Under limited time and resources, there is only so much one person can develop. However, by identifying appropriate open source projects and integrating them seamlessly, an application can become much more extensive and functional. Integrating WebCalendar, and widgets like the JavaScript date picker are perfect examples of this. If integrated poorly, these tools are obvious and detract from the user experience. Fortunately, I was able to integrate these tools well and package SalonBook as more than the sum of its parts.

In terms of contributions to the field, this prototype stands as a proof of concept for the idea of web-based appointment scheduling of any kind. Doctors’ offices, dentist offices, salons, and many other industries are in great need of online, on demand scheduling. This project proves that it is very possible to build this functionality with the above-mentioned tools at very low cost. All tools used were open source, except Dreamweaver.


Future Direction

Full integration of open source tools is critical in creating a seamless overall application. There are some ways in which WebCalendar was not integrated which I would like to pursue given more time.

The first is to automate WebCalendar account creation and login when those tasks are completed for the SalonBook application. This would require inserting a new Salon user into the appropriate WebCalendar database tables when a salon creates a SalonBook account. Of course, this would also ensure that the WebCalendar account information matches their SalonBook information. The same for login would be an important improvement. As it exists now, salons have to log in to the WebCalendar application additionally within the SalonBook system when they navigate to it via the Dashboard. Separate sign in is only required once per session though, as the login is stored throughout a session even as they go back and forth from WebCalendar to SalonBook. Even so, automated account creation and login are important integration features that would be next in line, given more time.

Also, a constraint of WebCalendar was that it only allowed a user to have one calendar. Ideally, a salon would have one calendar for each stylist. This could be achieved using an automated WebCalendar account creation script with a well-defined prefix naming system. Salons would be oblivious to what is actually going on behind the scenes, and simply be able to navigate to calendars that are tied to each stylist. When a salon would add a stylist, another WebCalendar account (and corresponding calendar) would be created, using a prefix of the stylist name and salon name. This would dramatically increase the power of the application as a schedule visualization and distribution tool. Availability would be more apparent, and schedules could be printed for each stylist at the start of the day.

Additionally, I would like to host the prototype online and run usability tests with surveys to gauge the effectiveness of the interface and functionality. This is a key step to perfecting the front-end interface and the functionality of the tools.

Finally, I would like to explore more dynamic, Web 2.0 tools like JavaScript, jQuery, and OpenLaszlo in more depth with the hope of making the interface richer for the user. Over the course of this project, I was able to get a small taste of these web development tools and their capabilities. They would allow me to give my application a more desktop software-feel on the web, with features like animation and drag-and drop. Unfortunately, I did not have enough time to fully explore them, and so that would be an addition improvement to consider going forward.

Sunday, April 19, 2009

UI Progress

The following use cases are now functional: User login/logout, user create account, user view/edit account information, salon search (with filters by name, city, or zip code), promotions search (with filters by salon name, salon city, salon zip, and by beauty or hair services being promoted/discounted), viewing user point total, salon login/logout, salon create account, salon view/edit account, salon add new stylist, salon remove stylist, salon add/remove services offered by each stylist, salon add/remove beauty services that they offer, salon create promotions, salon view promotions, salon edit promotions (promotion note, start date, expiration date, and services applicable), and salon view customer ratings of stylists at their salon.

Additionally, I've spent time editing the CSS of the page layout and the spry menu bars in order to get a better look and feel, even though the design is still fairly basic, as I have spent much more time wrestling with PHP and SQL queries trying to build the functionality. Here are some screenshots of the interface:

User Dashboard:

View/Edit Account Info:


Promotions Search/Filter:


One interesting problem that arose was the way to retrieve and pass information from the database to and from the client end. I designed my database to be flexible in the salon-stylist relationships it could represent. Essentially, my database is designed to allow for the possibility that a stylist belong to multiple salons, AND that they offer a different subset of services at each salon. I did this by having a many-to-one relationship table of salon/stylist/service so that while there will be multiple records for each salon, stylist, or service, each combination of stylist-salon-service is unique. While this is great for richness of information, it makes retrieval of information much more difficult. In order to allow salons to add/edit stylists and services they offer, I would have to be looking up each stylist and each service for that salon. The solution was this:


$colname_Stylist = "-1";
if (isset($_GET['id'])) {
  $colname_Stylist = $_GET['id'];
}
//find stylist in database from URL variable passed through link on previous page
mysql_select_db($database_test, $test);
$query_Stylist = sprintf("SELECT * FROM stylist WHERE id = %s", GetSQLValueString($colname_Stylist, "int"));
$Stylist = mysql_query($query_Stylist, $test) or die(mysql_error());
$row_Stylist = mysql_fetch_assoc($Stylist);
$totalRows_Stylist = mysql_num_rows($Stylist);


//check to see if stylist currently offers women's haircut
$colname_Services = "-1";
if (isset($_GET['id'])) {
  $colname_Services = $_GET['id'];
}
$womensCut = 1;
mysql_select_db($database_test, $test);
$query_Services = sprintf("SELECT * FROM salon_stylist_h_service WHERE `stylist id` = %s AND `salon id` = %s AND `hair service id` = %s" , GetSQLValueString($colname_Services, "int"),  GetSQLValueString($salonID, "int"),   GetSQLValueString($womensCut, "int"));
$Services = mysql_query($query_Services, $test) or die(mysql_error());
$row_Services = mysql_fetch_assoc($Services);
$totalRows_Services = mysql_num_rows($Services);

//check to see if stylist currently offers mens cut
$mensCut = 2;
mysql_select_db($database_test, $test);
$query_Services2 = sprintf("SELECT * FROM salon_stylist_h_service WHERE `stylist id` = %s AND `salon id` = %s AND `hair service id` = %s" , GetSQLValueString($colname_Services, "int"),  GetSQLValueString($row_Salon['id'], "int"),   GetSQLValueString($mensCut, "int"));
$Services2 = mysql_query($query_Services2, $test) or die(mysql_error());
$row_Services2 = mysql_fetch_assoc($Services2);
$totalRows_Services2 = mysql_num_rows($Services2);



//delete womens haircut script
$womensCut=1;
  $deleteSQL = sprintf("DELETE FROM salon_stylist_h_service WHERE `stylist id`=%s AND `salon id`=%s AND `hair service id`=%s",
                       GetSQLValueString($_GET['id'], "int"), GetSQLValueString($salonID, "int"), GetSQLValueString($womensCut, "int"));

  mysql_select_db($database_test, $test);
  $Result1 = mysql_query($deleteSQL, $test) or die(mysql_error());


//delete mens haircut script

$mensCut=2;
  $deleteSQL2 = sprintf("DELETE FROM salon_stylist_h_service WHERE `stylist id`=%s AND `salon id`=%s AND `hair service id`=%s",
                       GetSQLValueString($_GET['id'], "int"), GetSQLValueString($salonID, "int"), GetSQLValueString($mensCut, "int"));

  mysql_select_db($database_test, $test);
  $Result2 = mysql_query($deleteSQL2, $test) or die(mysql_error());

//insert script

//insert womens cut if checked
 if ($_POST["Services1"] == 1) { 
 $insertSQL = sprintf("INSERT INTO salon_stylist_h_service (`salon id`, `stylist id`, `hair service id`) VALUES (%s, %s, %s)",
                       GetSQLValueString($salonID, "int"),
                       GetSQLValueString($_GET['id'], "int"),
                      // GetSQLValueString(isset($_POST['Services1']) ? "true" : "", "defined","1","0"));
  GetSQLValueString($womensCut, "int"));

  mysql_select_db($database_test, $test);
  $Result1 = mysql_query($insertSQL, $test) or die(mysql_error());
 }
//insert mens cut if checked
if ($_POST["Services2"] == 2) { 
$insertSQL2 = sprintf("INSERT INTO salon_stylist_h_service (`salon id`, `stylist id`, `hair service id`) VALUES (%s, %s, %s)",
                       GetSQLValueString($salonID, "int"),
                       GetSQLValueString($_GET['id'], "int"),
                      // GetSQLValueString(isset($_POST['Services2']) ? "true" : "", "defined","1","0"));
GetSQLValueString($mensCut, "int"));

  mysql_select_db($database_test, $test);
  $Result2 = mysql_query($insertSQL2, $test) or die(mysql_error());
}
//go to:
$deleteGoTo = "Salon_Dashboard.php";
  if (isset($_SERVER['QUERY_STRING'])) {
    $deleteGoTo .= (strpos($deleteGoTo, '?')) ? "&" : "?";
    $deleteGoTo .= $_SERVER['QUERY_STRING'];
  }
  header(sprintf("Location: %s", $deleteGoTo));


Basically, what this script does is for the selected stylist (and logged in salon), populate the forms on the screen to the current state of the world. Then, once the form is submitted, delete all entries in the salon/stylist/service table. Then add new entries into this table for the stylist and salon for the selected services. It is a nice solution because it adds and deletes in batches, thus keeping related information together in the data tables as well, rather than checking and adding/removing each separately.

User viewing and filtering Promotions by Salon name, city, or zip code was also challenging. I could create a recordset that would pull all salons that matched the criteria of the name, city, zip code fields. I could also pull promotions from the database that matched the beauty/hair service filter. The problem was integrating the salon filter to the promotions filter. The promotions table only stores the salon id of the salon that created a given promotion. So I could not use the filters as direct search criteria into the promotions table. I had to use embedded do-while loops and a new array to store the results in order to later print to screen. Below is most of the code that populates the array with the proper promotions:

$colname_city = "-1";
if (isset($_POST['city'])) {
  $colname_city = $_POST['city'];
}
$colname_name = "-1";
if (isset($_POST['name'])) {
  $colname_name = $_POST['name'];
}
$colname_zip = "-1";
if (isset($_POST['zip'])) {
  $colname_zip = $_POST['zip'];
}
$name="Josh's Salon";
mysql_select_db($database_test, $test);
$query_Salons = sprintf("SELECT * FROM salon WHERE `zip code` = %s OR (`city id` = %s AND name LIKE %s) ORDER BY name DESC", GetSQLValueString($colname_zip, "int"), GetSQLValueString($colname_city, "int"), GetSQLValueString("%" . $colname_name . "%", "text"));
$Salons = mysql_query($query_Salons, $test) or die(mysql_error());
$row_Salons = mysql_fetch_assoc($Salons);
$totalRows_Salons = mysql_num_rows($Salons);

mysql_select_db($database_test, $test);
$query_HairServices = "SELECT * FROM `hair service`";
$HairServices = mysql_query($query_HairServices, $test) or die(mysql_error());
$row_HairServices = mysql_fetch_assoc($HairServices);
$totalRows_HairServices = mysql_num_rows($HairServices);

mysql_select_db($database_test, $test);
$query_BeautyServices = "SELECT * FROM `beauty service`";
$BeautyServices = mysql_query($query_BeautyServices, $test) or die(mysql_error());
$row_BeautyServices = mysql_fetch_assoc($BeautyServices);
$totalRows_BeautyServices = mysql_num_rows($BeautyServices);

$colname_beauty = "-1";
if (isset($_POST['beautyservice'])) {
  $colname_beauty = $_POST['beautyservice'];
}

$colname_hair = "-1";
if (isset($_POST['hairservice'])) {
  $colname_hair = $_POST['hairservice'];
}


$date = date(y-m-d);
$promos = array();
$i=0;
do{
$thisID = $row_Salons['id'];
//$thisID = 2;
mysql_select_db($database_test, $test);
if($colname_hair == 0 && $colname_beauty == 0){
$query_Promotions = sprintf("SELECT * FROM Promotions WHERE `salon id` = %s ORDER BY `end date` ASC", GetSQLValueString($thisID, "int"));
}
if($colname_hair != 0 && $colname_beauty != 0){
$query_Promotions = sprintf("SELECT * FROM Promotions WHERE `salon id` = %s AND (`beauty service id` = %s  OR `hair service id` = %s) ORDER BY `end date` ASC", GetSQLValueString($thisID, "int"), GetSQLValueString($colname_beauty, "int"), GetSQLValueString($colname_hair, "int"));
}
if($colname_hair != 0 && $colname_beauty == 0){
$query_Promotions = sprintf("SELECT * FROM Promotions WHERE `salon id` = %s AND `hair service id` = %s ORDER BY `end date` ASC", GetSQLValueString($thisID, "int"), GetSQLValueString($colname_hair, "int"));
}
if($colname_hair == 0 && $colname_beauty != 0){
$query_Promotions = sprintf("SELECT * FROM Promotions WHERE `salon id` = %s AND `beauty service id` = %s ORDER BY `end date` ASC", GetSQLValueString($thisID, "int"), GetSQLValueString($colname_beauty, "int"));
}

$Promotions = mysql_query($query_Promotions, $test) or die(mysql_error());
$row_Promotions = mysql_fetch_assoc($Promotions);
$totalRows_Promotions = mysql_num_rows($Promotions);
do{
if($totalRows_Promotions > 0){
$promos[$i] = $row_Promotions;
$i++;
}
} while($row_Promotions = mysql_fetch_assoc($Promotions));


} while($row_Salons = mysql_fetch_assoc($Salons));


For each result in the salon recordset based on the name/city/zip filter, the script uses the salon id from each salon and queries the promotions table with the salon id, beauty service id, and hair service id. It's likely that this too will pull multiple matching promotions. So, now a second do-while loop puts each resulting promotion from this query into an array. Then the outside loop moves to the next resulting salon from the salon filters, uses that id and repeats the promotions search, and so on until the result sets are null.

The next big step is to add the functionality of actually scheduling appointments. I've been in a dialogue with Professor Nimeroff about the best way to do this. The goal is to use Google Calendars, but there are conceptual issues with authentication, calendar ownership, and limitations on embedded calendars that need to be worked out. The zend framework and gData libraries are very robust and allow me to create calendars, add events, remove events, query events, and set availability in a fairly straightforward manner. The problem is how and who to authenticate into google (behind the scenes) and also how to enable more than read-only embedded calendars so that the Google Calendars interface can be utilized.

Friday, April 3, 2009

User Interface

For the past two weeks I have shifted my efforts to the front-end interface of SalonBook. I installed Adobe Dreamweaver and Illustrator, and I have been using both to design the look and feel of the application.

I have been learning how to use Dreamweaver on the go, and it includes lots of helpful tools that can create database connection, session variables, and database queries. Of course, the built-in tools are not nearly advanced or complex enough to be sufficient for my needs, but thus far it has provided a good starting place and code infrastructure.

I struggled for a long time trying to figure out how to use session variables directly in SQL queries, but I was eventually able to figure out the syntax and method for accomplishing this.

For now, I am working on trying to build all of the functionality of the site with the exception of the scheduling aspect. This will likely prove to be the most challenging, from a conceptual standpoint as well as implementation.

The difficult, important questions that I am currently working through for the scheduling aspect include if/how to package multiple services into one appointment, how to assign appointments for hair and beauty when the stylist is not specified, and how to have appointment blocks pre-specified by the salons. Once I come up with solid answers to these fundamental questions, and after the rest of the UI is working, I will begin implementing Google calendars as a back-end appointment database and pulling that information forward on the client side of my site.

Sunday, March 22, 2009

ADODB Working and Interfaces

First and foremost, I was finally able to get ADODB working. Now I can connect to my database using adodb and access my database tables using adodb ActiveRecord. This is important because it abstracts away the messy and database-specific SQL code and allows me to use object-oriented programing. I've run multiple test scripts that add, edit, and remove items from tables using ActiveRecord, and they are all functioning properly.

After getting over this hump, I shifted
my focus to the other side of things...the interface. The first step in interface design is to layout a skeleton design for the critical web pages users will see. Thus far I've laid out the interface for the user dashboard and salon (manager) dashboard. In designing these interfaces, I came up with the idea of having a Facebook-style "newsfeed" for salons and users that will write relevant updates and news to the feed by pulling information out of the database based on date. I believe this feature will give my users a more rich, personalized experience. Also, the top navigation bar will feature mouse-over pull down menus so that the interface will be clean and minimal until a user seeks more information. Otherwise the interface will look cluttered. The other major interfaces will be the scheduling interface for the salon and for the user. For the salon, I will use the Google Calendars interface as well as their database functionality to present the full spectrum of calendar viewing and editing. For user scheduling, I will have a form that asks for the user to specify the neighborhood, date, time, service, and salon/stylist if applicable when searching for an appointment. I will display the results with my own interface rather than Google Calendars for this part. It will simply show the available salon(s) and the time(s) available at and near the requested time. One of the main purposes of designing the interfaces was to make sure that my database would be capturing all of the information my interface would need. Once I designed these skeleton interfaces, I realized I would need a "Promotions" table that kept track of new deals and sales salons decide to offer. This information would be needed for the news feed, which could then pull these deals out of the database based on relevance (location, dates, etc) and serve them to users on the news feed. Thus, I created a new table in my salonbook database to support this information.

Sunday, March 1, 2009

ADODB

ADODB is a database abstraction layer that allows developers to write portable code that is not tied down to one particular type of database. It also has an ActiveRecord feature which turns database rows into objects. That lets me work with object-oriented programming rather than tediously writing and rewriting MySQL queries. I've spent the last 10 days familiarizing myself with these tools and experimenting with them.

The first thing I did was research and learn to connect to my database with the tools built into PHP. I've successfully written a couple scripts that can connect to my database, create a PHP object, and perform an insert into the database, using the properties of that object as the field values for the table. One script looked like this:


class stylist{

public $first = 'not set';
public $last = 'not set';
public $email = 'not set';
public $password = 'not set';

function stylist($firstname, $lastname, $emailaddress, $pass){
$this->first = $firstname;
$this->last = $lastname;
$this->email = $emailaddress;
$this->password = $pass;

}

function insert(){
mysql_query("INSERT INTO stylist (first, last, email, password) 
VALUES ('$this->first', '$this->last', '$this->email', '$this->password')");
}
}

$con = mysql_connect("localhost","rjroth","aZsXdCf");
if (!$con)
  {
  die('Could not connect: ' . mysql_error());
  }
mysql_select_db("salonbook", $con);

//Create new stylist objects
$bob = new stylist('Bob', 'Vance', 'Bob@Bob', 'BV');
$jane = new stylist('Jane', 'Dance', 'Jane@Bob', 'JD');
//Call the function that executes the MySQL query
$bob->insert();
$jane->insert();

mysql_close($con);

?>


This was great, but because the insert() function uses a hard-coded MySQL query, this code would be useless if my data were moved to a different database. That's where ADODB comes in. Useful ADODB tutorials and examples were not easy to come by on the internet, but I was able to piece together a cursory understanding that allowed me to use ADODB to perform some simple scripts that can get and set data to MySQL. Here is one example of a script that SELECTS * from my "state" table and prints each element on a separate line and numbers them:


include("/Library/WebServer/Documents/adodb/adodb.inc.php");
 $db = NewADOConnection('mysql');
 $db->Connect("localhost", "rjroth", "aZsXdCf", "salonbook");
 $result = $db->Execute("SELECT * FROM state");
 if ($result === false) die("failed");  
 while (!$result->EOF) {
for ($i=0, $max=$result->FieldCount(); $i < $max; $i++)
  print $result->fields[$i].' ';
$result->MoveNext();
print "
\n";
 } 

?>


This is a step in the right direction, but it still doesn't save me much tedium as a coder and its only a small step in the direction of object-oriented coding. ADODB implements a version of ActiveRecord that can make the entire process of getting and setting data object-oriented. Unfortunately, there are even less examples of code that use this online. Here's the most helpful one I could find: 

http://phplens.com/lens/adodb/docs-active-record.htm

I tried to implement this and integrate it with my database. However, I was unable to get anything to work. When I ran the script no INSERTS would actually be made into database table, and thus far my debugging efforts have been futile. Below is the script that mirrors the example from the above link; it is designed to load a row from the "states" database into a newly created "state" object, and then as a test I want to print out the ID from the state that was just loaded:


include('/Library/WebServer/Documents/adodb/adodb.inc.php');
require_once('/Library/WebServer/Documents/adodb/adodb-active-record.php');


// create an object instance
// configure library for a MySQL connection
$db = NewADOConnection("mysql");

// open connection to database
$db->Connect("localhost", "rjroth", "aZsXdCf", "salonbook") or die("Unable to connect!");
echo 'hello world';
class state extends ADOdb_Active_Record
{
var $_table = 'state';
}

$state = new state();
$state->load("id=5");
echo $state->id;

?> 

Unfortunately, I can't get this to work so far. Hopefully, with help from Jeff, I will turn the corner this week and go on to build all the objects I will need to represent my database.

Wednesday, February 11, 2009

Database Tables!

After looking into options for database management interfaces, I found phpMyAdmin and was able to successfully download and integrate it with my Apache server. Using phpMyAdmin, I constructed a new database ("salonbook") and built all of the tables that I designed. The process was tedious but fairly straightforward.

I ran into some issues trying to figure out what exactly the different "indexing" options are. The different "index" options for a given field are: none, primary, unique, index, fulltext. I realize that for tables with unique id's, these are primary indexes into that table. However, the 'index' option is unclear to me. I'm hypothesizing based on research that it is appropriate for storing id's from other tables in a field, in order to link the data together. For now I used 'unique' for fields that should never have repeats appear in the table. One example would be email addresses for users; two users should never be able to have the same email address. I also looked into what 'fulltext' means, and I discerned that it allows you to search and sort data based on string matching. These indexing issues are important, and I plan on teasing out the details with Professor Nimeroff when I discuss this with him.

Another issue was whether to store "notes" as text types or as varchar types. I looked into the definitions and limitations on the two types, and they both have pros and cons. Varchars will truncate padding on the end of input, whereas text types will not do that. Text types have limited size, whereas varchars don't. Still unsure which I'm going to use though.

Date/Time types are also a mystery to me right now. Not sure which type to use between: date, time, datetime, timestamp. For now I used type 'datetime' for appointment times.

In order to avoid having sets or enums within tables, I established tables that feature many-to-one and many-to-many relationships. Examples are salon_stylist_h_service and salon_b_service. This way their may be many table entries with the same stylist and/or salon and/or service, but never a repeat of any combination of these three values. This relationship allows for much more flexibility in terms of the use cases that can be handled. With this design, for example, I can represent the circumstance where a stylist works at various salons, and that he offers a different set of services at each salon.

I'm not sure if I am or am not going to store hair appointment information. Google Calendars can provide me with equivalent back-end functionality. However, I built the tables for now, if nothing else to maintain customer history information (which may prove valuable to salons down the line).

Here are some screenshots of my database and data tables I built:

salonbook database and its tables:


"user" table and its fields:



"state" table (with all 50 states entered into the table):



"neighborhood" table (with a few familiar neighborhoods entered):


*Because I store the 'city id' in the neighborhood table, it has indirect knowledge of the city to which the neighborhood belongs, and the city knows its state. So because of this structure, a neighborhood knows what city and state its in without explicitly storing city or state name in the "neighborhood" table.

The next thing I need to look into and work on is setting up and learning how to integrate ADO DB. It'll play an integral link in passing information between my database and my interface.

Wednesday, February 4, 2009

The New Plan

After research, thought, and discussion with Professor Nimeroff, I've formulated a plan that shifts the focus of my work. 

The mere scheduling aspect of the site is not novel, and the management of scheduling data in my own database would be extremely tedious and time consuming. So, I decided to look into the offerings of various calendar toolkits already available on the web. It turns out Google Calendars has all of the functionality that I would need for the scheduling aspect of the site. Google Calendars supports creation of new calendars, adding and deleting events (including recurring events), and viewing the schedule. The API for this toolkit is openly available online and will prove to be very helpful in harnessing this technology towards my project.

http://code.google.com/apis/calendar/docs/2.0/developers_guide_protocol.html#AuthAuthSub

While this calendar application will be very useful, my project will extend far beyond the simplicity of a generic calendar application. SalonBook is more than just a scheduler, it is an online salon management tool and salon community. There will be three distinct interfaces customized for three distinct groups of users. Salons, stylists, and clients will all create accounts and experience SalonBook is a different way. Below are the "use cases" that my site will set out to handle, organized by user type:




After thinking through all of the use cases, I had to revisit my data tables in order to capture all of the data I would need. The use cases dictate what data you will need access to at what points in a user experience, and so as stepped through the data tables as dictated by the use cases, I found many deficiencies in my initial tables. In order to allow for more flexible relationships between salons, services, and stylists, I created tables with "many-to-one" relationships that could represent a wide array of cases without redundancies. I also decided to split "services" into two separate categories: hair services and beauty services. The two aspects are distinctly different when it comes to salon management and schedule organization. It became very messy to try to treat them as different instances of the same "service" object. Below is a screenshot of my new set of data tables as I've designed them so far:



Its clear from this that the scope and functionality of my website will go far beyond a generic scheduler. Google Calendars will assist in back-end management of only a small portion of the data being stored, managed, and queried. Building the three distinct user interfaces and linking all of these pieces and data together will be a substantial undertaking. Thus, Professor Nimeroff suggested Amazon Web Services as a potential way of offloading the grudge work of managing my own database. It turns out Amazon has a service called SimpleDB that is perfect for the needs of this project. SimpleDB allows developers to create and store data tables, then access and manipulate that data with simple function calls from my program.

http://aws.amazon.com/simpledb/

Using SimpleDB will allow me to focus my efforts on the interfaces and pull together a rich, cohesive, and novel application. To do this, I'm going to employ OpenLaszlo. It's syntax is relatively straightforward and is capable of deploying applications in a number of different formats, abstracting away gritty compatibility issues.

So, this is the new plan. The next steps from here are to finalize the data tables and use cases, and to create a features matrix with all the features I want for my application against all the various tools that already exist and are relevant to this project.