Document Home


Previous: Rest of the Record, and System Logging and Fail-Overs

The Rest of the participant_game Record


If you think about it a little, you'll likely come up with the items that have to be added to the record. First, I've made no allowance for changing innings. DUH! (Actually, I did know about that. As I've mentioned before, it is perfectly appropriate to assemble a clean structure and then make it do all the tricks you want it to perform. This should become a little more clear as I continue.) Second, and somewhat related to the first item, I've made no allowance for constructing the unique key that identifies each record in the participant_game table. As you'll see, since I've decided to build in support for multiple users scoring the same game, there are some additional considerations attendant to that that I'll have to work through. At least part of the moral of this little story is that this is always the way things go. While I purposefully left off changing the inning and thus constructing the key values in earlier incarnations of the script, the addition of multi-user capability places demands on those identifying elements that did not exist in earlier schemes. The flip side of the story is thgat if you pay attention to making the underlying execution model as clean as possible your ability to successfully incorporate application enhancements will be maximized. This is very much the same point I made when I was discussing database design. This is not coincidental, and is an element of the perspective that leads to a pure object-oriented approach in which an object, its characteristics, and the operations it performs are coupled. But that's kind of blue-sky philosophical stuff, and I'm digging a trench here. On a pragmatic level, my argument is, as always, that it is the nature of information systems to evolve. If you make the conceptual underpinnings of the various elements of the system as clean as possible, you will be able to accomodate the changes wrought by that evolution much more readily.


For the most part, adding these elements is largely mechanical in the sense that a major portion of the implementation effort involves keeping track of everything that is getting passed around between incarnations of the script. You'll see what I mean shortly ... in the next version of the script one of the things I'm going to do is look at ways to put most, if not all, of the hidden fields into a big container and pass that around, but we'll get to that when the times comes


If you recall, the original key column in the participant_game table was 10 characters in length, representing the concantenation of game_id, inning, and inning_order, and uniquely identifying each individual row in the table. That's obviously not going to work with multiple users, because there would be no way to distinguish which entry came from which user, and therefore no way to accurately represent the stream of entered records from individual users. (Actually, that's not quite true. I could read something from the environment and use that as an identifier as I discussed in another context a few sections ago, but many of the same arguments that led me to reject that strategy at that time would also apply here.) I gave very brief consideration to using the session_id in this fashion, as that would not require addtional elements to be introduced, but the downstream requirements for maintaining that scheme are significant enough to reject it out of hand. (For example, we could no longer simply periodically delete the session file. We'd have to make sure that the session_id was unique within any given game, and we'd probably want to somewhere maintain an association between the entry person and session_id.) It makes far more sense simply to have the user enter a user name, then use that in the key. This also provides basic support for user-based password authentication, which I'll be starting to adopt in coming iterations.


The exact manner in which the user name will be entered after this script is wrapped into an overall framework is something that I'll deal with as I build that framework, but it is quite possible that it will remain in the form I've implemented here. Remembering that we also have to capture the game_id for the game we are recording, I simply changed the sel_form subroutine to accept entry of those two elements, as shown in the code snippet below ...


		print	header("text/html"),
		('<html>');
		print ("<H1>User and Game Identification, and Entry Form Configuration</H1>");
		print ("<p><form>");
