[system] / branches / rel-2-1-a1 / webwork-modperl / lib / WeBWorK / DB.pm Repository:
ViewVC logotype

Annotation of /branches/rel-2-1-a1/webwork-modperl/lib/WeBWorK/DB.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1589 - (view) (download) (as text)
Original Path: trunk/webwork-modperl/lib/WeBWorK/DB.pm

1 : sh002i 775 ################################################################################
2 :     # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project
3 :     # $Id$
4 :     ################################################################################
5 :    
6 :     package WeBWorK::DB;
7 :    
8 :     =head1 NAME
9 :    
10 : sh002i 956 WeBWorK::DB - interface with the WeBWorK databases.
11 : sh002i 775
12 : sh002i 1012 =head1 SYNOPSIS
13 :    
14 :     my $db = WeBWorK::DB->new($courseEnvironment);
15 :    
16 :     my @userIDs = $db->listUsers();
17 :     my $Sam = $db->{user}->{record}->new();
18 :    
19 :     $Sam->user_id("sammy");
20 :     $Sam->first_name("Sam");
21 :     $Sam->last_name("Hathaway");
22 :     # etc.
23 :    
24 :     $db->addUser($User);
25 :     my $Dennis = $db->getUser("dennis");
26 :     $Dennis->status("C");
27 :     $db->putUser->($Dennis);
28 :    
29 :     $db->deleteUser("sammy");
30 :    
31 : sh002i 956 =head1 DESCRIPTION
32 :    
33 :     WeBWorK::DB provides a consistent interface to a number of database backends.
34 :     Access and modification functions are provided for each logical table used by
35 :     the webwork system. The particular backend ("schema" and "driver"), record
36 : sh002i 1012 class, data source, and additional parameters are specified by the C<%dbLayout>
37 : sh002i 956 hash in the course environment.
38 :    
39 :     =head1 ARCHITECTURE
40 :    
41 :     The new database system uses a three-tier architecture to insulate each layer
42 :     from the adjacent layers.
43 :    
44 :     =head2 Top Layer: DB
45 :    
46 :     The top layer of the architecture is the DB module. It provides the methods
47 :     listed below, and uses schema modules (via tables) to implement those methods.
48 :    
49 : sh002i 1568 / new* list* exists* add* get* get*s put* delete* \ <- api
50 : sh002i 956 +------------------------------------------------------------------+
51 :     | DB |
52 :     +------------------------------------------------------------------+
53 :     \ password permission key user set set_user problem problem_user / <- tables
54 :    
55 :     =head2 Middle Layer: Schemas
56 :    
57 :     The middle layer of the architecture is provided by one or more schema modules.
58 :     They are called "schema" modules because they control the structure of the data
59 :     for a table. This includes odd things like the way multiple tables are encoded
60 : sh002i 1012 in a single hash in the WW1Hash schema, and the encoding scheme used.
61 : sh002i 956
62 :     The schema modules provide an API that matches the requirements of the DB
63 :     layer, on a per-table basis. Each schema module has a style that determines
64 :     which drivers it can interface with. For example, WW1Hash is a "hash" style
65 :     schema. SQL is a "dbi" style schema.
66 :    
67 :     =head3 Examples
68 :    
69 :     Both WeBWorK 1.x and 2.x courses use:
70 :    
71 :     / password permission key \ / user \ <- tables provided
72 :     +-----------------------------+ +----------------+
73 :     | Auth1Hash | | Classlist1Hash |
74 :     +-----------------------------+ +----------------+
75 :     \ hash / \ hash / <- driver style required
76 :    
77 :     WeBWorK 1.x courses also use:
78 :    
79 :     / set_user problem_user \ / set problem \
80 :     +-------------------------+ +---------------------+
81 :     | WW1Hash | | GlobalTableEmulator |
82 :     +-------------------------+ +---------------------+
83 :     \ hash / \ null /
84 :    
85 :     The GlobalTableEmulator schema emulates the global set and problem tables using
86 :     data from the set_user and problem_user tables.
87 :    
88 :     WeBWorK 2.x courses also use:
89 :    
90 :     / set set_user problem problem_user \
91 :     +-------------------------------------+
92 :     | WW2Hash |
93 :     +-------------------------------------+
94 :     \ hash /
95 :    
96 :     =head2 Bottom Layer: Drivers
97 :    
98 :     Driver modules implement a style for a schema. They provide physical access to
99 :     a data source containing the data for a table. The style of a driver determines
100 : sh002i 1012 what methods it provides. All drivers provide C<connect(MODE)> and
101 :     C<disconnect()> methods. A hash style driver provides a C<hash()> method which
102 :     returns the tied hash. A dbi style driver provides a C<handle()> method which
103 :     returns the DBI handle.
104 : sh002i 956
105 :     =head3 Examples
106 :    
107 :     / hash \ / hash \ / hash \ <- style
108 :     +--------+ +--------+ +--------+
109 :     | DB | | GDBM | | DB3 |
110 :     +--------+ +--------+ +--------+
111 :    
112 :     / dbi \ / ldap \
113 :     +-------+ +--------+
114 :     | SQL | | LDAP |
115 :     +-------+ +--------+
116 :    
117 : sh002i 1012 =head2 Record Types
118 :    
119 :     In C<%dblayout>, each table is assigned a record class, used for passing
120 :     complete records to and from the database. The default record classes are
121 :     subclasses of the WeBWorK::DB::Record class, and are named as follows: User,
122 :     Password, PermissionLevel, Key, Set, UserSet, Problem, UserProblem. In the
123 :     following documentation, a reference the the record class for a table means the
124 :     record class currently defined for that table in C<%dbLayout>.
125 :    
126 : sh002i 775 =cut
127 :    
128 :     use strict;
129 :     use warnings;
130 : sh002i 1096 use Carp;
131 : sh002i 904 use Data::Dumper;
132 : sh002i 1226 use WeBWorK::Timing;
133 : sh002i 775 use WeBWorK::Utils qw(runtime_use);
134 :    
135 :     ################################################################################
136 :     # constructor
137 :     ################################################################################
138 :    
139 : sh002i 956 =head1 CONSTRUCTOR
140 : sh002i 1012
141 : sh002i 956 =over
142 :    
143 : sh002i 1012 =item new($ce)
144 : sh002i 956
145 : sh002i 1012 The C<new> method creates a DB object and brings up the underlying
146 : sh002i 1035 schema/driver structure according to the C<%dbLayout> hash in C<$ce>, a
147 : sh002i 1012 WeBWorK::CourseEnvironment object.
148 :    
149 :     =back
150 :    
151 : sh002i 1108 =head2 C<%dbLayout> Format
152 :    
153 :     The C<%dbLayout> hash consists of items keyed by table names. The value of each
154 :     item is a reference to a hash containing the following items:
155 :    
156 :     =over
157 :    
158 :     =item record
159 :    
160 :     The name of a perl module to use for representing the data in a record.
161 :    
162 :     =item schema
163 :    
164 :     The name of a perl module to use for access to the table.
165 :    
166 :     =item driver
167 :    
168 :     The name of a perl module to use for access to the data source.
169 :    
170 :     =item source
171 :    
172 :     The location of the data source that should be used by the driver module.
173 :     Depending on the driver, this may be a path, a url, or a DBI spec.
174 :    
175 :     =item params
176 :    
177 :     A reference to a hash containing extra information needed by the schema. Some
178 :     schemas require parameters, some do not. Consult the documentation for the
179 :     schema in question.
180 :    
181 :     =back
182 :    
183 : sh002i 1167 For each table defined in C<%dbLayout>, C<new> loads the record, schema, and
184 :     driver modules. It the schema module's C<tables> method lists the current table
185 :     (or contains the string "*") and the output of the schema and driver modules'
186 :     C<style> methods match, the table is installed. Otherwise, an exception is
187 :     thrown.
188 :    
189 : sh002i 956 =cut
190 :    
191 : sh002i 775 sub new($$) {
192 : sh002i 814 my ($invocant, $ce) = @_;
193 : sh002i 775 my $class = ref($invocant) || $invocant;
194 : sh002i 798 my $self = {};
195 : sh002i 931 bless $self, $class; # bless this here so we can pass it to the schema
196 : sh002i 775
197 :     # load the modules required to handle each table, and create driver
198 : sh002i 1167 my %dbLayout = %{$ce->{dbLayout}};
199 :     foreach my $table (keys %dbLayout) {
200 :     my $layout = $dbLayout{$table};
201 : sh002i 798 my $record = $layout->{record};
202 : sh002i 775 my $schema = $layout->{schema};
203 :     my $driver = $layout->{driver};
204 :     my $source = $layout->{source};
205 : sh002i 808 my $params = $layout->{params};
206 : sh002i 775
207 : sh002i 798 runtime_use($record);
208 : sh002i 1096
209 :     runtime_use($driver);
210 :     my $driverObject = eval { $driver->new($source, $params) };
211 : sh002i 1167 croak "error instantiating DB driver $driver for table $table: $@"
212 : sh002i 1096 if $@;
213 :    
214 : sh002i 798 runtime_use($schema);
215 : sh002i 1096 my $schemaObject = eval { $schema->new(
216 :     $self, $driver->new($source, $params),
217 :     $table, $record, $params) };
218 : sh002i 1167 croak "error instantiating DB schema $schema for table $table: $@"
219 : sh002i 1096 if $@;
220 :    
221 :     $self->{$table} = $schemaObject;
222 : sh002i 775 }
223 :    
224 :     return $self;
225 :     }
226 :    
227 : sh002i 1012 =head1 METHODS
228 :    
229 :     =cut
230 :    
231 : sh002i 775 ################################################################################
232 :     # password functions
233 :     ################################################################################
234 :    
235 : sh002i 1012 =head2 Password Methods
236 :    
237 :     =over
238 :    
239 : sh002i 1201 =item newPassword()
240 :    
241 :     Returns a new, empty password object.
242 :    
243 :     =cut
244 :    
245 :     sub newPassword {
246 : sh002i 1236 my ($self, $prototype) = @_;
247 :     return $self->{password}->{record}->new($prototype);
248 : sh002i 1201 }
249 :    
250 : sh002i 1012 =item listPasswords()
251 :    
252 :     Returns a list of user IDs representing the records in the password table.
253 :    
254 :     =cut
255 :    
256 : sh002i 1096 sub listPasswords {
257 : sh002i 775 my ($self) = @_;
258 : sh002i 1096
259 :     croak "listPasswords: requires 0 arguments"
260 :     unless @_ == 1;
261 :    
262 : sh002i 808 return map { $_->[0] }
263 :     $self->{password}->list(undef);
264 : sh002i 775 }
265 :    
266 : sh002i 1012 =item addPassword($Password)
267 :    
268 :     $Password is a record object. The password will be added to the password table
269 :     if a password with the same user ID does not already exist. If one does exist,
270 :     an exception is thrown. To add a password, a user with a matching user ID must
271 :     exist in the user table.
272 :    
273 :     =cut
274 :    
275 : sh002i 1512 sub addPassword {
276 : sh002i 775 my ($self, $Password) = @_;
277 : sh002i 1096
278 :     croak "addPassword: requires 1 argument"
279 :     unless @_ == 2;
280 :     croak "addPassword: argument 1 must be of type ", $self->{password}->{record}
281 :     unless ref $Password eq $self->{password}->{record};
282 :     croak "addPassword: password exists (perhaps you meant to use putPassword?)"
283 :     if $self->{password}->exists($Password->user_id);
284 :     croak "addPassword: user ", $Password->user_id, " not found"
285 : sh002i 775 unless $self->{user}->exists($Password->user_id);
286 : sh002i 1096
287 : sh002i 1199 checkKeyfields($Password);
288 :    
289 : sh002i 775 return $self->{password}->add($Password);
290 :     }
291 :    
292 : sh002i 1012 =item getPassword($userID)
293 :    
294 :     If a record with a matching user ID exists, a record object containting that
295 :     record's data will be returned. If no such record exists, an undefined value
296 :     will be returned.
297 :    
298 :     =cut
299 :    
300 : sh002i 1512 sub getPassword {
301 : sh002i 775 my ($self, $userID) = @_;
302 : sh002i 1096
303 :     croak "getPassword: requires 1 argument"
304 :     unless @_ == 2;
305 :     croak "getPassword: argument 1 must contain a user_id"
306 :     unless defined $userID;
307 :    
308 : sh002i 775 return $self->{password}->get($userID);
309 :     }
310 :    
311 : sh002i 1512 =item getPasswords(@uesrIDs)
312 :    
313 :     Return a list of password records associated with the user IDs given. If there
314 :     is no record associated with a given user ID, that element of the list will be
315 :     undefined.
316 :    
317 :     =cut
318 :    
319 :     sub getPasswords {
320 :     my ($self, @userIDs) = @_;
321 :    
322 :     croak "getPasswords: requires 1 or more argument"
323 :     unless @_ >= 2;
324 :     foreach my $i (0 .. $#userIDs) {
325 :     croak "getPasswords: element $i of argument list must contain a user_id"
326 :     unless defined $userIDs[$i];
327 :     }
328 :    
329 : sh002i 1587 return $self->{password}->gets(map { [$_] } @userIDs);
330 : sh002i 1512 }
331 :    
332 : sh002i 1012 =item putPassword($Password)
333 :    
334 :     $Password is a record object. If a password record with the same user ID exists
335 :     in the password table, the data in the record is replaced with the data in
336 :     $Password. If a matching password record does not exist, an exception is
337 :     thrown.
338 :    
339 :     =cut
340 :    
341 : sh002i 775 sub putPassword($$) {
342 :     my ($self, $Password) = @_;
343 : sh002i 1096
344 :     croak "putPassword: requires 1 argument"
345 :     unless @_ == 2;
346 :     croak "putPassword: argument 1 must be of type ", $self->{password}->{record}
347 :     unless ref $Password eq $self->{password}->{record};
348 :     croak "putPassword: password not found (perhaps you meant to use addPassword?)"
349 :     unless $self->{password}->exists($Password->user_id);
350 :    
351 : sh002i 1199 checkKeyfields($Password);
352 :    
353 : sh002i 775 return $self->{password}->put($Password);
354 :     }
355 :    
356 : sh002i 1012 =item deletePassword($userID)
357 :    
358 :     If a password record with a user ID matching $userID exists in the password
359 :     table, it is removed and the method returns a true value. If one does exist,
360 :     a false value is returned.
361 :    
362 :     =cut
363 :    
364 : sh002i 775 sub deletePassword($$) {
365 :     my ($self, $userID) = @_;
366 : sh002i 1096
367 :     croak "putPassword: requires 1 argument"
368 :     unless @_ == 2;
369 :     croak "deletePassword: argument 1 must contain a user_id"
370 :     unless defined $userID;
371 :    
372 : sh002i 775 return $self->{password}->delete($userID);
373 :     }
374 :    
375 : sh002i 1012 =back
376 :    
377 :     =cut
378 :    
379 : sh002i 775 ################################################################################
380 :     # permission functions
381 :     ################################################################################
382 :    
383 : sh002i 1108 =head2 Permission Level Methods
384 :    
385 :     =over
386 :    
387 : sh002i 1201 =item newPermissionLevel()
388 :    
389 :     Returns a new, empty permission level object.
390 :    
391 :     =cut
392 :    
393 :     sub newPermissionLevel {
394 : sh002i 1236 my ($self, $prototype) = @_;
395 :     return $self->{permission}->{record}->new($prototype);
396 : sh002i 1201 }
397 :    
398 : sh002i 1108 =item listPermissionLevels()
399 :    
400 :     Returns a list of user IDs representing the records in the permission table.
401 :    
402 :     =cut
403 :    
404 : sh002i 775 sub listPermissionLevels($) {
405 :     my ($self) = @_;
406 : sh002i 1096
407 :     croak "listPermissionLevels: requires 0 arguments"
408 :     unless @_ == 1;
409 :    
410 : sh002i 808 return map { $_->[0] }
411 :     $self->{permission}->list(undef);
412 : sh002i 775 }
413 :    
414 : sh002i 1108 =item addPermissionLevel($PermissionLevel)
415 :    
416 :     $PermissionLevel is a record object. The permission level will be added to the
417 :     permission table if a permission level with the same user ID does not already
418 :     exist. If one does exist, an exception is thrown. To add a permission level, a
419 :     user with a matching user ID must exist in the user table.
420 :    
421 :     =cut
422 :    
423 : sh002i 808 sub addPermissionLevel($$) {
424 : sh002i 775 my ($self, $PermissionLevel) = @_;
425 : sh002i 1096
426 :     croak "addPermissionLevel: requires 1 argument"
427 :     unless @_ == 2;
428 :     croak "addPermissionLevel: argument 1 must be of type ", $self->{permission}->{record}
429 :     unless ref $PermissionLevel eq $self->{permission}->{record};
430 :     croak "addPermissionLevel: permission level exists (perhaps you meant to use putPermissionLevel?)"
431 :     if $self->{permission}->exists($PermissionLevel->user_id);
432 :     croak "addPermissionLevel: user ", $PermissionLevel->user_id, " not found"
433 : sh002i 775 unless $self->{user}->exists($PermissionLevel->user_id);
434 : sh002i 1096
435 : sh002i 1199 checkKeyfields($PermissionLevel);
436 :    
437 : sh002i 775 return $self->{permission}->add($PermissionLevel);
438 :     }
439 :    
440 : sh002i 1108 =item getPermissionLevel($userID)
441 :    
442 :     If a record with a matching user ID exists, a record object containting that
443 :     record's data will be returned. If no such record exists, an undefined value
444 :     will be returned.
445 :    
446 :     =cut
447 :    
448 : sh002i 775 sub getPermissionLevel($$) {
449 :     my ($self, $userID) = @_;
450 : sh002i 1096
451 :     croak "getPermissionLevel: requires 1 argument"
452 :     unless @_ == 2;
453 :     croak "getPermissionLevel: argument 1 must contain a user_id"
454 :     unless defined $userID;
455 :    
456 : sh002i 775 return $self->{permission}->get($userID);
457 :     }
458 :    
459 : sh002i 1512 =item getPermissionLevels(@uesrIDs)
460 :    
461 :     Return a list of permission level records associated with the user IDs given. If
462 :     there is no record associated with a given user ID, that element of the list
463 :     will be undefined.
464 :    
465 :     =cut
466 :    
467 :     sub getPermissionLevels {
468 :     my ($self, @userIDs) = @_;
469 :    
470 :     croak "getPermissionLevels: requires 1 or more argument"
471 :     unless @_ >= 2;
472 :     foreach my $i (0 .. $#userIDs) {
473 :     croak "getPermissionLevels: element $i of argument list must contain a user_id"
474 :     unless defined $userIDs[$i];
475 :     }
476 :    
477 : sh002i 1587 return $self->{permission}->gets(map { [$_] } @userIDs);
478 : sh002i 1512 }
479 :    
480 : sh002i 1108 =item putPermissionLevel($PermissionLevel)
481 :    
482 :     $PermissionLevel is a record object. If a permission level record with the same
483 :     user ID exists in the permission table, the data in the record is replaced with
484 :     the data in $PermissionLevel. If a matching permission level record does not
485 :     exist, an exception is thrown.
486 :    
487 :     =cut
488 :    
489 : sh002i 775 sub putPermissionLevel($$) {
490 :     my ($self, $PermissionLevel) = @_;
491 : sh002i 1096
492 :     croak "putPermissionLevel: requires 1 argument"
493 :     unless @_ == 2;
494 :     croak "putPermissionLevel: argument 1 must be of type ", $self->{permission}->{record}
495 :     unless ref $PermissionLevel eq $self->{permission}->{record};
496 :     croak "putPermissionLevel: permission level not found (perhaps you meant to use addPermissionLevel?)"
497 :     unless $self->{permission}->exists($PermissionLevel->user_id);
498 :    
499 : sh002i 1199 checkKeyfields($PermissionLevel);
500 :    
501 : sh002i 775 return $self->{permission}->put($PermissionLevel);
502 :     }
503 :    
504 : sh002i 1108 =item deletePermissionLevel($userID)
505 :    
506 :     If a permission level record with a user ID matching $userID exists in the
507 :     permission table, it is removed and the method returns a true value. If one
508 :     does exist, a false value is returned.
509 :    
510 :     =cut
511 :    
512 : sh002i 775 sub deletePermissionLevel($$) {
513 :     my ($self, $userID) = @_;
514 : sh002i 1096
515 :     croak "deletePermissionLevel: requires 1 argument"
516 :     unless @_ == 2;
517 :     croak "deletePermissionLevel: argument 1 must contain a user_id"
518 :     unless defined $userID;
519 :    
520 : sh002i 775 return $self->{permission}->delete($userID);
521 :     }
522 :    
523 :     ################################################################################
524 :     # key functions
525 :     ################################################################################
526 :    
527 : sh002i 1108 =head2 Key Methods
528 :    
529 :     =over
530 :    
531 : sh002i 1201 =item newKey()
532 :    
533 :     Returns a new, empty key object.
534 :    
535 :     =cut
536 :    
537 :     sub newKey {
538 : sh002i 1236 my ($self, $prototype) = @_;
539 :     return $self->{key}->{record}->new($prototype);
540 : sh002i 1201 }
541 :    
542 : sh002i 1108 =item listKeys()
543 :    
544 :     Returns a list of user IDs representing the records in the key table.
545 :    
546 :     =cut
547 :    
548 : sh002i 775 sub listKeys($) {
549 :     my ($self) = @_;
550 : sh002i 1096
551 :     croak "listKeys: requires 0 arguments"
552 :     unless @_ == 1;
553 :    
554 : sh002i 808 return map { $_->[0] }
555 :     $self->{key}->list(undef);
556 : sh002i 775 }
557 :    
558 : sh002i 1108 =item addKey($Key)
559 :    
560 :     $Key is a record object. The key will be added to the key table if a key with
561 :     the same user ID does not already exist. If one does exist, an exception is
562 :     thrown. To add a key, a user with a matching user ID must exist in the user
563 :     table.
564 :    
565 :     =cut
566 :    
567 : sh002i 808 sub addKey($$) {
568 : sh002i 775 my ($self, $Key) = @_;
569 : sh002i 1096
570 :     croak "addKey: requires 1 argument"
571 :     unless @_ == 2;
572 :     croak "addKey: argument 1 must be of type ", $self->{key}->{record}
573 :     unless ref $Key eq $self->{key}->{record};
574 :     croak "addKey: key exists (perhaps you meant to use putKey?)"
575 :     if $self->{key}->exists($Key->user_id);
576 :     croak "addKey: user ", $Key->user_id, " not found"
577 : sh002i 775 unless $self->{user}->exists($Key->user_id);
578 : sh002i 1096
579 : sh002i 1199 checkKeyfields($Key);
580 :    
581 : sh002i 775 return $self->{key}->add($Key);
582 :     }
583 :    
584 : sh002i 1108 =item getKey($userID)
585 :    
586 :     If a record with a matching user ID exists, a record object containting that
587 :     record's data will be returned. If no such record exists, an undefined value
588 :     will be returned.
589 :    
590 :     =cut
591 :    
592 : sh002i 775 sub getKey($$) {
593 :     my ($self, $userID) = @_;
594 : sh002i 1096
595 :     croak "getKey: requires 1 argument"
596 :     unless @_ == 2;
597 :     croak "getKey: argument 1 must contain a user_id"
598 :     unless defined $userID;
599 :    
600 : sh002i 775 return $self->{key}->get($userID);
601 :     }
602 :    
603 : sh002i 1512 =item getKeys(@uesrIDs)
604 :    
605 :     Return a list of key records associated with the user IDs given. If there is no
606 :     record associated with a given user ID, that element of the list will be
607 :     undefined.
608 :    
609 :     =cut
610 :    
611 :     sub getKeys {
612 :     my ($self, @userIDs) = @_;
613 :    
614 :     croak "getKeys: requires 1 or more argument"
615 :     unless @_ >= 2;
616 :     foreach my $i (0 .. $#userIDs) {
617 :     croak "getKeys: element $i of argument list must contain a user_id"
618 :     unless defined $userIDs[$i];
619 :     }
620 :    
621 : sh002i 1587 return $self->{key}->gets(map { [$_] } @userIDs);
622 : sh002i 1512 }
623 :    
624 : sh002i 1108 =item putKey($Key)
625 :    
626 :     $Key is a record object. If a key record with the same user ID exists in the
627 :     key table, the data in the record is replaced with the data in $Key. If a
628 :     matching key record does not exist, an exception is thrown.
629 :    
630 :     =cut
631 :    
632 : sh002i 775 sub putKey($$) {
633 :     my ($self, $Key) = @_;
634 : sh002i 1096
635 :     croak "putKey: requires 1 argument"
636 :     unless @_ == 2;
637 :     croak "putKey: argument 1 must be of type ", $self->{key}->{record}
638 :     unless ref $Key eq $self->{key}->{record};
639 :     croak "putKey: key not found (perhaps you meant to use addKey?)"
640 :     unless $self->{key}->exists($Key->user_id);
641 :    
642 : sh002i 1199 checkKeyfields($Key);
643 :    
644 : sh002i 775 return $self->{key}->put($Key);
645 :     }
646 :    
647 : sh002i 1108 =item deleteKey($userID)
648 :    
649 :     If a key record with a user ID matching $userID exists in the key table, it is
650 :     removed and the method returns a true value. If one does exist, a false value
651 :     is returned.
652 :    
653 :     =cut
654 :    
655 : sh002i 775 sub deleteKey($$) {
656 :     my ($self, $userID) = @_;
657 : sh002i 1096
658 :     croak "deleteKey: requires 1 argument"
659 :     unless @_ == 2;
660 :     croak "deleteKey: argument 1 must contain a user_id"
661 :     unless defined $userID;
662 :    
663 : sh002i 775 return $self->{key}->delete($userID);
664 :     }
665 :    
666 :     ################################################################################
667 :     # user functions
668 :     ################################################################################
669 :    
670 : sh002i 1108 =head2 User Methods
671 :    
672 :     =over
673 :    
674 : sh002i 1201 =item newUser()
675 :    
676 :     Returns a new, empty user object.
677 :    
678 :     =cut
679 :    
680 :     sub newUser {
681 : sh002i 1236 my ($self, $prototype) = @_;
682 :     return $self->{user}->{record}->new($prototype);
683 : sh002i 1201 }
684 :    
685 : sh002i 1108 =item listUsers()
686 :    
687 :     Returns a list of user IDs representing the records in the user table.
688 :    
689 :     =cut
690 :    
691 : sh002i 1568 sub listUsers {
692 : sh002i 775 my ($self) = @_;
693 : sh002i 1096
694 :     croak "listUsers: requires 0 arguments"
695 :     unless @_ == 1;
696 :    
697 : sh002i 808 return map { $_->[0] }
698 :     $self->{user}->list(undef);
699 : sh002i 775 }
700 :    
701 : sh002i 1108 =item addUser($User)
702 :    
703 :     $User is a record object. The user will be added to the user table if a user
704 :     with the same user ID does not already exist. If one does exist, an exception
705 :     is thrown.
706 :    
707 :     =cut
708 :    
709 : sh002i 1568 sub addUser {
710 : sh002i 775 my ($self, $User) = @_;
711 : sh002i 1096
712 :     croak "addUser: requires 1 argument"
713 :     unless @_ == 2;
714 :     croak "addUser: argument 1 must be of type ", $self->{user}->{record}
715 :     unless ref $User eq $self->{user}->{record};
716 :     croak "addUser: user exists (perhaps you meant to use putUser?)"
717 :     if $self->{user}->exists($User->user_id);
718 :    
719 : sh002i 1199 checkKeyfields($User);
720 :    
721 : sh002i 775 return $self->{user}->add($User);
722 :     }
723 :    
724 : sh002i 1108 =item getUser($userID)
725 :    
726 :     If a record with a matching user ID exists, a record object containting that
727 :     record's data will be returned. If no such record exists, an undefined value
728 :     will be returned.
729 :    
730 :     =cut
731 :    
732 : sh002i 1568 sub getUser {
733 : sh002i 775 my ($self, $userID) = @_;
734 : sh002i 1096
735 :     croak "getUser: requires 1 argument"
736 :     unless @_ == 2;
737 :     croak "getUser: argument 1 must contain a user_id"
738 :     unless defined $userID;
739 :    
740 : sh002i 775 return $self->{user}->get($userID);
741 :     }
742 :    
743 : sh002i 1512 =item getUsers(@uesrIDs)
744 :    
745 :     Return a list of user records associated with the user IDs given. If there is no
746 :     record associated with a given user ID, that element of the list will be
747 :     undefined.
748 :    
749 :     =cut
750 :    
751 :     sub getUsers {
752 :     my ($self, @userIDs) = @_;
753 :    
754 :     croak "getUsers: requires 1 or more argument"
755 :     unless @_ >= 2;
756 :     foreach my $i (0 .. $#userIDs) {
757 :     croak "getUsers: element $i of argument list must contain a user_id"
758 :     unless defined $userIDs[$i];
759 :     }
760 :    
761 : sh002i 1587 return $self->{user}->gets(map { [$_] } @userIDs);
762 : sh002i 1512 }
763 :    
764 : sh002i 1108 =item putUser($User)
765 :    
766 :     $User is a record object. If a user record with the same user ID exists in the
767 :     user table, the data in the record is replaced with the data in $User. If a
768 :     matching user record does not exist, an exception is thrown.
769 :    
770 :     =cut
771 :    
772 : sh002i 1568 sub putUser {
773 : sh002i 775 my ($self, $User) = @_;
774 : sh002i 1096
775 :     croak "putUser: requires 1 argument"
776 :     unless @_ == 2;
777 :     croak "putUser: argument 1 must be of type ", $self->{user}->{record}
778 :     unless ref $User eq $self->{user}->{record};
779 :     croak "putUser: user not found (perhaps you meant to use addUser?)"
780 :     unless $self->{user}->exists($User->user_id);
781 :    
782 : sh002i 1199 checkKeyfields($User);
783 :    
784 : sh002i 775 return $self->{user}->put($User);
785 :     }
786 :    
787 : sh002i 1108 =item deleteUser($userID)
788 :    
789 :     If a user record with a user ID matching $userID exists in the user table, it
790 :     is removed and the method returns a true value. If one does exist, a false
791 :     value is returned. When a user record is deleted, all records associated with
792 :     that user are also deleted. This includes the password, permission, and key
793 :     records, and all user set records for that user.
794 :    
795 :     =cut
796 :    
797 : sh002i 1568 sub deleteUser {
798 : sh002i 775 my ($self, $userID) = @_;
799 : sh002i 1096
800 :     croak "deleteUser: requires 1 argument"
801 :     unless @_ == 2;
802 :     croak "deleteUser: argument 1 must contain a user_id"
803 :     unless defined $userID;
804 :    
805 : sh002i 1167 #$self->deleteUserSet($userID, $_)
806 :     # foreach $self->listUserSets($userID);
807 :     $self->deleteUserSet($userID, undef);
808 : sh002i 775 $self->deletePassword($userID);
809 :     $self->deletePermissionLevel($userID);
810 :     $self->deleteKey($userID);
811 :     return $self->{user}->delete($userID);
812 :     }
813 :    
814 : sh002i 1583 =back
815 :    
816 :     =cut
817 :    
818 : sh002i 775 ################################################################################
819 :     # set functions
820 :     ################################################################################
821 :    
822 : sh002i 1583 =head2 Global Set Methods
823 :    
824 :     FIXME: write this
825 :    
826 :     =over
827 :    
828 :     =cut
829 :    
830 : sh002i 1201 sub newGlobalSet {
831 : sh002i 1236 my ($self, $prototype) = @_;
832 :     return $self->{set}->{record}->new($prototype);
833 : sh002i 1201 }
834 :    
835 : sh002i 775 sub listGlobalSets($) {
836 :     my ($self) = @_;
837 : sh002i 1096
838 :     croak "listGlobalSets: requires 0 arguments"
839 :     unless @_ == 1;
840 :    
841 : sh002i 808 return map { $_->[0] }
842 :     $self->{set}->list(undef);
843 : sh002i 775 }
844 :    
845 : sh002i 808 sub addGlobalSet($$) {
846 : sh002i 775 my ($self, $GlobalSet) = @_;
847 : sh002i 1096
848 :     croak "addGlobalSet: requires 1 argument"
849 :     unless @_ == 2;
850 :     croak "addGlobalSet: argument 1 must be of type ", $self->{set}->{record}
851 :     unless ref $GlobalSet eq $self->{set}->{record};
852 :     croak "addGlobalSet: global set exists (perhaps you meant to use putGlobalSet?)"
853 :     if $self->{set}->exists($GlobalSet->set_id);
854 :    
855 : sh002i 1199 checkKeyfields($GlobalSet);
856 :    
857 : sh002i 775 return $self->{set}->add($GlobalSet);
858 :     }
859 :    
860 :     sub getGlobalSet($$) {
861 :     my ($self, $setID) = @_;
862 : sh002i 1096
863 :     croak "getGlobalSet: requires 1 argument"
864 :     unless @_ == 2;
865 :     croak "getGlobalSet: argument 1 must contain a set_id"
866 :     unless defined $setID;
867 :    
868 : sh002i 775 return $self->{set}->get($setID);
869 :     }
870 :    
871 : sh002i 1512 =item getGlobalSets(@setIDs)
872 :    
873 :     Return a list of global set records associated with the user IDs given. If there
874 :     is no record associated with a given user ID, that element of the list will be
875 :     undefined.
876 :    
877 :     =cut
878 :    
879 :     sub getGlobalSets {
880 :     my ($self, @setIDs) = @_;
881 :    
882 :     croak "getGlobalSets: requires 1 or more argument"
883 :     unless @_ >= 2;
884 :     foreach my $i (0 .. $#setIDs) {
885 :     croak "getGlobalSets: element $i of argument list must contain a set_id"
886 :     unless defined $setIDs[$i];
887 :     }
888 :    
889 : sh002i 1587 return $self->{set}->gets(map { [$_] } @setIDs);
890 : sh002i 1512 }
891 :    
892 : sh002i 775 sub putGlobalSet($$) {
893 :     my ($self, $GlobalSet) = @_;
894 : sh002i 1096
895 :     croak "putGlobalSet: requires 1 argument"
896 :     unless @_ == 2;
897 :     croak "putGlobalSet: argument 1 must be of type ", $self->{set}->{record}
898 :     unless ref $GlobalSet eq $self->{set}->{record};
899 :     croak "putGlobalSet: global set not found (perhaps you meant to use addGlobalSet?)"
900 :     unless $self->{set}->exists($GlobalSet->set_id);
901 :    
902 : sh002i 1199 checkKeyfields($GlobalSet);
903 :    
904 : sh002i 775 return $self->{set}->put($GlobalSet);
905 :     }
906 :    
907 :     sub deleteGlobalSet($$) {
908 :     my ($self, $setID) = @_;
909 : sh002i 1096
910 :     croak "deleteGlobalSet: requires 1 argument"
911 :     unless @_ == 2;
912 :     croak "deleteGlobalSet: argument 1 must contain a set_id"
913 : sh002i 1167 unless defined $setID or caller eq __PACKAGE__;
914 : sh002i 1096
915 : sh002i 1167 #$self->deleteUserSet($_, $setID)
916 :     # foreach $self->listSetUsers($setID);
917 :     #$self->deleteGlobalProblem($setID, $_)
918 :     # foreach $self->listGlobalProblems($setID);
919 :     $self->deleteUserSet(undef, $setID);
920 :     $self->deleteGlobalProblem($setID, undef);
921 : sh002i 775 return $self->{set}->delete($setID);
922 :     }
923 :    
924 : sh002i 1583 =back
925 :    
926 :     =cut
927 :    
928 : sh002i 775 ################################################################################
929 :     # set_user functions
930 :     ################################################################################
931 :    
932 : sh002i 1583 =head2 User-Specific Set Methods
933 :    
934 :     FIXME: write this
935 :    
936 :     =over
937 :    
938 :     =cut
939 :    
940 : sh002i 1201 sub newUserSet {
941 : sh002i 1236 my ($self, $prototype) = @_;
942 :     return $self->{set_user}->{record}->new($prototype);
943 : sh002i 1201 }
944 :    
945 : sh002i 909 sub listSetUsers($$) {
946 :     my ($self, $setID) = @_;
947 : sh002i 1096
948 :     croak "listSetUsers: requires 1 argument"
949 :     unless @_ == 2;
950 :     croak "listSetUsers: argument 1 must contain a set_id"
951 :     unless defined $setID;
952 :    
953 : sh002i 909 return map { $_->[0] } # extract user_id
954 :     $self->{set_user}->list(undef, $setID);
955 :     }
956 :    
957 :     sub listUserSets($$) {
958 : sh002i 775 my ($self, $userID) = @_;
959 : sh002i 1096
960 :     croak "listUserSets: requires 1 argument"
961 :     unless @_ == 2;
962 :     croak "listUserSets: argument 1 must contain a user_id"
963 :     unless defined $userID;
964 :    
965 : sh002i 808 return map { $_->[1] } # extract set_id
966 :     $self->{set_user}->list($userID, undef);
967 : sh002i 775 }
968 :    
969 : sh002i 808 sub addUserSet($$) {
970 : sh002i 775 my ($self, $UserSet) = @_;
971 : sh002i 1096
972 :     croak "addUserSet: requires 1 argument"
973 :     unless @_ == 2;
974 :     croak "addUserSet: argument 1 must be of type ", $self->{set_user}->{record}
975 :     unless ref $UserSet eq $self->{set_user}->{record};
976 :     croak "addUserSet: user set exists (perhaps you meant to use putUserSet?)"
977 :     if $self->{set_user}->exists($UserSet->user_id, $UserSet->set_id);
978 :     croak "addUserSet: user ", $UserSet->user_id, " not found"
979 : sh002i 775 unless $self->{user}->exists($UserSet->user_id);
980 : sh002i 1096 croak "addUserSet: set ", $UserSet->set_id, " not found"
981 : sh002i 775 unless $self->{set}->exists($UserSet->set_id);
982 : sh002i 1096
983 : sh002i 1199 checkKeyfields($UserSet);
984 :    
985 : sh002i 775 return $self->{set_user}->add($UserSet);
986 :     }
987 :    
988 : sh002i 909 sub getUserSet($$$) {
989 : sh002i 775 my ($self, $userID, $setID) = @_;
990 : sh002i 1096
991 :     croak "getUserSet: requires 2 arguments"
992 :     unless @_ == 3;
993 :     croak "getUserSet: argument 1 must contain a user_id"
994 :     unless defined $userID;
995 :     croak "getUserSet: argument 2 must contain a set_id"
996 :     unless defined $setID;
997 :    
998 : sh002i 1589 #return $self->{set_user}->get($userID, $setID);
999 :     return ( $self->getUserSets([$userID, $setID]) )[0];
1000 : sh002i 775 }
1001 :    
1002 : sh002i 1512 =item getUserSets(@userSetIDs)
1003 :    
1004 :     Return a list of user set records associated with the user IDs given. If there
1005 :     is no record associated with a given user ID, that element of the list will be
1006 :     undefined. @userProblemIDs consists of references to arrays in which the first
1007 :     element is the user_id and the second element is the set_id.
1008 :    
1009 :     =cut
1010 :    
1011 :     sub getUserSets {
1012 :     my ($self, @userSetIDs) = @_;
1013 :    
1014 :     croak "getUserSets: requires 1 or more argument"
1015 :     unless @_ >= 2;
1016 :     foreach my $i (0 .. $#userSetIDs) {
1017 :     croak "getUserSets: element $i of argument list must contain a <user_id, set_id> pair"
1018 :     unless defined $userSetIDs[$i]
1019 :     and ref $userSetIDs[$i] eq "ARRAY"
1020 :     and @{$userSetIDs[$i]} == 2
1021 :     and defined $userSetIDs[$i]->[0]
1022 :     and defined $userSetIDs[$i]->[1];
1023 :     }
1024 :    
1025 : sh002i 1568 return $self->{set_user}->gets(@userSetIDs);
1026 : sh002i 1512 }
1027 :    
1028 : sh002i 775 sub putUserSet($$) {
1029 :     my ($self, $UserSet) = @_;
1030 : sh002i 1096
1031 :     croak "putUserSet: requires 1 argument"
1032 :     unless @_ == 2;
1033 :     croak "putUserSet: argument 1 must be of type ", $self->{set_user}->{record}
1034 :     unless ref $UserSet eq $self->{set_user}->{record};
1035 :     croak "putUserSet: user set not found (perhaps you meant to use addUserSet?)"
1036 :     unless $self->{set_user}->exists($UserSet->user_id, $UserSet->set_id);
1037 :     croak "putUserSet: user ", $UserSet->user_id, " not found"
1038 :     unless $self->{user}->exists($UserSet->user_id);
1039 :     croak "putUserSet: set ", $UserSet->set_id, " not found"
1040 :     unless $self->{set}->exists($UserSet->set_id);
1041 :    
1042 : sh002i 1199 checkKeyfields($UserSet);
1043 :    
1044 : sh002i 775 return $self->{set_user}->put($UserSet);
1045 :     }
1046 :    
1047 : sh002i 909 sub deleteUserSet($$$) {
1048 : sh002i 775 my ($self, $userID, $setID) = @_;
1049 : sh002i 1096
1050 :     croak "getUserSet: requires 2 arguments"
1051 :     unless @_ == 3;
1052 :     croak "getUserSet: argument 1 must contain a user_id"
1053 : sh002i 1167 unless defined $userID or caller eq __PACKAGE__;
1054 : sh002i 1096 croak "getUserSet: argument 2 must contain a set_id"
1055 : sh002i 1167 unless defined $userID or caller eq __PACKAGE__;
1056 : sh002i 1096
1057 : sh002i 1167 #$self->deleteUserProblem($userID, $setID, $_)
1058 :     # foreach $self->listUserProblems($userID, $setID);
1059 :     $self->deleteUserProblem($userID, $setID, undef);
1060 : sh002i 775 return $self->{set_user}->delete($userID, $setID);
1061 :     }
1062 :    
1063 : sh002i 1583 =back
1064 :    
1065 :     =cut
1066 :    
1067 : sh002i 775 ################################################################################
1068 :     # problem functions
1069 :     ################################################################################
1070 :    
1071 : sh002i 1583 =head2 Global Problem Methods
1072 :    
1073 :     FIXME: write this
1074 :    
1075 :     =over
1076 :    
1077 :     =cut
1078 :    
1079 : sh002i 1201 sub newGlobalProblem {
1080 : sh002i 1236 my ($self, $prototype) = @_;
1081 :     return $self->{problem}->{record}->new($prototype);
1082 : sh002i 1201 }
1083 :    
1084 : sh002i 775 sub listGlobalProblems($$) {
1085 :     my ($self, $setID) = @_;
1086 : sh002i 1096
1087 :     croak "listGlobalProblems: requires 1 arguments"
1088 :     unless @_ == 2;
1089 :     croak "listGlobalProblems: argument 1 must contain a set_id"
1090 :     unless defined $setID;
1091 :    
1092 : sh002i 775 return map { $_->[1] }
1093 : sh002i 1096 $self->{problem}->list($setID, undef);
1094 : sh002i 775 }
1095 :    
1096 : sh002i 808 sub addGlobalProblem($$) {
1097 : sh002i 775 my ($self, $GlobalProblem) = @_;
1098 : sh002i 1096
1099 :     croak "addGlobalProblem: requires 1 argument"
1100 :     unless @_ == 2;
1101 :     croak "addGlobalProblem: argument 1 must be of type ", $self->{problem}->{record}
1102 :     unless ref $GlobalProblem eq $self->{problem}->{record};
1103 :     croak "addGlobalProblem: global problem exists (perhaps you meant to use putGlobalProblem?)"
1104 :     if $self->{problem}->exists($GlobalProblem->set_id, $GlobalProblem->problem_id);
1105 :     croak "addGlobalProblem: set ", $GlobalProblem->set_id, " not found"
1106 : sh002i 775 unless $self->{set}->exists($GlobalProblem->set_id);
1107 : sh002i 1096
1108 : sh002i 1199 checkKeyfields($GlobalProblem);
1109 :    
1110 : sh002i 775 return $self->{problem}->add($GlobalProblem);
1111 :     }
1112 :    
1113 :     sub getGlobalProblem($$$) {
1114 :     my ($self, $setID, $problemID) = @_;
1115 : sh002i 1096
1116 :     croak "getGlobalProblem: requires 2 arguments"
1117 :     unless @_ == 3;
1118 :     croak "getGlobalProblem: argument 1 must contain a set_id"
1119 :     unless defined $setID;
1120 :     croak "getGlobalProblem: argument 2 must contain a problem_id"
1121 :     unless defined $problemID;
1122 :    
1123 : sh002i 916 return $self->{problem}->get($setID, $problemID);
1124 : sh002i 775 }
1125 :    
1126 : sh002i 1512 =item getGlobalProblems(@problemIDs)
1127 :    
1128 :     Return a list of global set records associated with the user IDs given. If there
1129 :     is no record associated with a given user ID, that element of the list will be
1130 :     undefined. @problemIDs consists of references to arrays in which the first
1131 :     element is the set_id, and the second element is the problem_id.
1132 :    
1133 :     =cut
1134 :    
1135 :     sub getGlobalProblems {
1136 :     my ($self, @problemIDs) = @_;
1137 :    
1138 :     croak "getGlobalProblems: requires 1 or more argument"
1139 :     unless @_ >= 2;
1140 :     foreach my $i (0 .. $#problemIDs) {
1141 :     croak "getUserSets: element $i of argument list must contain a <set_id, problem_id> pair"
1142 :     unless defined $problemIDs[$i]
1143 :     and ref $problemIDs[$i] eq "ARRAY"
1144 :     and @{$problemIDs[$i]} == 2
1145 :     and defined $problemIDs[$i]->[0]
1146 :     and defined $problemIDs[$i]->[1];
1147 :     }
1148 :    
1149 : sh002i 1568 return $self->{problem}->gets(@problemIDs);
1150 : sh002i 1512 }
1151 :    
1152 : sh002i 775 sub putGlobalProblem($$) {
1153 :     my ($self, $GlobalProblem) = @_;
1154 : sh002i 1096
1155 :     croak "putGlobalProblem: requires 1 argument"
1156 :     unless @_ == 2;
1157 :     croak "putGlobalProblem: argument 1 must be of type ", $self->{problem}->{record}
1158 :     unless ref $GlobalProblem eq $self->{problem}->{record};
1159 :     croak "putGlobalProblem: global problem not found (perhaps you meant to use addGlobalProblem?)"
1160 :     unless $self->{problem}->exists($GlobalProblem->set_id, $GlobalProblem->problem_id);
1161 :     croak "putGlobalProblem: set ", $GlobalProblem->set_id, " not found"
1162 :     unless $self->{set}->exists($GlobalProblem->set_id);
1163 :    
1164 : sh002i 1199 checkKeyfields($GlobalProblem);
1165 :    
1166 : sh002i 775 return $self->{problem}->put($GlobalProblem);
1167 :     }
1168 :    
1169 :     sub deleteGlobalProblem($$$) {
1170 :     my ($self, $setID, $problemID) = @_;
1171 : sh002i 1096
1172 : sh002i 1167 croak "deleteGlobalProblem: requires 2 arguments"
1173 : sh002i 1096 unless @_ == 3;
1174 : sh002i 1167 croak "deleteGlobalProblem: argument 1 must contain a set_id"
1175 :     unless defined $setID or caller eq __PACKAGE__;
1176 :     croak "deleteGlobalProblem: argument 2 must contain a problem_id"
1177 :     unless defined $problemID or caller eq __PACKAGE__;
1178 : sh002i 1096
1179 : sh002i 1167 #$self->deleteUserProblem($_, $setID, $problemID)
1180 :     # foreach $self->listProblemUsers($setID, $problemID);
1181 :     $self->deleteUserProblem(undef, $setID, $problemID);
1182 : sh002i 775 return $self->{problem}->delete($setID, $problemID);
1183 :     }
1184 :    
1185 : sh002i 1583 =back
1186 :    
1187 :     =cut
1188 :    
1189 : sh002i 775 ################################################################################
1190 :     # problem_user functions
1191 :     ################################################################################
1192 :    
1193 : sh002i 1583 =head2 User-Specific Problem Methods
1194 :    
1195 :     FIXME: write this
1196 :    
1197 :     =over
1198 :    
1199 :     =cut
1200 :    
1201 : sh002i 1201 sub newUserProblem {
1202 : sh002i 1236 my ($self, $prototype) = @_;
1203 :     return $self->{problem_user}->{record}->new($prototype);
1204 : sh002i 1201 }
1205 :    
1206 : sh002i 923 sub listProblemUsers($$$) {
1207 :     my ($self, $setID, $problemID) = @_;
1208 : sh002i 1096
1209 :     croak "listProblemUsers: requires 2 arguments"
1210 :     unless @_ == 3;
1211 :     croak "listProblemUsers: argument 1 must contain a set_id"
1212 :     unless defined $setID;
1213 :     croak "listProblemUsers: argument 2 must contain a problem_id"
1214 :     unless defined $problemID;
1215 :    
1216 : sh002i 923 return map { $_->[0] } # extract user_id
1217 :     $self->{problem_user}->list(undef, $setID, $problemID);
1218 :     }
1219 :    
1220 : sh002i 775 sub listUserProblems($$$) {
1221 :     my ($self, $userID, $setID) = @_;
1222 : sh002i 1096
1223 :     croak "listUserProblems: requires 2 arguments"
1224 :     unless @_ == 3;
1225 :     croak "listUserProblems: argument 1 must contain a user_id"
1226 :     unless defined $userID;
1227 :     croak "listUserProblems: argument 2 must contain a set_id"
1228 :     unless defined $setID;
1229 :    
1230 : sh002i 923 return map { $_->[2] } # extract problem_id
1231 : sh002i 808 $self->{problem_user}->list($userID, $setID, undef);
1232 : sh002i 775 }
1233 :    
1234 : sh002i 808 sub addUserProblem($$) {
1235 : sh002i 775 my ($self, $UserProblem) = @_;
1236 : sh002i 1096
1237 :     croak "addUserProblem: requires 1 argument"
1238 :     unless @_ == 2;
1239 :     croak "addUserProblem: argument 1 must be of type ", $self->{problem_user}->{record}
1240 :     unless ref $UserProblem eq $self->{problem_user}->{record};
1241 :     croak "addUserProblem: user problem exists (perhaps you meant to use putUserProblem?)"
1242 : malsyned 1185 if $self->{problem_user}->exists($UserProblem->user_id, $UserProblem->set_id, $UserProblem->problem_id);
1243 : sh002i 1096 croak "addUserProblem: user set ", $UserProblem->set_id, " for user ", $UserProblem->user_id, " not found"
1244 : sh002i 808 unless $self->{set_user}->exists($UserProblem->user_id, $UserProblem->set_id);
1245 : sh002i 1096 croak "addUserProblem: problem ", $UserProblem->problem_id, " in set ", $UserProblem->set_id, " not found"
1246 : sh002i 914 unless $self->{problem}->exists($UserProblem->set_id, $UserProblem->problem_id);
1247 : sh002i 1096
1248 : sh002i 1199 checkKeyfields($UserProblem);
1249 :    
1250 : sh002i 775 return $self->{problem_user}->add($UserProblem);
1251 :     }
1252 :    
1253 : sh002i 798 sub getUserProblem($$$$) {
1254 : sh002i 775 my ($self, $userID, $setID, $problemID) = @_;
1255 : sh002i 1096
1256 :     croak "getUserProblem: requires 3 arguments"
1257 :     unless @_ == 4;
1258 :     croak "getUserProblem: argument 1 must contain a user_id"
1259 :     unless defined $userID;
1260 :     croak "getUserProblem: argument 2 must contain a set_id"
1261 :     unless defined $setID;
1262 :     croak "getUserProblem: argument 3 must contain a problem_id"
1263 :     unless defined $problemID;
1264 :    
1265 : sh002i 1589 #return $self->{problem_user}->get($userID, $setID, $problemID);
1266 :     return ( $self->getUserProblems([$userID, $setID, $problemID]) )[0];
1267 : sh002i 775 }
1268 :    
1269 : sh002i 1512 =item getUserProblems(@userProblemIDs)
1270 :    
1271 :     Return a list of user set records associated with the user IDs given. If there
1272 :     is no record associated with a given user ID, that element of the list will be
1273 :     undefined. @userProblemIDs consists of references to arrays in which the first
1274 :     element is the user_id, the second element is the set_id, and the third element
1275 :     is the problem_id.
1276 :    
1277 :     =cut
1278 :    
1279 :     sub getUserProblems {
1280 :     my ($self, @userProblemIDs) = @_;
1281 :    
1282 :     croak "getUserProblems: requires 1 or more argument"
1283 :     unless @_ >= 2;
1284 :     foreach my $i (0 .. $#userProblemIDs) {
1285 :     croak "getUserProblems: element $i of argument list must contain a <user_id, set_id, problem_id> triple"
1286 :     unless defined $userProblemIDs[$i]
1287 :     and ref $userProblemIDs[$i] eq "ARRAY"
1288 :     and @{$userProblemIDs[$i]} == 3
1289 :     and defined $userProblemIDs[$i]->[0]
1290 :     and defined $userProblemIDs[$i]->[1]
1291 :     and defined $userProblemIDs[$i]->[2];
1292 :     }
1293 :    
1294 : sh002i 1586 return $self->{problem_user}->gets(@userProblemIDs);
1295 : sh002i 1512 }
1296 :    
1297 : sh002i 775 sub putUserProblem($$) {
1298 :     my ($self, $UserProblem) = @_;
1299 : sh002i 1096
1300 :     croak "putUserProblem: requires 1 argument"
1301 :     unless @_ == 2;
1302 :     croak "putUserProblem: argument 1 must be of type ", $self->{problem_user}->{record}
1303 :     unless ref $UserProblem eq $self->{problem_user}->{record};
1304 :     croak "putUserProblem: user set ", $UserProblem->set_id, " for user ", $UserProblem->user_id, " not found"
1305 :     unless $self->{set_user}->exists($UserProblem->user_id, $UserProblem->set_id);
1306 : malsyned 1104 croak "putUserProblem: user problem not found (perhaps you meant to use addUserProblem?)"
1307 :     unless $self->{problem_user}->exists($UserProblem->user_id, $UserProblem->set_id, $UserProblem->problem_id);
1308 : sh002i 1096 croak "putUserProblem: problem ", $UserProblem->problem_id, " in set ", $UserProblem->set_id, " not found"
1309 :     unless $self->{problem}->exists($UserProblem->set_id, $UserProblem->problem_id);
1310 :    
1311 : sh002i 1199 checkKeyfields($UserProblem);
1312 :    
1313 : sh002i 775 return $self->{problem_user}->put($UserProblem);
1314 :     }
1315 :    
1316 : sh002i 798 sub deleteUserProblem($$$$) {
1317 : sh002i 775 my ($self, $userID, $setID, $problemID) = @_;
1318 : sh002i 1096
1319 :     croak "getUserProblem: requires 3 arguments"
1320 :     unless @_ == 4;
1321 :     croak "getUserProblem: argument 1 must contain a user_id"
1322 : sh002i 1167 unless defined $userID or caller eq __PACKAGE__;
1323 : sh002i 1096 croak "getUserProblem: argument 2 must contain a set_id"
1324 : sh002i 1167 unless defined $setID or caller eq __PACKAGE__;
1325 : sh002i 1096 croak "getUserProblem: argument 3 must contain a problem_id"
1326 : sh002i 1167 unless defined $problemID or caller eq __PACKAGE__;
1327 : sh002i 1096
1328 : sh002i 775 return $self->{problem_user}->delete($userID, $setID, $problemID);
1329 :     }
1330 :    
1331 : sh002i 1583 =back
1332 :    
1333 :     =cut
1334 :    
1335 : sh002i 775 ################################################################################
1336 :     # set+set_user functions
1337 :     ################################################################################
1338 :    
1339 : sh002i 1583 =head2 Set Merging Methods
1340 :    
1341 :     FIXME: write this
1342 :    
1343 :     =over
1344 :    
1345 :     =cut
1346 :    
1347 : sh002i 1096 sub getGlobalUserSet {
1348 :     carp "getGlobalUserSet: this method is deprecated -- use getMergedSet instead";
1349 :     return shift->getMergedSet(@_);
1350 :     }
1351 :    
1352 :     sub getMergedSet {
1353 : sh002i 798 my ($self, $userID, $setID) = @_;
1354 : sh002i 1096
1355 : gage 1541 croak "getMergedSet: requires 2 arguments"
1356 : sh002i 1096 unless @_ == 3;
1357 : gage 1541 croak "getMergedSet: argument 1 must contain a user_id"
1358 : sh002i 1096 unless defined $userID;
1359 : gage 1541 croak "getMergedSet: argument 2 must contain a set_id"
1360 : sh002i 1096 unless defined $setID;
1361 :    
1362 : sh002i 1583 #my $UserSet = $self->getUserSet($userID, $setID);
1363 :     #return unless $UserSet;
1364 :     #my $GlobalSet = $self->getGlobalSet($setID);
1365 :     #if ($GlobalSet) {
1366 :     # foreach ($UserSet->FIELDS()) {
1367 :     # next unless $GlobalSet->can($_);
1368 :     # next if $UserSet->$_();
1369 :     # $UserSet->$_($GlobalSet->$_());
1370 :     # }
1371 :     #}
1372 :     #return $UserSet;
1373 :    
1374 : sh002i 1586 return ( $self->getMergedSets([$userID, $setID]) )[0];
1375 : sh002i 798 }
1376 : sh002i 775
1377 : sh002i 1512 =item geMegedSets(@userSetIDs)
1378 :    
1379 :     Return a list of merged set records associated with the user IDs given. If there
1380 :     is no record associated with a given user ID, that element of the list will be
1381 : gage 1541 undefined. @userSetIDs consists of references to arrays in which the first
1382 : sh002i 1512 element is the user_id and the second element is the set_id.
1383 :    
1384 :     =cut
1385 :    
1386 : gage 1541 sub getMergedSets {
1387 : sh002i 1512 my ($self, @userSetIDs) = @_;
1388 :    
1389 : gage 1541 croak "getMergedSets: requires 1 or more argument"
1390 : sh002i 1512 unless @_ >= 2;
1391 :     foreach my $i (0 .. $#userSetIDs) {
1392 : gage 1541 croak "getMergedSets: element $i of argument list must contain a <user_id, set_id> pair"
1393 : sh002i 1512 unless defined $userSetIDs[$i]
1394 :     and ref $userSetIDs[$i] eq "ARRAY"
1395 :     and @{$userSetIDs[$i]} == 2
1396 :     and defined $userSetIDs[$i]->[0]
1397 :     and defined $userSetIDs[$i]->[1];
1398 :     }
1399 :    
1400 : sh002i 1583 my @UserSets = $self->getUserSets(@userSetIDs);
1401 :    
1402 : sh002i 1587 my @globalSetIDs = map { $_->[1] } @userSetIDs;
1403 : sh002i 1583 my @GlobalSets = $self->getGlobalSets(@globalSetIDs);
1404 :    
1405 :     my %globalSetFields = map { $_ => 1 } $self->newGlobalSet->FIELDS;
1406 :     my @commonFields = grep { exists $globalSetFields{$_} } $self->newUserSet->FIELDS;
1407 :    
1408 :     for (my $i = 0; $i < @UserSets; $i++) {
1409 :     my $UserSet = $UserSets[$i];
1410 :     my $GlobalSet = $GlobalSets[$i];
1411 :     next unless $UserSet and $GlobalSet;
1412 : sh002i 1587 #warn "---------- USER SET\n", $UserSet->toString, "---------- USER SET\n";
1413 :     #warn "---------- GLOBAL SET\n", $GlobalSet->toString, "---------- GLOBAL SET\n";
1414 : sh002i 1583 foreach my $field (@commonFields) {
1415 : sh002i 1587 next if defined $UserSet->$field;
1416 :     #warn "using global value for field $field\n";
1417 : sh002i 1583 $UserSet->$field($GlobalSet->$field);
1418 :     }
1419 :     }
1420 :    
1421 :     return @UserSets;
1422 : sh002i 1512 }
1423 :    
1424 : sh002i 1583 =back
1425 : gage 1541
1426 : sh002i 1583 =cut
1427 : gage 1541
1428 : sh002i 775 ################################################################################
1429 :     # problem+problem_user functions
1430 :     ################################################################################
1431 :    
1432 : sh002i 1583 =head2 Problem Merging Methods
1433 :    
1434 :     FIXME: write this
1435 :    
1436 :     =over
1437 :    
1438 :     =cut
1439 :    
1440 : sh002i 1096 sub getGlobalUserProblem {
1441 :     carp "getGlobalUserProblem: this method is deprecated -- use getMergedProblem instead";
1442 :     return shift->getMergedProblem(@_);
1443 :     }
1444 :    
1445 :     sub getMergedProblem {
1446 : sh002i 798 my ($self, $userID, $setID, $problemID) = @_;
1447 : sh002i 1096
1448 :     croak "getGlobalUserSet: requires 3 arguments"
1449 :     unless @_ == 4;
1450 :     croak "getGlobalUserSet: argument 1 must contain a user_id"
1451 :     unless defined $userID;
1452 :     croak "getGlobalUserSet: argument 2 must contain a set_id"
1453 :     unless defined $setID;
1454 :     croak "getGlobalUserSet: argument 3 must contain a problem_id"
1455 :     unless defined $problemID;
1456 :    
1457 : sh002i 1583 #my $UserProblem = $self->getUserProblem($userID, $setID, $problemID);
1458 :     #return unless $UserProblem;
1459 :     #my $GlobalProblem = $self->getGlobalProblem($setID, $problemID);
1460 :     #if ($GlobalProblem) {
1461 :     # foreach ($UserProblem->FIELDS()) {
1462 :     # next unless $GlobalProblem->can($_);
1463 :     # next if $UserProblem->$_();
1464 :     # $UserProblem->$_($GlobalProblem->$_());
1465 :     # }
1466 :     #}
1467 :     #return $UserProblem;
1468 :    
1469 : sh002i 1586 return ( $self->getMergedProblems([$userID, $setID, $problemID]) )[0];
1470 : sh002i 798 }
1471 : sh002i 775
1472 : sh002i 1512 =item getMergedProblems(@userProblemIDs)
1473 :    
1474 :     Return a list of merged set records associated with the user IDs given. If there
1475 :     is no record associated with a given user ID, that element of the list will be
1476 :     undefined. @userProblemIDs consists of references to arrays in which the first
1477 :     element is the user_id, the second element is the set_id, and the third element
1478 :     is the problem_id.
1479 :    
1480 :     =cut
1481 :    
1482 : sh002i 1583 #sub getMergedProblems {
1483 :     # my ($self, @userProblemIDs) = @_;
1484 :     #
1485 :     # croak "getMergedProblems: requires 1 or more argument"
1486 :     # unless @_ >= 2;
1487 :     # foreach my $i (0 .. $#userProblemIDs) {
1488 :     # croak "getMergedProblems: element $i of argument list must contain a <user_id, set_id, problem_id> triple"
1489 :     # unless defined $userProblemIDs[$i]
1490 :     # and ref $userProblemIDs[$i] eq "ARRAY"
1491 :     # and @{$userProblemIDs[$i]} == 3
1492 :     # and defined $userProblemIDs[$i]->[0]
1493 :     # and defined $userProblemIDs[$i]->[1]
1494 :     # and defined $userProblemIDs[$i]->[2];
1495 :     # }
1496 :     #
1497 :     # return map { $self->getMergedProblem(@{$_}) } @userProblemIDs;
1498 :     #}
1499 :    
1500 : sh002i 1512 sub getMergedProblems {
1501 :     my ($self, @userProblemIDs) = @_;
1502 :    
1503 :     croak "getMergedProblems: requires 1 or more argument"
1504 :     unless @_ >= 2;
1505 :     foreach my $i (0 .. $#userProblemIDs) {
1506 :     croak "getMergedProblems: element $i of argument list must contain a <user_id, set_id, problem_id> triple"
1507 :     unless defined $userProblemIDs[$i]
1508 :     and ref $userProblemIDs[$i] eq "ARRAY"
1509 :     and @{$userProblemIDs[$i]} == 3
1510 :     and defined $userProblemIDs[$i]->[0]
1511 :     and defined $userProblemIDs[$i]->[1]
1512 :     and defined $userProblemIDs[$i]->[2];
1513 :     }
1514 :    
1515 : sh002i 1583 my @UserProblems = $self->getUserProblems(@userProblemIDs);
1516 :    
1517 :     my @globalProblemIDs = map { [ $_->[1], $_->[2] ] } @userProblemIDs;
1518 :     my @GlobalProblems = $self->getGlobalProblems(@globalProblemIDs);
1519 :    
1520 :     my %globalProblemFields = map { $_ => 1 } $self->newGlobalProblem->FIELDS;
1521 :     my @commonFields = grep { exists $globalProblemFields{$_} } $self->newUserProblem->FIELDS;
1522 :    
1523 :     for (my $i = 0; $i < @UserProblems; $i++) {
1524 :     my $UserProblem = $UserProblems[$i];
1525 :     my $GlobalProblem = $GlobalProblems[$i];
1526 :     next unless $UserProblem and $GlobalProblem;
1527 :     foreach my $field (@commonFields) {
1528 : sh002i 1587 next if defined $UserProblem->$field;
1529 : sh002i 1583 $UserProblem->$field($GlobalProblem->$field);
1530 :     }
1531 :     }
1532 :    
1533 :     return @UserProblems;
1534 : sh002i 1512 }
1535 :    
1536 : sh002i 1583 =back
1537 :    
1538 :     =cut
1539 :    
1540 : sh002i 808 ################################################################################
1541 :     # debugging
1542 :     ################################################################################
1543 :    
1544 : sh002i 1583 #sub dumpDB($$) {
1545 :     # my ($self, $table) = @_;
1546 :     # return $self->{$table}->dumpDB();
1547 :     #}
1548 : sh002i 808
1549 : sh002i 1199 ################################################################################
1550 :     # sanity checking
1551 :     ################################################################################
1552 :    
1553 :     sub checkKeyfields($) {
1554 :     my ($Record) = @_;
1555 :     foreach my $keyfield ($Record->KEYFIELDS) {
1556 : sh002i 1226 my $value = $Record->$keyfield;
1557 :     croak "checkKeyfields: $keyfield is empty"
1558 :     unless defined $value and $value ne "";
1559 :    
1560 :     if ($keyfield eq "problem_id") {
1561 :     croak "checkKeyfields: invalid characters in $keyfield field: $value (valid characters are [0-9])"
1562 :     unless $value =~ m/^\d*$/;
1563 :     } else {
1564 :     croak "checkKeyfields: invalid characters in $keyfield field: $value (valid characters are [A-Za-z0-9_])"
1565 : malsyned 1299 unless $value =~ m/^[\w-]*$/;
1566 : sh002i 1226 }
1567 : sh002i 1199 }
1568 :     }
1569 :    
1570 : sh002i 1012 =head1 AUTHOR
1571 :    
1572 :     Written by Sam Hathaway, sh002i (at) math.rochester.edu.
1573 :    
1574 : sh002i 1035 =cut
1575 : gage 1023
1576 : sh002i 775 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9