[system] / trunk / webwork-modperl / lib / WeBWorK / DB.pm Repository:
ViewVC logotype

View of /trunk/webwork-modperl/lib/WeBWorK/DB.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1541 - (download) (as text) (annotate)
Sat Sep 27 19:23:27 2003 UTC (9 years, 7 months ago) by gage
File size: 42666 byte(s)
Fixed spelling on "getMergedSets"
--Mike

    1 ################################################################################
    2 # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project
    3 # $Id$
    4 ################################################################################
    5 
    6 package WeBWorK::DB;
    7 
    8 =head1 NAME
    9 
   10 WeBWorK::DB - interface with the WeBWorK databases.
   11 
   12 =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 =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 class, data source, and additional parameters are specified by the C<%dbLayout>
   37 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             / new* list* exists* add* get* put* delete* \             <- api
   50  +------------------------------------------------------------------+
   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 in a single hash in the WW1Hash schema, and the encoding scheme used.
   61 
   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 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 
  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 =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 =cut
  127 
  128 use strict;
  129 use warnings;
  130 use Carp;
  131 use Data::Dumper;
  132 use WeBWorK::Timing;
  133 use WeBWorK::Utils qw(runtime_use);
  134 
  135 ################################################################################
  136 # constructor
  137 ################################################################################
  138 
  139 =head1 CONSTRUCTOR
  140 
  141 =over
  142 
  143 =item new($ce)
  144 
  145 The C<new> method creates a DB object and brings up the underlying
  146 schema/driver structure according to the C<%dbLayout> hash in C<$ce>, a
  147 WeBWorK::CourseEnvironment object.
  148 
  149 =back
  150 
  151 =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 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 =cut
  190 
  191 sub new($$) {
  192   my ($invocant, $ce) = @_;
  193   my $class = ref($invocant) || $invocant;
  194   my $self = {};
  195   bless $self, $class; # bless this here so we can pass it to the schema
  196 
  197   # load the modules required to handle each table, and create driver
  198   my %dbLayout = %{$ce->{dbLayout}};
  199   foreach my $table (keys %dbLayout) {
  200     my $layout = $dbLayout{$table};
  201     my $record = $layout->{record};
  202     my $schema = $layout->{schema};
  203     my $driver = $layout->{driver};
  204     my $source = $layout->{source};
  205     my $params = $layout->{params};
  206 
  207     runtime_use($record);
  208 
  209     runtime_use($driver);
  210     my $driverObject = eval { $driver->new($source, $params) };
  211     croak "error instantiating DB driver $driver for table $table: $@"
  212       if $@;
  213 
  214     runtime_use($schema);
  215     my $schemaObject = eval { $schema->new(
  216       $self, $driver->new($source, $params),
  217       $table, $record, $params) };
  218     croak "error instantiating DB schema $schema for table $table: $@"
  219       if $@;
  220 
  221     $self->{$table} = $schemaObject;
  222   }
  223 
  224   return $self;
  225 }
  226 
  227 =head1 METHODS
  228 
  229 =cut
  230 
  231 ################################################################################
  232 # password functions
  233 ################################################################################
  234 
  235 =head2 Password Methods
  236 
  237 =over
  238 
  239 =item newPassword()
  240 
  241 Returns a new, empty password object.
  242 
  243 =cut
  244 
  245 sub newPassword {
  246   my ($self, $prototype) = @_;
  247   return $self->{password}->{record}->new($prototype);
  248 }
  249 
  250 =item listPasswords()
  251 
  252 Returns a list of user IDs representing the records in the password table.
  253 
  254 =cut
  255 
  256 sub listPasswords {
  257   my ($self) = @_;
  258 
  259   croak "listPasswords: requires 0 arguments"
  260     unless @_ == 1;
  261 
  262   return map { $_->[0] }
  263     $self->{password}->list(undef);
  264 }
  265 
  266 =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 sub addPassword {
  276   my ($self, $Password) = @_;
  277 
  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     unless $self->{user}->exists($Password->user_id);
  286 
  287   checkKeyfields($Password);
  288 
  289   return $self->{password}->add($Password);
  290 }
  291 
  292 =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 sub getPassword {
  301   my ($self, $userID) = @_;
  302 
  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   return $self->{password}->get($userID);
  309 }
  310 
  311 =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   return map { $self->getPassword($_) } @userIDs;
  330 }
  331 
  332 =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 sub putPassword($$) {
  342   my ($self, $Password) = @_;
  343 
  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   checkKeyfields($Password);
  352 
  353   return $self->{password}->put($Password);
  354 }
  355 
  356 =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 sub deletePassword($$) {
  365   my ($self, $userID) = @_;
  366 
  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   return $self->{password}->delete($userID);
  373 }
  374 
  375 =back
  376 
  377 =cut
  378 
  379 ################################################################################
  380 # permission functions
  381 ################################################################################
  382 
  383 =head2 Permission Level Methods
  384 
  385 =over
  386 
  387 =item newPermissionLevel()
  388 
  389 Returns a new, empty permission level object.
  390 
  391 =cut
  392 
  393 sub newPermissionLevel {
  394   my ($self, $prototype) = @_;
  395   return $self->{permission}->{record}->new($prototype);
  396 }
  397 
  398 =item listPermissionLevels()
  399 
  400 Returns a list of user IDs representing the records in the permission table.
  401 
  402 =cut
  403 
  404 sub listPermissionLevels($) {
  405   my ($self) = @_;
  406 
  407   croak "listPermissionLevels: requires 0 arguments"
  408     unless @_ == 1;
  409 
  410   return map { $_->[0] }
  411     $self->{permission}->list(undef);
  412 }
  413 
  414 =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 sub addPermissionLevel($$) {
  424   my ($self, $PermissionLevel) = @_;
  425 
  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     unless $self->{user}->exists($PermissionLevel->user_id);
  434 
  435   checkKeyfields($PermissionLevel);
  436 
  437   return $self->{permission}->add($PermissionLevel);
  438 }
  439 
  440 =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 sub getPermissionLevel($$) {
  449   my ($self, $userID) = @_;
  450 
  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   return $self->{permission}->get($userID);
  457 }
  458 
  459 =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   return map { $self->getPermissionLevel($_) } @userIDs;
  478 }
  479 
  480 =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 sub putPermissionLevel($$) {
  490   my ($self, $PermissionLevel) = @_;
  491 
  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   checkKeyfields($PermissionLevel);
  500 
  501   return $self->{permission}->put($PermissionLevel);
  502 }
  503 
  504 =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 sub deletePermissionLevel($$) {
  513   my ($self, $userID) = @_;
  514 
  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   return $self->{permission}->delete($userID);
  521 }
  522 
  523 ################################################################################
  524 # key functions
  525 ################################################################################
  526 
  527 =head2 Key Methods
  528 
  529 =over
  530 
  531 =item newKey()
  532 
  533 Returns a new, empty key object.
  534 
  535 =cut
  536 
  537 sub newKey {
  538   my ($self, $prototype) = @_;
  539   return $self->{key}->{record}->new($prototype);
  540 }
  541 
  542 =item listKeys()
  543 
  544 Returns a list of user IDs representing the records in the key table.
  545 
  546 =cut
  547 
  548 sub listKeys($) {
  549   my ($self) = @_;
  550 
  551   croak "listKeys: requires 0 arguments"
  552     unless @_ == 1;
  553 
  554   return map { $_->[0] }
  555     $self->{key}->list(undef);
  556 }
  557 
  558 =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 sub addKey($$) {
  568   my ($self, $Key) = @_;
  569 
  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     unless $self->{user}->exists($Key->user_id);
  578 
  579   checkKeyfields($Key);
  580 
  581   return $self->{key}->add($Key);
  582 }
  583 
  584 =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 sub getKey($$) {
  593   my ($self, $userID) = @_;
  594 
  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   return $self->{key}->get($userID);
  601 }
  602 
  603 =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   return map { $self->getKey($_) } @userIDs;
  622 }
  623 
  624 =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 sub putKey($$) {
  633   my ($self, $Key) = @_;
  634 
  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   checkKeyfields($Key);
  643 
  644   return $self->{key}->put($Key);
  645 }
  646 
  647 =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 sub deleteKey($$) {
  656   my ($self, $userID) = @_;
  657 
  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   return $self->{key}->delete($userID);
  664 }
  665 
  666 ################################################################################
  667 # user functions
  668 ################################################################################
  669 
  670 =head2 User Methods
  671 
  672 =over
  673 
  674 =item newUser()
  675 
  676 Returns a new, empty user object.
  677 
  678 =cut
  679 
  680 sub newUser {
  681   my ($self, $prototype) = @_;
  682   return $self->{user}->{record}->new($prototype);
  683 }
  684 
  685 =item listUsers()
  686 
  687 Returns a list of user IDs representing the records in the user table.
  688 
  689 =cut
  690 
  691 sub listUsers($) {
  692   my ($self) = @_;
  693 
  694   croak "listUsers: requires 0 arguments"
  695     unless @_ == 1;
  696 
  697   return map { $_->[0] }
  698     $self->{user}->list(undef);
  699 }
  700 
  701 =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 sub addUser($$) {
  710   my ($self, $User) = @_;
  711 
  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   checkKeyfields($User);
  720 
  721   return $self->{user}->add($User);
  722 }
  723 
  724 =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 sub getUser($$) {
  733   my ($self, $userID) = @_;
  734 
  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   return $self->{user}->get($userID);
  741 }
  742 
  743 =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   return map { $self->getUser($_) } @userIDs;
  762 }
  763 
  764 =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 sub putUser($$) {
  773   my ($self, $User) = @_;
  774 
  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   checkKeyfields($User);
  783 
  784   return $self->{user}->put($User);
  785 }
  786 
  787 =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 sub deleteUser($$) {
  798   my ($self, $userID) = @_;
  799 
  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   #$self->deleteUserSet($userID, $_)
  806   # foreach $self->listUserSets($userID);
  807   $self->deleteUserSet($userID, undef);
  808   $self->deletePassword($userID);
  809   $self->deletePermissionLevel($userID);
  810   $self->deleteKey($userID);
  811   return $self->{user}->delete($userID);
  812 }
  813 
  814 ################################################################################
  815 # set functions
  816 ################################################################################
  817 
  818 sub newGlobalSet {
  819   my ($self, $prototype) = @_;
  820   return $self->{set}->{record}->new($prototype);
  821 }
  822 
  823 sub listGlobalSets($) {
  824   my ($self) = @_;
  825 
  826   croak "listGlobalSets: requires 0 arguments"
  827     unless @_ == 1;
  828 
  829   return map { $_->[0] }
  830     $self->{set}->list(undef);
  831 }
  832 
  833 sub addGlobalSet($$) {
  834   my ($self, $GlobalSet) = @_;
  835 
  836   croak "addGlobalSet: requires 1 argument"
  837     unless @_ == 2;
  838   croak "addGlobalSet: argument 1 must be of type ", $self->{set}->{record}
  839     unless ref $GlobalSet eq $self->{set}->{record};
  840   croak "addGlobalSet: global set exists (perhaps you meant to use putGlobalSet?)"
  841     if $self->{set}->exists($GlobalSet->set_id);
  842 
  843   checkKeyfields($GlobalSet);
  844 
  845   return $self->{set}->add($GlobalSet);
  846 }
  847 
  848 sub getGlobalSet($$) {
  849   my ($self, $setID) = @_;
  850 
  851   croak "getGlobalSet: requires 1 argument"
  852     unless @_ == 2;
  853   croak "getGlobalSet: argument 1 must contain a set_id"
  854     unless defined $setID;
  855 
  856   return $self->{set}->get($setID);
  857 }
  858 
  859 =item getGlobalSets(@setIDs)
  860 
  861 Return a list of global set records associated with the user IDs given. If there
  862 is no record associated with a given user ID, that element of the list will be
  863 undefined.
  864 
  865 =cut
  866 
  867 sub getGlobalSets {
  868   my ($self, @setIDs) = @_;
  869 
  870   croak "getGlobalSets: requires 1 or more argument"
  871     unless @_ >= 2;
  872   foreach my $i (0 .. $#setIDs) {
  873     croak "getGlobalSets: element $i of argument list must contain a set_id"
  874       unless defined $setIDs[$i];
  875   }
  876 
  877   return map { $self->getGlobalSet($_) } @setIDs;
  878 }
  879 
  880 sub putGlobalSet($$) {
  881   my ($self, $GlobalSet) = @_;
  882 
  883   croak "putGlobalSet: requires 1 argument"
  884     unless @_ == 2;
  885   croak "putGlobalSet: argument 1 must be of type ", $self->{set}->{record}
  886     unless ref $GlobalSet eq $self->{set}->{record};
  887   croak "putGlobalSet: global set not found (perhaps you meant to use addGlobalSet?)"
  888     unless $self->{set}->exists($GlobalSet->set_id);
  889 
  890   checkKeyfields($GlobalSet);
  891 
  892   return $self->{set}->put($GlobalSet);
  893 }
  894 
  895 sub deleteGlobalSet($$) {
  896   my ($self, $setID) = @_;
  897 
  898   croak "deleteGlobalSet: requires 1 argument"
  899     unless @_ == 2;
  900   croak "deleteGlobalSet: argument 1 must contain a set_id"
  901     unless defined $setID or caller eq __PACKAGE__;
  902 
  903   #$self->deleteUserSet($_, $setID)
  904   # foreach $self->listSetUsers($setID);
  905   #$self->deleteGlobalProblem($setID, $_)
  906   # foreach $self->listGlobalProblems($setID);
  907   $self->deleteUserSet(undef, $setID);
  908   $self->deleteGlobalProblem($setID, undef);
  909   return $self->{set}->delete($setID);
  910 }
  911 
  912 ################################################################################
  913 # set_user functions
  914 ################################################################################
  915 
  916 sub newUserSet {
  917   my ($self, $prototype) = @_;
  918   return $self->{set_user}->{record}->new($prototype);
  919 }
  920 
  921 sub listSetUsers($$) {
  922   my ($self, $setID) = @_;
  923 
  924   croak "listSetUsers: requires 1 argument"
  925     unless @_ == 2;
  926   croak "listSetUsers: argument 1 must contain a set_id"
  927     unless defined $setID;
  928 
  929   return map { $_->[0] } # extract user_id
  930     $self->{set_user}->list(undef, $setID);
  931 }
  932 
  933 sub listUserSets($$) {
  934   my ($self, $userID) = @_;
  935 
  936   croak "listUserSets: requires 1 argument"
  937     unless @_ == 2;
  938   croak "listUserSets: argument 1 must contain a user_id"
  939     unless defined $userID;
  940 
  941   return map { $_->[1] } # extract set_id
  942     $self->{set_user}->list($userID, undef);
  943 }
  944 
  945 sub addUserSet($$) {
  946   my ($self, $UserSet) = @_;
  947 
  948   croak "addUserSet: requires 1 argument"
  949     unless @_ == 2;
  950   croak "addUserSet: argument 1 must be of type ", $self->{set_user}->{record}
  951     unless ref $UserSet eq $self->{set_user}->{record};
  952   croak "addUserSet: user set exists (perhaps you meant to use putUserSet?)"
  953     if $self->{set_user}->exists($UserSet->user_id, $UserSet->set_id);
  954   croak "addUserSet: user ", $UserSet->user_id, " not found"
  955     unless $self->{user}->exists($UserSet->user_id);
  956   croak "addUserSet: set ", $UserSet->set_id, " not found"
  957     unless $self->{set}->exists($UserSet->set_id);
  958 
  959   checkKeyfields($UserSet);
  960 
  961   return $self->{set_user}->add($UserSet);
  962 }
  963 
  964 sub getUserSet($$$) {
  965   my ($self, $userID, $setID) = @_;
  966 
  967   croak "getUserSet: requires 2 arguments"
  968     unless @_ == 3;
  969   croak "getUserSet: argument 1 must contain a user_id"
  970     unless defined $userID;
  971   croak "getUserSet: argument 2 must contain a set_id"
  972     unless defined $setID;
  973 
  974   return $self->{set_user}->get($userID, $setID);
  975 }
  976 
  977 =item getUserSets(@userSetIDs)
  978 
  979 Return a list of user set records associated with the user IDs given. If there
  980 is no record associated with a given user ID, that element of the list will be
  981 undefined. @userProblemIDs consists of references to arrays in which the first
  982 element is the user_id and the second element is the set_id.
  983 
  984 =cut
  985 
  986 sub getUserSets {
  987   my ($self, @userSetIDs) = @_;
  988 
  989   croak "getUserSets: requires 1 or more argument"
  990     unless @_ >= 2;
  991   foreach my $i (0 .. $#userSetIDs) {
  992     croak "getUserSets: element $i of argument list must contain a <user_id, set_id> pair"
  993       unless defined $userSetIDs[$i]
  994              and ref $userSetIDs[$i] eq "ARRAY"
  995              and @{$userSetIDs[$i]} == 2
  996              and defined $userSetIDs[$i]->[0]
  997              and defined $userSetIDs[$i]->[1];
  998   }
  999 
 1000   return map { $self->getUserSet(@{$_}) } @userSetIDs;
 1001 }
 1002 
 1003 sub putUserSet($$) {
 1004   my ($self, $UserSet) = @_;
 1005 
 1006   croak "putUserSet: requires 1 argument"
 1007     unless @_ == 2;
 1008   croak "putUserSet: argument 1 must be of type ", $self->{set_user}->{record}
 1009     unless ref $UserSet eq $self->{set_user}->{record};
 1010   croak "putUserSet: user set not found (perhaps you meant to use addUserSet?)"
 1011     unless $self->{set_user}->exists($UserSet->user_id, $UserSet->set_id);
 1012   croak "putUserSet: user ", $UserSet->user_id, " not found"
 1013     unless $self->{user}->exists($UserSet->user_id);
 1014   croak "putUserSet: set ", $UserSet->set_id, " not found"
 1015     unless $self->{set}->exists($UserSet->set_id);
 1016 
 1017   checkKeyfields($UserSet);
 1018 
 1019   return $self->{set_user}->put($UserSet);
 1020 }
 1021 
 1022 sub deleteUserSet($$$) {
 1023   my ($self, $userID, $setID) = @_;
 1024 
 1025   croak "getUserSet: requires 2 arguments"
 1026     unless @_ == 3;
 1027   croak "getUserSet: argument 1 must contain a user_id"
 1028     unless defined $userID or caller eq __PACKAGE__;
 1029   croak "getUserSet: argument 2 must contain a set_id"
 1030     unless defined $userID or caller eq __PACKAGE__;
 1031 
 1032   #$self->deleteUserProblem($userID, $setID, $_)
 1033   # foreach $self->listUserProblems($userID, $setID);
 1034   $self->deleteUserProblem($userID, $setID, undef);
 1035   return $self->{set_user}->delete($userID, $setID);
 1036 }
 1037 
 1038 ################################################################################
 1039 # problem functions
 1040 ################################################################################
 1041 
 1042 sub newGlobalProblem {
 1043   my ($self, $prototype) = @_;
 1044   return $self->{problem}->{record}->new($prototype);
 1045 }
 1046 
 1047 sub listGlobalProblems($$) {
 1048   my ($self, $setID) = @_;
 1049 
 1050   croak "listGlobalProblems: requires 1 arguments"
 1051     unless @_ == 2;
 1052   croak "listGlobalProblems: argument 1 must contain a set_id"
 1053     unless defined $setID;
 1054 
 1055   return map { $_->[1] }
 1056     $self->{problem}->list($setID, undef);
 1057 }
 1058 
 1059 sub addGlobalProblem($$) {
 1060   my ($self, $GlobalProblem) = @_;
 1061 
 1062   croak "addGlobalProblem: requires 1 argument"
 1063     unless @_ == 2;
 1064   croak "addGlobalProblem: argument 1 must be of type ", $self->{problem}->{record}
 1065     unless ref $GlobalProblem eq $self->{problem}->{record};
 1066   croak "addGlobalProblem: global problem exists (perhaps you meant to use putGlobalProblem?)"
 1067     if $self->{problem}->exists($GlobalProblem->set_id, $GlobalProblem->problem_id);
 1068   croak "addGlobalProblem: set ", $GlobalProblem->set_id, " not found"
 1069     unless $self->{set}->exists($GlobalProblem->set_id);
 1070 
 1071   checkKeyfields($GlobalProblem);
 1072 
 1073   return $self->{problem}->add($GlobalProblem);
 1074 }
 1075 
 1076 sub getGlobalProblem($$$) {
 1077   my ($self, $setID, $problemID) = @_;
 1078 
 1079   croak "getGlobalProblem: requires 2 arguments"
 1080     unless @_ == 3;
 1081   croak "getGlobalProblem: argument 1 must contain a set_id"
 1082     unless defined $setID;
 1083   croak "getGlobalProblem: argument 2 must contain a problem_id"
 1084     unless defined $problemID;
 1085 
 1086   return $self->{problem}->get($setID, $problemID);
 1087 }
 1088 
 1089 =item getGlobalProblems(@problemIDs)
 1090 
 1091 Return a list of global set records associated with the user IDs given. If there
 1092 is no record associated with a given user ID, that element of the list will be
 1093 undefined. @problemIDs consists of references to arrays in which the first
 1094 element is the set_id, and the second element is the problem_id.
 1095 
 1096 =cut
 1097 
 1098 sub getGlobalProblems {
 1099   my ($self, @problemIDs) = @_;
 1100 
 1101   croak "getGlobalProblems: requires 1 or more argument"
 1102     unless @_ >= 2;
 1103   foreach my $i (0 .. $#problemIDs) {
 1104     croak "getUserSets: element $i of argument list must contain a <set_id, problem_id> pair"
 1105       unless defined $problemIDs[$i]
 1106              and ref $problemIDs[$i] eq "ARRAY"
 1107              and @{$problemIDs[$i]} == 2
 1108              and defined $problemIDs[$i]->[0]
 1109              and defined $problemIDs[$i]->[1];
 1110   }
 1111 
 1112   return map { $self->getGlobalProblem(@{$_}) } @problemIDs;
 1113 }
 1114 
 1115 sub putGlobalProblem($$) {
 1116   my ($self, $GlobalProblem) = @_;
 1117 
 1118   croak "putGlobalProblem: requires 1 argument"
 1119     unless @_ == 2;
 1120   croak "putGlobalProblem: argument 1 must be of type ", $self->{problem}->{record}
 1121     unless ref $GlobalProblem eq $self->{problem}->{record};
 1122   croak "putGlobalProblem: global problem not found (perhaps you meant to use addGlobalProblem?)"
 1123     unless $self->{problem}->exists($GlobalProblem->set_id, $GlobalProblem->problem_id);
 1124   croak "putGlobalProblem: set ", $GlobalProblem->set_id, " not found"
 1125     unless $self->{set}->exists($GlobalProblem->set_id);
 1126 
 1127   checkKeyfields($GlobalProblem);
 1128 
 1129   return $self->{problem}->put($GlobalProblem);
 1130 }
 1131 
 1132 sub deleteGlobalProblem($$$) {
 1133   my ($self, $setID, $problemID) = @_;
 1134 
 1135   croak "deleteGlobalProblem: requires 2 arguments"
 1136     unless @_ == 3;
 1137   croak "deleteGlobalProblem: argument 1 must contain a set_id"
 1138     unless defined $setID or caller eq __PACKAGE__;
 1139   croak "deleteGlobalProblem: argument 2 must contain a problem_id"
 1140     unless defined $problemID or caller eq __PACKAGE__;
 1141 
 1142   #$self->deleteUserProblem($_, $setID, $problemID)
 1143   # foreach $self->listProblemUsers($setID, $problemID);
 1144   $self->deleteUserProblem(undef, $setID, $problemID);
 1145   return $self->{problem}->delete($setID, $problemID);
 1146 }
 1147 
 1148 ################################################################################
 1149 # problem_user functions
 1150 ################################################################################
 1151 
 1152 sub newUserProblem {
 1153   my ($self, $prototype) = @_;
 1154   return $self->{problem_user}->{record}->new($prototype);
 1155 }
 1156 
 1157 sub listProblemUsers($$$) {
 1158   my ($self, $setID, $problemID) = @_;
 1159 
 1160   croak "listProblemUsers: requires 2 arguments"
 1161     unless @_ == 3;
 1162   croak "listProblemUsers: argument 1 must contain a set_id"
 1163     unless defined $setID;
 1164   croak "listProblemUsers: argument 2 must contain a problem_id"
 1165     unless defined $problemID;
 1166 
 1167   return map { $_->[0] } # extract user_id
 1168     $self->{problem_user}->list(undef, $setID, $problemID);
 1169 }
 1170 
 1171 sub listUserProblems($$$) {
 1172   my ($self, $userID, $setID) = @_;
 1173 
 1174   croak "listUserProblems: requires 2 arguments"
 1175     unless @_ == 3;
 1176   croak "listUserProblems: argument 1 must contain a user_id"
 1177     unless defined $userID;
 1178   croak "listUserProblems: argument 2 must contain a set_id"
 1179     unless defined $setID;
 1180 
 1181   return map { $_->[2] } # extract problem_id
 1182     $self->{problem_user}->list($userID, $setID, undef);
 1183 }
 1184 
 1185 sub addUserProblem($$) {
 1186   my ($self, $UserProblem) = @_;
 1187 
 1188   croak "addUserProblem: requires 1 argument"
 1189     unless @_ == 2;
 1190   croak "addUserProblem: argument 1 must be of type ", $self->{problem_user}->{record}
 1191     unless ref $UserProblem eq $self->{problem_user}->{record};
 1192   croak "addUserProblem: user problem exists (perhaps you meant to use putUserProblem?)"
 1193     if $self->{problem_user}->exists($UserProblem->user_id, $UserProblem->set_id, $UserProblem->problem_id);
 1194   croak "addUserProblem: user set ", $UserProblem->set_id, " for user ", $UserProblem->user_id, " not found"
 1195     unless $self->{set_user}->exists($UserProblem->user_id, $UserProblem->set_id);
 1196   croak "addUserProblem: problem ", $UserProblem->problem_id, " in set ", $UserProblem->set_id, " not found"
 1197     unless $self->{problem}->exists($UserProblem->set_id, $UserProblem->problem_id);
 1198 
 1199   checkKeyfields($UserProblem);
 1200 
 1201   return $self->{problem_user}->add($UserProblem);
 1202 }
 1203 
 1204 sub getUserProblem($$$$) {
 1205   my ($self, $userID, $setID, $problemID) = @_;
 1206 
 1207   croak "getUserProblem: requires 3 arguments"
 1208     unless @_ == 4;
 1209   croak "getUserProblem: argument 1 must contain a user_id"
 1210     unless defined $userID;
 1211   croak "getUserProblem: argument 2 must contain a set_id"
 1212     unless defined $setID;
 1213   croak "getUserProblem: argument 3 must contain a problem_id"
 1214     unless defined $problemID;
 1215 
 1216   return $self->{problem_user}->get($userID, $setID, $problemID);
 1217 }
 1218 
 1219 =item getUserProblems(@userProblemIDs)
 1220 
 1221 Return a list of user set records associated with the user IDs given. If there
 1222 is no record associated with a given user ID, that element of the list will be
 1223 undefined. @userProblemIDs consists of references to arrays in which the first
 1224 element is the user_id, the second element is the set_id, and the third element
 1225 is the problem_id.
 1226 
 1227 =cut
 1228 
 1229 sub getUserProblems {
 1230   my ($self, @userProblemIDs) = @_;
 1231 
 1232   croak "getUserProblems: requires 1 or more argument"
 1233     unless @_ >= 2;
 1234   foreach my $i (0 .. $#userProblemIDs) {
 1235     croak "getUserProblems: element $i of argument list must contain a <user_id, set_id, problem_id> triple"
 1236       unless defined $userProblemIDs[$i]
 1237              and ref $userProblemIDs[$i] eq "ARRAY"
 1238              and @{$userProblemIDs[$i]} == 3
 1239              and defined $userProblemIDs[$i]->[0]
 1240              and defined $userProblemIDs[$i]->[1]
 1241              and defined $userProblemIDs[$i]->[2];
 1242   }
 1243 
 1244   return map { $self->getUserProblem(@{$_}) } @userProblemIDs;
 1245 }
 1246 
 1247 sub putUserProblem($$) {
 1248   my ($self, $UserProblem) = @_;
 1249 
 1250   croak "putUserProblem: requires 1 argument"
 1251     unless @_ == 2;
 1252   croak "putUserProblem: argument 1 must be of type ", $self->{problem_user}->{record}
 1253     unless ref $UserProblem eq $self->{problem_user}->{record};
 1254   croak "putUserProblem: user set ", $UserProblem->set_id, " for user ", $UserProblem->user_id, " not found"
 1255     unless $self->{set_user}->exists($UserProblem->user_id, $UserProblem->set_id);
 1256   croak "putUserProblem: user problem not found (perhaps you meant to use addUserProblem?)"
 1257     unless $self->{problem_user}->exists($UserProblem->user_id, $UserProblem->set_id, $UserProblem->problem_id);
 1258   croak "putUserProblem: problem ", $UserProblem->problem_id, " in set ", $UserProblem->set_id, " not found"
 1259     unless $self->{problem}->exists($UserProblem->set_id, $UserProblem->problem_id);
 1260 
 1261   checkKeyfields($UserProblem);
 1262 
 1263   return $self->{problem_user}->put($UserProblem);
 1264 }
 1265 
 1266 sub deleteUserProblem($$$$) {
 1267   my ($self, $userID, $setID, $problemID) = @_;
 1268 
 1269   croak "getUserProblem: requires 3 arguments"
 1270     unless @_ == 4;
 1271   croak "getUserProblem: argument 1 must contain a user_id"
 1272     unless defined $userID or caller eq __PACKAGE__;
 1273   croak "getUserProblem: argument 2 must contain a set_id"
 1274     unless defined $setID or caller eq __PACKAGE__;
 1275   croak "getUserProblem: argument 3 must contain a problem_id"
 1276     unless defined $problemID or caller eq __PACKAGE__;
 1277 
 1278   return $self->{problem_user}->delete($userID, $setID, $problemID);
 1279 }
 1280 
 1281 ################################################################################
 1282 # set+set_user functions
 1283 ################################################################################
 1284 
 1285 sub getGlobalUserSet {
 1286   carp "getGlobalUserSet: this method is deprecated -- use getMergedSet instead";
 1287   return shift->getMergedSet(@_);
 1288 }
 1289 
 1290 sub getMergedSet {
 1291   my ($self, $userID, $setID) = @_;
 1292 
 1293   #my $timer = WeBWorK::Timing->new("getMergedSet");
 1294 
 1295   croak "getMergedSet: requires 2 arguments"
 1296     unless @_ == 3;
 1297   croak "getMergedSet: argument 1 must contain a user_id"
 1298     unless defined $userID;
 1299   croak "getMergedSet: argument 2 must contain a set_id"
 1300     unless defined $setID;
 1301 
 1302   #$timer->start;
 1303   my $UserSet = $self->getUserSet($userID, $setID);
 1304   #$timer->continue("got user set");
 1305   return unless $UserSet;
 1306   my $GlobalSet = $self->getGlobalSet($setID);
 1307   #$timer->continue("got global set");
 1308   if ($GlobalSet) {
 1309     foreach ($UserSet->FIELDS()) {
 1310       next unless $GlobalSet->can($_);
 1311       next if $UserSet->$_();
 1312       $UserSet->$_($GlobalSet->$_());
 1313     }
 1314   }
 1315   #$timer->continue("merged records");
 1316   #$timer->stop;
 1317   return $UserSet;
 1318 }
 1319 
 1320 
 1321 =item geMegedSets(@userSetIDs)
 1322 
 1323 
 1324 Return a list of merged set records associated with the user IDs given. If there
 1325 is no record associated with a given user ID, that element of the list will be
 1326 undefined. @userSetIDs consists of references to arrays in which the first
 1327 element is the user_id and the second element is the set_id.
 1328 
 1329 
 1330 =cut
 1331 
 1332 
 1333 sub getMergedSets {
 1334   my ($self, @userSetIDs) = @_;
 1335 
 1336   croak "getMergedSets: requires 1 or more argument"
 1337     unless @_ >= 2;
 1338   foreach my $i (0 .. $#userSetIDs) {
 1339     croak "getMergedSets: element $i of argument list must contain a <user_id, set_id> pair"
 1340       unless defined $userSetIDs[$i]
 1341              and ref $userSetIDs[$i] eq "ARRAY"
 1342              and @{$userSetIDs[$i]} == 2
 1343              and defined $userSetIDs[$i]->[0]
 1344              and defined $userSetIDs[$i]->[1];
 1345   }
 1346 
 1347   return map { $self->getMergedSet(@{$_}) } @userSetIDs;
 1348 
 1349 }
 1350 
 1351 
 1352 
 1353 ################################################################################
 1354 # problem+problem_user functions
 1355 ################################################################################
 1356 
 1357 sub getGlobalUserProblem {
 1358   carp "getGlobalUserProblem: this method is deprecated -- use getMergedProblem instead";
 1359   return shift->getMergedProblem(@_);
 1360 }
 1361 
 1362 sub getMergedProblem {
 1363   my ($self, $userID, $setID, $problemID) = @_;
 1364 
 1365   #my $timer = WeBWorK::Timing->new("getMergedSet");
 1366 
 1367   croak "getGlobalUserSet: requires 3 arguments"
 1368     unless @_ == 4;
 1369   croak "getGlobalUserSet: argument 1 must contain a user_id"
 1370     unless defined $userID;
 1371   croak "getGlobalUserSet: argument 2 must contain a set_id"
 1372     unless defined $setID;
 1373   croak "getGlobalUserSet: argument 3 must contain a problem_id"
 1374     unless defined $problemID;
 1375 
 1376   #$timer->start;
 1377   my $UserProblem = $self->getUserProblem($userID, $setID, $problemID);
 1378   #$timer->continue("got user problem");
 1379   return unless $UserProblem;
 1380   my $GlobalProblem = $self->getGlobalProblem($setID, $problemID);
 1381   #$timer->continue("got global problem");
 1382   if ($GlobalProblem) {
 1383     foreach ($UserProblem->FIELDS()) {
 1384       next unless $GlobalProblem->can($_);
 1385       next if $UserProblem->$_();
 1386       $UserProblem->$_($GlobalProblem->$_());
 1387     }
 1388   }
 1389   #$timer->continue("merged records");
 1390   #$timer->stop;
 1391   return $UserProblem;
 1392 }
 1393 
 1394 =item getMergedProblems(@userProblemIDs)
 1395 
 1396 Return a list of merged set records associated with the user IDs given. If there
 1397 is no record associated with a given user ID, that element of the list will be
 1398 undefined. @userProblemIDs consists of references to arrays in which the first
 1399 element is the user_id, the second element is the set_id, and the third element
 1400 is the problem_id.
 1401 
 1402 =cut
 1403 
 1404 sub getMergedProblems {
 1405   my ($self, @userProblemIDs) = @_;
 1406 
 1407   croak "getMergedProblems: requires 1 or more argument"
 1408     unless @_ >= 2;
 1409   foreach my $i (0 .. $#userProblemIDs) {
 1410     croak "getMergedProblems: element $i of argument list must contain a <user_id, set_id, problem_id> triple"
 1411       unless defined $userProblemIDs[$i]
 1412              and ref $userProblemIDs[$i] eq "ARRAY"
 1413              and @{$userProblemIDs[$i]} == 3
 1414              and defined $userProblemIDs[$i]->[0]
 1415              and defined $userProblemIDs[$i]->[1]
 1416              and defined $userProblemIDs[$i]->[2];
 1417   }
 1418 
 1419   return map { $self->getMergedProblem(@{$_}) } @userProblemIDs;
 1420 }
 1421 
 1422 ################################################################################
 1423 # debugging
 1424 ################################################################################
 1425 
 1426 sub dumpDB($$) {
 1427   my ($self, $table) = @_;
 1428   return $self->{$table}->dumpDB();
 1429 }
 1430 
 1431 ################################################################################
 1432 # sanity checking
 1433 ################################################################################
 1434 
 1435 sub checkKeyfields($) {
 1436   my ($Record) = @_;
 1437   foreach my $keyfield ($Record->KEYFIELDS) {
 1438     my $value = $Record->$keyfield;
 1439     croak "checkKeyfields: $keyfield is empty"
 1440       unless defined $value and $value ne "";
 1441 
 1442     if ($keyfield eq "problem_id") {
 1443       croak "checkKeyfields: invalid characters in $keyfield field: $value (valid characters are [0-9])"
 1444         unless $value =~ m/^\d*$/;
 1445     } else {
 1446       croak "checkKeyfields: invalid characters in $keyfield field: $value (valid characters are [A-Za-z0-9_])"
 1447         unless $value =~ m/^[\w-]*$/;
 1448     }
 1449   }
 1450 }
 1451 
 1452 =head1 AUTHOR
 1453 
 1454 Written by Sam Hathaway, sh002i (at) math.rochester.edu.
 1455 
 1456 =cut
 1457 
 1458 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9