print ("Enter your Username (up to 5 characters):
<input type='text' name='user_name' width='5'><br>");
print ("Enter the Game ID for this game:
<input type='text' name='game_id' width='5'><br><br>");


The other two data elements required to make sure that each participant_game record is unique are, of course, inning and inning_order. The scalars holding the values for these elements are initialized in the get_session subroutine along with the $session_id and $pass scalars. $inning is initialized to the value '01' while $inning_order is initialized to '001'. You might ask, why initialize the scalars as characters ... why not just treat them as integers? If you look at the store subroutine and think about it for a bit you might see the reason. When a numeric value is concantenated onto a string the relative positon of the first significant digit of that value is always going to occupy the leftmost positon in the resultant string. This can cause problems when you are trying to put items into an appropriate order ... I wouldn't be surprised if you have noticed that if you were to name files by a convention in which the unique part of the name, to the left of the period, is a number you cannot sort that directory by the name of the file without having an order something like this ...

1.txt
11.txt
12.txt
.
.
.
19.txt
2.txt
21.txt
.
.
.


More likely than not, what you really want is something like this:

01.txt
02.txt
.
.
.
11.txt
12.txt
.
.
The common way of expressing what I'm doing here is zero-filling the left-hand portion of the string, also known as stuffing the left-hand portion with zeros. Now while there are tools for this type of thing, there is no point in creating a context in which you are forced to use them, and to avoid having to use them all you have to do is initialize the scalars as strings.


Once we have the values we need, the primary concern is moving them all around. Here, for example, is the section of the store subroutine in which I print hidden fields back to the browser ...

			$forms->hidden('action','enter'),
			$forms->hidden('num_forms',$num_forms),
			$forms->hidden("session_id",$session_id),
			$forms->hidden('pass',$pass),
			$forms->hidden('user_name',$user_name),
			$forms->hidden('game_id',$game_id),
			$forms->hidden('inning_order',$inning_order),
			$forms->hidden('inning',$inning),
			$forms->hidden('prev_inning',$prev_inning),
 

As you can see, we're getting to have quite a few of them, which leads to the desire to have a better way to move this stuff around. As I said, we'll get to that in the next version.


Now that I've captured the elements I need, the primary concern in managing the flow of these pages and the data entered onto them is making sure that the appropriate identifying information is written into the output file on each iteration of the script. While I'm passing all of the key information back and forth, of the elements comprising the value of the key; game_id, user_id, inning, and inning_order, in the context of a given entry session only inning and inning_order will change. The value of inning, of course, will change only occassionally, while inning_order will change with each line written.


Taking care of the inning value is relatively easy ... as you can see if you look at the first part of the get_session subroutine, I've simply added a text field to the top of the page on which game events are entered.

	print $forms->p("Inning:<input type='text' name='inning' width='2' value='$inning'>");
 


As long as an inning is on-going the individual entering records will simply keep entering records and submitting pages ... once the next inning starts the user will simply change the inning number and continue on. I can see a couple or three elements of this that I might want to enhance. First, there is no provision for protecting the format of the inning value. I've just gone on about sorts and all, and in this version the user can just type in anything they want and that will be recorded as the inning. Problems could arise from something as simple as the user recording "2" rather than "02" as the inning, or they could come from the potentially more malicious act of entering a wholly inappropriate value like "ABCD" for the inning, which would leave us unable to reconstruct the overall record of the game. For now, however, I'm going to leave that the way it is and in the next version I'll start discussing ways to control the user's input. Second, this structure provides no provision for distinquishing between half-innings, when the teams switch offensive and defensive roles. It would be easy enough to put in another element that would allow the user to switch between the top and the bottom halves of the inning, and that is probably what I'll do. However, a number of sports don't have the straightforward switch between offensive and defensive phases that baseball does, and in such contexts we would have to make sure that sufficient detail exists in the record to be able to infer that switch contexturally.


Managing the value of inning_order is more predictable and, since that value is handled by code, not subject to input, it is not all that difficult to deal with. Since it has to change with every record written, and the conditions that would require a reset of that value are visible at that point, its value is managed entirely within the context of the store subroutine. First, look at the way that the $inning_order scalar is incremented, in the while loop in which the records are written:

		while ($j <= $num_forms)	{ 
	

			if ($ec[$j])		{
				$k++;
				print $recs $game_id.'.'.$user_name.'.'.$inning.'.'.$inning_order."ZzZ".$game_id."ZzZ".$inning."ZzZ".$inning_order."ZzZ".$ec[$j]."ZzZ".$rc[$j]."ZzZ".$pc[$j]."ZzZ".$erc[$j]."ZzZ".$et[$j]."ZzZ\n";
				$inning_order++;
			}
		$j++;
		}



After each line is written to the output file, the value of $inning_order is incremented by one, and that value is written in the next pass through the loop. If there are no more records to be written in this iteration of the script, the value of the scalar is returned in a hidden field. After another set of records are entered and submitted, the value of inning_order is retrieved from the CGI object and incremented from there. When, however, the user has changed the inning value,


		if ($inning != $prev_inning)  {
			
			$inning_order='001';
			$forms->delete('prev_inning');
			$prev_inning=$inning;
			
		}
				

the if condition that checks the value of $inning against $prev_inning will be triggered, which will reset $inning_order to "001", and set the value of $prev_inning to the current value of $inning so the condition will not be triggered until the $inning value has again been changed.


That's pretty much all that has to be done to get the flow of the entry screens to handle unique users and deal with innings and the order of records within innings. Now all this script has to do is save the records in a form that incorporates the added fields. If you take a look at the store subroutine you'll probably be able to discern what I did. To be able to readily handle the key as a value in its own right and to extract the elements of which it is comprised as independent values I've seperated those components with periods ("."). As a result, in the first split of the insert_recs() subroutine, which you may recall uses "ZzZ" as a delimiter, the key field is returned as one string which is then split into the component values using the period delimiter, after which the values for those columns are stored independently. It may occur to you that this implies some degree of duplication in what is stored, and you'd be right. Each of the columns in the key are, in effect, stored twice, once in the key and once independently. I made this choice because processing power is at a relatively higher premium within the cluster than storage space. Since the duplication requires 15 bytes extra storage per record, and it would be reasonable to estimate two hundred records per inning, the duplication would add approximately 30 Kb to the database each game. Against this I balanced the fact that each extraction or concantenation operation performed as a query is run requires the execution of additional instructions, and as a result decided to use the space. Realistically, this is not all that big a deal, but does illustrate the kind of trade-offs that can be made to optimize the performance of a system in any given context. One ot two tweaks like this do not make all that much difference, but the aggregate performance increase from five or ten such tweaks can be substantial.



Next - System Logging and Fail-Overs