Document Home


Previous Maintaining the League Schedules
Previous Displaying Games Within a Month

Samples for this section
Modifying Schedule Entries





At one time I had given serious thought to implementing the actual maintenance of schedule records through the use of child windows that would have been generated on the selection of a hot spot located in the table element for a given date. While this sounds cool, there are some serious limitations to such an implementation in the absence of the installation of custom libraries on the client machine. The basic scripting capability built into virtually all browsers is in a language known as javascript, with the Microsoft implementation called jscript. A more precise name, however, is ecmascript, because all of these scripting engines strive to implement the language standard promulgated by the European Computer Manufacturers Association.


The problem with javascript in this context, however, is that spawning popup windows that can incorporate a reasonable amount of functionality would require a substantial investment of time in a path that, at least at this point, has limited relevance for the environment here. In general, these tools are intended to address the constraints imposed by relatively low-bandwidth network connections by creating an ephemeral environment on the client in which some level of application logic can be embedded, thus alowing the client browser to perform certain actions without requiring a round-trip to the server. In practice, even in more sophisticated implementations, the applicability of these tools is frequently limited to relatively trivial form-checking logic, such as making sure that key fields are filled in, and to what might be thought of as overlays that allow the display characteristics under which pages are rendered to be modified. While it is possible to implement substantially more functionality in javascript, most seem to prefer more heavy-duty tools if their requirements warrant such functionality in the first place. I may well implement some javascript functions in an error-checking mode in one of the upcoming sections, but at this point I doubt that any javascript incorporated into this application will be more extensive than that.


Once I had dropped the notion of using javascript to accomplish elements of the functionality involved with editing the schedule, I had to decide on the manner in which to do that with the generated pages. On reflection, it seemed to me that schedule maintenance is likely to be intensive as the slate of games is being populated, with only incidental use following schedule creation. Therefore, I decided to make it possible to add games to a number of days on the calendar without having to jump back and forth between pages to select days. I therefore decided to incorporate a mechanism by which a group of days could be selected on the generated calendar month, and use that set of selected days as the basis for a generated page on which the schedules for those days could be modified.


The first step is therefore to enable date selection within a given month. This takes place in the subroutine I named date_test() in the previous chapter, but here I will change its name to calendar_month(), just to head off those readers who might be muttering about the fact that I got done testing it in the last chapter <grin>. As I ultimately plan to call the schedule display subroutine from both the league maintenance area, requiring authentication, and the non-authenticated public area, it seems appropriate to generate the page appropriately to the context in which it is generated. Given that the request object will contain the authentication information if the request is coming from within the league path, I can easily determine that status. I avoid the necessity of a dding the @user_settings array to the argument list for this subroutine simply by making a call to the connection object. If the user name is there, I start a form that will encompass the data elements that will be used.

	my $user_name=$$r->connection->user;
	$$r->print("<form action='/bb_app/league/la-sched-edit' method='post'>") if $user_name;
I initially used the connection object here simply because I was too lazy to go back to BB_STACKED and make the appropriate changes before I could test the modifications I was making to the subroutine that generates the calendar. I have left it this way simply because at this point it seems to me that the very slight overhead associated with making the call to the connection object is offset by removing an argument from the subroutine call. If I were accessing more of the material I have stored in @user_settings I would not be doing it this way, but in this context I do not have any problem in letting it stand as it is. Also worthy of note in the snippet above is the form submittal method specified in the form tag. The post method puts submitteed data below the request header rather than in the URI as is the default with forms submitted with the get method. While the data thus submitted is less visible this way, it is not inherently less vulnerable to being successfully hacked; just less vulnerable to being trivially hacked. The real advantage of the post method lies in its ability to handle large chunks of data. Although I have not had any such problems in this application, I probably should have started using the post method when I implemented the game event edit functionality. Reflection on the potential amount of information being passed from a page of a month's worth of dates each holding potentially multiple games should indicate why it is considered advisable here.


The incorporation of a selection process within this environment is accomplished by constructing a small table within the table element for any specific day if the session is authenticated. The table holds the day of the month and the radio widgets that toggle the selected status of the date.

			$$r->print("<td><table width='8%' border='1' bgcolor='white'><td><td><font color='red' size='5'>$j</font>:</td>");
			$$r->print("<td>Yes<input type='radio' name=$c_day value='yes'></td><td>No<input type='radio' name=$c_day value='no' checked></td></tr></table>");
This generates a page that looks like this:


Generation of the page through a path not requiring authentication, however, will lead to the same calendar month displayed previously.


The strategy employed here for naming parameters is similar to that employed in the edit process, but here the suffix used to distinquish between similarly-named parameters is keyed to $j, the day of month counter.

			$c_day='day_'.$j;
			$c_home='home_'.$j;
			$c_away='away_'.$j;
			$c_time='time_'.$j;
			$c_game='game_'.$j;
Within the loop in which the games for a given day are printed, the values pertinent to display and identification of individual games are printed as hidden fields.
						if ($user_name)	{
							
							$$r->print("<input type='hidden' name=$c_home value=@$row[6]>");
							$$r->print("<input type='hidden' name=$c_away value=@$row[7]>");
							$$r->print("<input type='hidden' name=$c_time value=@$row[8]>");
							$$r->print("<input type='hidden' name=$c_game value=@$row[5]>");
					}
As in the previous context, the series of "day_" parameters will be single-valued, while the other parameters will hold the same number of values as there are games currently in the database for that date. That structure, of course, will determine the manner in which the parameters will be processed, as will shortly be apparent. The submit button at the end of the form includes the value "Edit Selected Dates".
$$r->print("<input type='submit' name='submit' value='Edit Selected Dates'></form>");


On submission of this page, the server will be generating a response to a request object with the string "la-sched-edit" in the path information. Before I get too deeply into what is going to happen as a result, however, I need to deal with reading information submitted with the post method. For submissions like the one here, those parameters can be read from the pertinent part of the submission (called the client block) through the use of the content method. (This method will not work with other types of post submittals, such as file uploads, but that is nor pertinent here.) In this circumstance, I can easily incorporate the new form of the submission method into the parameter processing scheme by populating the @args array with the output of both the args and the content methods, as in:

	my @args=($r->args,$r->content);
Now the %params hash of arrays will be appropriately constructed with information submitted by both get and post methods.


Within the small block of code in the league() subroutine executed if the string in $path contains "sched", the additional presence of the string "edit" will now lead to the execution of the subroutine I have named sched_edit().

	if ($path=~/sched/)	{
		
		if ($path=~/edit/)	{
			
			sched_edit($r,$db,$colors,$params,@select);
			
		}
	
		elsif ($path !~ /edit/)	{
				
			calendar_month($r,$db,$colors,$params,@select);
		}
	}
As before, my initial step was to make sure that the basic scheme for accessing those parameters was working in the manner that I had envisioned. In this initial version of the subroutine I use the scalar $c_day to hold the assembled string that represents the name of the parameter that holds the selected status for the specific day of the month held in $j. In effect, this is the mirror image of the manner in which I construct the parameter as the previous page is built. That construction takes place within a loop that iterates over the range of integer values from 1 through 31, representing the days in the month. In effect, I am at this point simply scanning for dates that have been selected, in which case the scalar that has the name held in $c_day will have the value "yes".


Once a selected date has been found, a small widget will be drawn that will be used to modify/enter schedule information for that date. On this first cut, however, I just wanted to be sure that what was in place was operating correctly so I printed the pertinent informatiopn back to the browser. As I mentioned a few paragraphs ago, most of the parameters associated with a given date are of variable length, but other than the "day_" parameters the arrays that hold the values for game records have a common number of elements. In other words, I have a home team, an away team, a game time, and the rest of the record for each of the games that were printed into a given date that was selected on the calendar month page. Therefore, I can use the elements in any of these arrays as a control for a loop over the games held in the schedule for that date. As I have done before, I use a loop counter to track the position of the array elements currently being processed and assemble the set of elements occupying the same position as the pertinent elements for a given game. As an example, if I were to select August 1st, 5th, and 8th of 2003 from my sample schedule database and click "Edit Selected Dates", the following page would be generated.



That, of course, indicates that everything is working appropriately.


I am going to incorporate those elements generally considered part of both entry and editing on the single page generated once the pertinent dates are selected. Such an approach is viable here both because the number of records associated with a given date is limited, and because there are relatively few data elements that determine the characteristics of a schedule game. The generated page will therefore incorporate the ability to add, delete, and modify records on one generation of the page. Personally, I would hesitate before implementing a similar interface to the edit page for game events, simply because there are many more records for a given half-inning and the order of the records is itself part of the information. The schedule of games for a day or set of days is, however, much more easily visualized, and from that standpoint I think it is much more amenable to such treatment.


As shown above, the key details of games already schedule are written to the calendar month page as hidden fields. On this page, I am going to group games by the day of the month, and display that date prominently to minimize the potential for confusion. While the date for which the game is scheduled is part of the game table, I do not pass that as one of the hidden fields, but rather reconstruct the date by printing the month and year values that were used to generate the calendar month as hidden fields:

		if ($user_name)	{
		
		$$r->print("<form action='/bb_app/league/la-sched-edit' method='post'>");
		$$r->print("<input type='hidden' name=year value=$year>");
		$$r->print("<input type='hidden' name=month value=$month>");
	}  
and combining them with the day counter ($j) to construct whatever date representation I wish.


Adding capability to the structure I put into place above, my next step was to generate a page that would allow the modification or deletion of the records already entered into the schedule. Whenever a "day_" parameter holds "yes", I now print the date and a table holding the array values for the parameters with the relevant value of $j; one row for each postion in those arrays, thus one row for each game stored in the database for that day. Each of these rows is comprised of a radio widget to mark the record for deletion, select boxes for the home and away teams, and a text widget for the start time.

$$r->print("<td>Select</td><td>to Delete</td><td>Home</td><td>Away</td><td>Time</td></tr>");
foreach my $t (@{$$params{'home_'.$j}})	{
				
	$delete_sel='del_'.$$params{'game_'.$j}[$k];
	$$r->print("<td>Yes<input type='radio' name=$delete_sel value='yes'></td><td>No<input type='radio' name=$delete_sel value='no' checked></td><td>");
	sel_box($select[6],$r,'home','','',$$params{'home_'.$j}[$k]);
	$$r->print("</td><td>");
	sel_box($select[6],$r,'away','','',$$params{'away_'.$j}[$k]);
	$$r->print("</td><td>");
	$$r->print("<input type='text' name='' value=$$params{'time_'.$j}[$k]></td><td>");
	$$r->print("<input type='hidden' name='game' value=$$params{'game_'.$j}[$k]></td></tr>");
	$k++;
}
The default value (the final argument passed to sel_box(), holding the value to be flagged as the default in the generated list) is the pertinent element from the array associated with that parameter. For this to work, of course, I must create a hash of team names and ID's that can be used by sel_box() to construct the select boxes. Therefore I have added the creation of such a hash to the main section of BB_STACKED
my $team_set=$dbh->prepare("select team_name,team_id from team"); 
$team_set->execute();
my $team=make_hash($team_set);
and added the defined reference as the seventh element in the %select array.
my @select=($events,$role,$participants,$result,$year,$month,$team);
Hence the $select[6] in the calls to sel_box() in the snippet above.


This produces a page that looks like this:




This is fine for records that have already been entered, but how would I go about adding games to a given date? For that matter, how can I go about making sure that a given date is not overbooked. Obviously, virtually any association of teams is going to have some level of constraint on the number of games that can be played on a given date. (In many cases schedules are further constrained by the need for shared venues, as when a league plays its games in a park or complex of fields. My implicit assumption that each team has its own facility removes that constraint here, but this would not be a difficult thing to build in, primarily requiring only thje addition of a venue column in the games table and appropriate selection mechanisms.) My suspicion is that the most common constraint under which most schedules would operate would be a limitation on the number of games scheduled by the day of the week. For the purposes of this illustration, I have established a limit of three games for Monday through Thursday of any given week, and of five games for Friday, Saturday, and Sunday.


The task of drawing an interface through which the schedule for any given date can be maintained is now a matter of drawing the table rows described above for games already in the system, and then appending to the table sufficient "blank" rows to represent the maximum number of games that can be scheduled for that date. In other words, if a Wednesday has one game stored when the page is generated, the table for that date will include one existing record and two "blank" rows into which games can be entered. While not all that difficult, the process I am going to follow is the kind of thing that can get a little confusing unless its overall structure is clear. Therefore, I put together the illustration to the right to aid in conceptualizing what is going on here. As the illustration indicates, the core logic within this subroutine is within a while() loop that executes as long as the integer used to track the day of the month is less than or equal to the value 31. Within that loop, nothing significant happens unless the parameter that holds the selection status of the day holds the value "yes". If that is so, the day of the week for the date represented by that counter is used to establish the maximum number of games that can be scheduled for that day. Following that, the elements in one of the arrays associated with the multi-valued parameters is used to control a foreach loop that prints the games that have been stored in the database for that date. As each of these games is printed a counter is incremented that serves both to count the number of games that have been printed and to track the position of the array elements being processed. After the existing records have been printed, a series of blank records is printed until the maximum number of games is reached, at which point the table of games is closed. The outer structure of the subroutine would then execute until another selected date was found or the day counter passed the value 31.



The code that implements this functionality follows:

sub sched_edit	{
	
	my ($r,$db,$colors,$params,@select)=@_;

	#initialize day counter
	my $j=1;

	my ($c_day,$max_games);

	#start outer table to serve as frame
	$$r->print("<br><center><table width='75%' border='2'><tr><td>");	

	
	while ($j <= 31) {
	
		##this scalar holds the name of the parameter flagging the selection status for the day represented by the day counter	
		$c_day = 'day_'.$j;
		
		##the presence of the value 'yes' indicates that the date was selected 
		if ($$params{$c_day}[0]=~/yes/)	{
			
			##initialize position/game counter
			my $k=0;
			
			$$r->print('<br><br>');

			##this scalar will play a role analagous to $c_day, in this case holding
the name of the parameter that will flag a record for deletion my $delete_sel; ##print a table holding the selected date $$r->print("<br><center><table border='1' $colors><tr><td><b>
$$params{'month'}[0] - $j - $$params{'year'}[0]>/b></td></tr></table><br>"); ##i passed the year and month from the previous screen as hidden fields, here they are passed as
arguments to the Day_of_Week function ## from Date::Calc my $dow=Day_of_Week($$params{'year'}[0],$$params{'month'}[0],$j); ##if the value in the $dow scalar is greater than 4 (later than thursday) if ($dow > 4) { $max_games=5; } ##otherwise, lower value elsif ($dow < 5) { $max_games=3; } ##start the table holding the day's records $$r->print("<center><table border='1' $colors><tr>"); ##table header row $$r->print("<td>Select</td><td>to Delete</td><td>Home</td>
<td>Away</td><td>Time</td></tr>"); ##for each element in the array holding the values of the home team parameter for that day foreach my $t (@{$$params{'home_'.$j}}) { ##construct the name of the parameter holding the deletion status. this name will be the concantenation of the string 'del_' ##with the game_id for the pertinent game $delete_sel='del_'.$$params{'game_'.$j}[$k]; ##radio widget for deletion selection $$r->print("<td>Yes<input type='radio' name=$delete_sel value='yes'></td><td>No
<input type='radio' name=$delete_sel value='no' checked></td><td>"); ##print select boxes with home and visiting teams, accessing the stored values from their locations in the relevant array and the ##pertinent hash of names and team_id's from its location in the array referenced by $select sel_box($select[6],$r,'home','','',$$params{'home_'.$j}[$k]); $$r->print("</td><td>"); sel_box($select[6],$r,'away','','',$$params{'away_'.$j}[$k]); $$r->print("</td><td>"); ##similarly for game time sel_box($select[7],$r,'time','','',$$params{'time_'.$j}[$k]); $$r->print("</td></tr>"); $k++; } while ($k < $max_games) { ##print a blank record. note that the final argument passes one whitespace character (space) as the default selection ##this is required for the modified operation of the sel_box() subroutine $$r->print("<tr><td></td><td></td><td>"); sel_box($select[6],$r,'home','','',' '); $$r->print("</td><td>"); sel_box($select[6],$r,'away','','',' '); $$r->print("</td><td>"); sel_box($select[7],$r,'time','','',' '); $$r->print("</td></tr>"); $k++; } ##end the table framing game records $$r->print('<br></table></center>'); } ##increment day counter $j++; } ##close frame table $$r->print('<br><br></table></center>'); }

Given the description above and the comments I have included in the code, I will not discuss the details of the implementation extensively here. There are, however, a couple of aspects of the implementation that are worthy of mention. First, I added the creation of a hash to hold start times to the main section of BB_STACKED, and added a reference to that hash to the $select array.
my %full_times=("  "=>'  ',
		"a - 12:00 P.M."=>'12:00',
            "b - 12:30 P.M."=>'12:30',
            "c - 1:00 P.M."=>'13:00',
            "d - 1:30 P.M."=>'13:30',
            "e - 2:00 P.M."=>'14:00',
            "f - 2:30 P.M."=>'14:30',
            "g - 3:00 P.M."=>'15:00',
            "h - 3:30 P.M."=>'15:30',
            "i - 4:00 P.M."=>'16:00',
            "j - 4:30 P.M."=>'16:30',
            "k - 5:00 P.M."=>'17:00',
            'l - 5:30 P.M.'=>'17:30',
            'm - 6:00 P.M.'=>'18:00',
		'n - 6:30 P.M.'=>'18:30',
            'o - 7:00 P.M.'=>'19:00',
            'p - 7:30 P.M.'=>'19:30',
            'q - 8:00 P.M.'=>'20:00',
            'r - 8:30 P.M.'=>'20:30',
            's - 9:00 P.M.'=>'21:00',
            't - 9:30 P.M.'=>'21:30',
            'u - 10:00 P.M.'=>'22:00'
            );
    
This is important both for useability and for data quality, because users would likely find the act of entering game times using a 24 hour clock to be cumbersome and prone to error. The format of time values, however, introduced a problem in the operation of the section of sel_box() that flags the selected value. The source of this problem laid in the nature of the comparison that established the selected item in the list of items in the select box. In the other contexts in which I have compared values in this subroutine those values have been integers: the values of the codes stored in the database. Therefore, the form of the comparison:
 ($v == $default)
was perfectly appropriate. Here, however, that comparison will generally result in two of the values being flagged as selected, as the comparison matches the two values that begin with a given hour and ignores the portion of the string beginning with the first colon. Rather than clunking the statement up with the requisite steps to make sure that the components of the comparison are of the appropriate form, I switched the comparison to a pattern match. The new form of the part of sel_box() that constructs the item list is now
       foreach $k(sort keys %$hash)	{
          
		          $v=$hash->{$k};
				   if ($v !~/^$default/)	{
		         	  
				   $$forms->print("<option value=$v>$k</option>");
					   
	           	}
	           	   elsif ($v =~/^$default/)	{
		           	   
		           	   $$forms->print("<option selected value=$v>$k</option>");
	           	}    	
	    }
The most important thing to note about the pattern matches in the if() and elsif() statements is that the match is anchored to the beginning of the string with the caret (^) character. This will help to prevent inadvertent matches with partial strings. While such matches should not be a problem, given that virtually all of the entry within the system is from select box selections, a network glitch could lead to an incomplete object being returned to the server, it is generally a good idea to build in safeguards, especially when they are as easy to implement as is this.


Modification of sel_box() to incorporate a pattern match also requires a slight modification to the manner in which sel_box() is called. In previous versions, I simply passed a null value to the subroutine, resulting in a failed equality test. Without special arguments, a pattern match will always return success when a pattern is matched to an undefined value, which is what the $default scalar in sel_box() will hold after it tries to read an array element that holds nothing. As a result, the generated page will include select boxes with every item flagged as selected. As always, there are a number of ways in which I can work around this, among them being modifications to the pattern match itself. The approach I adopted is probably the most simple, and has the advantage of not constraining the applicability of the pattern match, as would be the case if I were to modify the form of the match to apply to this situation. Given that I make sure that the sources of values for these select tables have blank elements to prevent the inadvertent submittal of values when none had been intentionally selected, I can use those records as the pattern to match. That is the reason I now pass a whitespace character to the subroutine, resulting in a match with those blank values. This now results in a page that looks like this:



which is what I am after.


There are, however, just a couple of loose ends to tie up before I move along. First, I do not like the way the controls are situated on the calendar month page, as in the illustration to the right. I personally have clicked the wrong one a couple of times, and I am the one who put them there. Separating the controls was not a big deal; I moved the month selection widget to a spot on the page above the month, and put it inside a table of its own to provide a degree of visual differentiation from the rest of the page. As this widget displays the month and year of the calendar month drawn on the page, it also provides more immediate confirmation of the month being viewed to the user. As display of a month with a heavy schedule of games will extend the length of the page considerably, this is a particularly nice feature.

The code below generates the current version of this widget, and within the calendar_month() subroutine is now located immediately above the generation of the month.

	$$r->print("<br><br><center><table width='40%' border='1' $colors><tr><td><br>"); 
	$$r->print("<form action='/bb_app/league/la-sched'>");
	$$r->print("<center><table width='15%' border='2' $colors><tr><td>");
	sel_box($select[5],$r,'month','','',$month);
	$$r->print("</td><td>");
	sel_box($select[4],$r,'year','','',$year);
	$$r->print("</td></tr></table>");
	$$r->print("<br><center><input type='submit' name='submit' value='Select Month'>/center></form></td></tr></table>");
In a similar vein I have placed the submit button that submits the selections made to a calendar month within a small table below the month.
$$r->print("<center><table border='1' width='25%' $colors><tr><td><center><input type='submit' name='submit'
value='Edit Selected Dates'></center></td></tr></table></center></form>");
The result is a page on which the controls are much more clearly delineated.



Finally, I must put the items on the schedule edit page into an html form and incorporate some capability to submit that form. As in previous instances in which I have submitted a page of material for storage, I include the string "la-store" in the form action.

	$$r->print("<form action='/bb_app/league/la-store' method='post'>");
The submit button at the end of the form
$$r->print("<br><br><input type='submit' name='submit' value='Submit Schedule Modifications'></form>");
includes the value "Submit Schedule Modifications". This value, in concert with the specified form action, will of course determine how the server will process the submitted material.


And that, of course, is the subject of the next chapter.



Next- Processing Schedule Modifications