[system] / trunk / webwork2 / lib / WeBWorK / DB.pm Repository:
ViewVC logotype

View of /trunk/webwork2/lib/WeBWorK/DB.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1023 - (download) (as text) (annotate)
Thu Jun 5 14:39:49 2003 UTC (9 years, 11 months ago) by gage
File size: 16491 byte(s)
added =cut
near end of DB.pm file.
before
1;

(I thought I had already commited this????)
--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                / 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 Data::Dumper;
  131 use WeBWorK::Utils qw(runtime_use);
  132 
  133 use constant TABLES => qw(password permission key user set set_user problem problem_user);
  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 $ce, a
  147 WeBWorK::CourseEnvironment object.
  148 
  149 =back
  150 
  151 =cut
  152 
  153 sub new($$) {
  154   my ($invocant, $ce) = @_;
  155   my $class = ref($invocant) || $invocant;
  156   my $self = {};
  157   bless $self, $class; # bless this here so we can pass it to the schema
  158 
  159   # load the modules required to handle each table, and create driver
  160   foreach my $table (TABLES) {
  161     unless (defined $ce->{dbLayout}->{$table}) {
  162       warn "ignoring table $table: layout not specified in dbLayout"; # ***
  163       next;
  164     }
  165 
  166     my $layout = $ce->{dbLayout}->{$table};
  167     my $record = $layout->{record};
  168     my $schema = $layout->{schema};
  169     my $driver = $layout->{driver};
  170     my $source = $layout->{source};
  171     my $params = $layout->{params};
  172 
  173     runtime_use($record);
  174     runtime_use($schema);
  175     runtime_use($driver);
  176     $self->{$table} = $schema->new(
  177       $self,
  178       $driver->new($source, $params),
  179       $table,
  180       $record,
  181       $params
  182     );
  183   }
  184 
  185   return $self;
  186 }
  187 
  188 =head1 METHODS
  189 
  190 =cut
  191 
  192 ################################################################################
  193 # password functions
  194 ################################################################################
  195 
  196 =head2 Password Methods
  197 
  198 =over
  199 
  200 =item listPasswords()
  201 
  202 Returns a list of user IDs representing the records in the password table.
  203 
  204 =cut
  205 
  206 sub listPasswords($) {
  207   my ($self) = @_;
  208   return map { $_->[0] }
  209     $self->{password}->list(undef);
  210 }
  211 
  212 =item addPassword($Password)
  213 
  214 $Password is a record object. The password will be added to the password table
  215 if a password with the same user ID does not already exist. If one does exist,
  216 an exception is thrown. To add a password, a user with a matching user ID must
  217 exist in the user table.
  218 
  219 =cut
  220 
  221 sub addPassword($$) {
  222   my ($self, $Password) = @_;
  223   die __PACKAGE__, ": addPassword($Password) failed: user not found.\n"
  224     unless $self->{user}->exists($Password->user_id);
  225   return $self->{password}->add($Password);
  226 }
  227 
  228 =item getPassword($userID)
  229 
  230 If a record with a matching user ID exists, a record object containting that
  231 record's data will be returned. If no such record exists, an undefined value
  232 will be returned.
  233 
  234 =cut
  235 
  236 sub getPassword($$) {
  237   my ($self, $userID) = @_;
  238   die __PACKAGE__, ": getPassword() failed: you must specify a userID.\n"
  239     unless $userID;
  240   return $self->{password}->get($userID);
  241 }
  242 
  243 =item putPassword($Password)
  244 
  245 $Password is a record object. If a password record with the same user ID exists
  246 in the password table, the data in the record is replaced with the data in
  247 $Password. If a matching password record does not exist, an exception is
  248 thrown.
  249 
  250 =cut
  251 
  252 sub putPassword($$) {
  253   my ($self, $Password) = @_;
  254   return $self->{password}->put($Password);
  255 }
  256 
  257 =item deletePassword($userID)
  258 
  259 If a password record with a user ID matching $userID exists in the password
  260 table, it is removed and the method returns a true value. If one does exist,
  261 a false value is returned.
  262 
  263 =cut
  264 
  265 sub deletePassword($$) {
  266   my ($self, $userID) = @_;
  267   return $self->{password}->delete($userID);
  268 }
  269 
  270 =back
  271 
  272 =cut
  273 
  274 ################################################################################
  275 # permission functions
  276 ################################################################################
  277 
  278 sub listPermissionLevels($) {
  279   my ($self) = @_;
  280   return map { $_->[0] }
  281     $self->{permission}->list(undef);
  282 }
  283 
  284 sub addPermissionLevel($$) {
  285   my ($self, $PermissionLevel) = @_;
  286   die "addPermissionLevel failed: user ", $PermissionLevel->user_id, " does not exist.\n"
  287     unless $self->{user}->exists($PermissionLevel->user_id);
  288   return $self->{permission}->add($PermissionLevel);
  289 }
  290 
  291 sub getPermissionLevel($$) {
  292   my ($self, $userID) = @_;
  293   return $self->{permission}->get($userID);
  294 }
  295 
  296 sub putPermissionLevel($$) {
  297   my ($self, $PermissionLevel) = @_;
  298   return $self->{permission}->put($PermissionLevel);
  299 }
  300 
  301 sub deletePermissionLevel($$) {
  302   my ($self, $userID) = @_;
  303   return $self->{permission}->delete($userID);
  304 }
  305 
  306 ################################################################################
  307 # key functions
  308 ################################################################################
  309 
  310 sub listKeys($) {
  311   my ($self) = @_;
  312   return map { $_->[0] }
  313     $self->{key}->list(undef);
  314 }
  315 
  316 sub addKey($$) {
  317   my ($self, $Key) = @_;
  318   die "addKey failed: user ", $Key->user_id, " does not exist.\n"
  319     unless $self->{user}->exists($Key->user_id);
  320   return $self->{key}->add($Key);
  321 }
  322 
  323 sub getKey($$) {
  324   my ($self, $userID) = @_;
  325   return $self->{key}->get($userID);
  326 }
  327 
  328 sub putKey($$) {
  329   my ($self, $Key) = @_;
  330   return $self->{key}->put($Key);
  331 }
  332 
  333 sub deleteKey($$) {
  334   my ($self, $userID) = @_;
  335   return $self->{key}->delete($userID);
  336 }
  337 
  338 ################################################################################
  339 # user functions
  340 ################################################################################
  341 
  342 sub listUsers($) {
  343   my ($self) = @_;
  344   return map { $_->[0] }
  345     $self->{user}->list(undef);
  346 }
  347 
  348 sub addUser($$) {
  349   my ($self, $User) = @_;
  350   return $self->{user}->add($User);
  351 }
  352 
  353 sub getUser($$) {
  354   my ($self, $userID) = @_;
  355   return $self->{user}->get($userID);
  356 }
  357 
  358 sub putUser($$) {
  359   my ($self, $User) = @_;
  360   return $self->{user}->put($User);
  361 }
  362 
  363 sub deleteUser($$) {
  364   my ($self, $userID) = @_;
  365   $self->deletePassword($userID);
  366   $self->deletePermissionLevel($userID);
  367   $self->deleteKey($userID);
  368   $self->deleteUserSet($userID, $_)
  369     foreach $self->listUsers();
  370   return $self->{user}->delete($userID);
  371 }
  372 
  373 ################################################################################
  374 # set functions
  375 ################################################################################
  376 
  377 sub listGlobalSets($) {
  378   my ($self) = @_;
  379   return map { $_->[0] }
  380     $self->{set}->list(undef);
  381 }
  382 
  383 sub addGlobalSet($$) {
  384   my ($self, $GlobalSet) = @_;
  385   return $self->{set}->add($GlobalSet);
  386 }
  387 
  388 sub getGlobalSet($$) {
  389   my ($self, $setID) = @_;
  390   return $self->{set}->get($setID);
  391 }
  392 
  393 sub putGlobalSet($$) {
  394   my ($self, $GlobalSet) = @_;
  395   return $self->{set}->put($GlobalSet);
  396 }
  397 
  398 sub deleteGlobalSet($$) {
  399   my ($self, $setID) = @_;
  400   $self->deleteGlobalProblem($setID, $_)
  401     foreach $self->listGlobalProblems($setID);
  402   $self->deleteUserSet($_, $setID)
  403     foreach $self->listUsers();
  404   return $self->{set}->delete($setID);
  405 }
  406 
  407 ################################################################################
  408 # set_user functions
  409 ################################################################################
  410 
  411 sub listSetUsers($$) {
  412   my ($self, $setID) = @_;
  413   return map { $_->[0] } # extract user_id
  414     $self->{set_user}->list(undef, $setID);
  415 }
  416 
  417 sub listUserSets($$) {
  418   my ($self, $userID) = @_;
  419   return map { $_->[1] } # extract set_id
  420     $self->{set_user}->list($userID, undef);
  421 }
  422 
  423 sub addUserSet($$) {
  424   my ($self, $UserSet) = @_;
  425   die "addUserSet failed: user ", $UserSet->user_id, " does not exist.\n"
  426     unless $self->{user}->exists($UserSet->user_id);
  427   die "addUserSet failed: set ", $UserSet->set_id, " does not exist.\n"
  428     unless $self->{set}->exists($UserSet->set_id);
  429   return $self->{set_user}->add($UserSet);
  430 }
  431 
  432 sub getUserSet($$$) {
  433   my ($self, $userID, $setID) = @_;
  434   return $self->{set_user}->get($userID, $setID);
  435 }
  436 
  437 sub putUserSet($$) {
  438   my ($self, $UserSet) = @_;
  439   return $self->{set_user}->put($UserSet);
  440 }
  441 
  442 sub deleteUserSet($$$) {
  443   my ($self, $userID, $setID) = @_;
  444   $self->deleteUserProblem($userID, $setID, $_)
  445     foreach $self->listUserProblems($userID, $setID);
  446   return $self->{set_user}->delete($userID, $setID);
  447 }
  448 
  449 ################################################################################
  450 # problem functions
  451 ################################################################################
  452 
  453 sub listGlobalProblems($$) {
  454   my ($self, $setID) = @_;
  455   return map { $_->[1] }
  456     #grep { $_->[0] eq $setID }
  457       $self->{problem}->list($setID, undef);
  458 }
  459 
  460 sub addGlobalProblem($$) {
  461   my ($self, $GlobalProblem) = @_;
  462   die "addGlobalProblem failed: set ", $GlobalProblem->set_id, " does not exist.\n"
  463     unless $self->{set}->exists($GlobalProblem->set_id);
  464   return $self->{problem}->add($GlobalProblem);
  465 }
  466 
  467 sub getGlobalProblem($$$) {
  468   my ($self, $setID, $problemID) = @_;
  469   return $self->{problem}->get($setID, $problemID);
  470 }
  471 
  472 sub putGlobalProblem($$) {
  473   my ($self, $GlobalProblem) = @_;
  474   return $self->{problem}->put($GlobalProblem);
  475 }
  476 
  477 sub deleteGlobalProblem($$$) {
  478   my ($self, $setID, $problemID) = @_;
  479   $self->deleteUserProblem($_, $setID, $problemID)
  480     foreach $self->listUsers();
  481   return $self->{problem}->delete($setID, $problemID);
  482 }
  483 
  484 ################################################################################
  485 # problem_user functions
  486 ################################################################################
  487 
  488 sub listProblemUsers($$$) {
  489   my ($self, $setID, $problemID) = @_;
  490   return map { $_->[0] } # extract user_id
  491     $self->{problem_user}->list(undef, $setID, $problemID);
  492 }
  493 
  494 sub listUserProblems($$$) {
  495   my ($self, $userID, $setID) = @_;
  496   return map { $_->[2] } # extract problem_id
  497     $self->{problem_user}->list($userID, $setID, undef);
  498 }
  499 
  500 sub addUserProblem($$) {
  501   my ($self, $UserProblem) = @_;
  502   die "addUserProblem failed: user set ", $UserProblem->set_id, " does not exist.\n"
  503     unless $self->{set_user}->exists($UserProblem->user_id, $UserProblem->set_id);
  504   die "addUserProblem failed: problem ", $UserProblem->problem_id, " does not exist.\n"
  505     unless $self->{problem}->exists($UserProblem->set_id, $UserProblem->problem_id);
  506   return $self->{problem_user}->add($UserProblem);
  507 }
  508 
  509 sub getUserProblem($$$$) {
  510   my ($self, $userID, $setID, $problemID) = @_;
  511   return $self->{problem_user}->get($userID, $setID, $problemID);
  512 }
  513 
  514 sub putUserProblem($$) {
  515   my ($self, $UserProblem) = @_;
  516   return $self->{problem_user}->put($UserProblem);
  517 }
  518 
  519 sub deleteUserProblem($$$$) {
  520   my ($self, $userID, $setID, $problemID) = @_;
  521   return $self->{problem_user}->delete($userID, $setID, $problemID);
  522 }
  523 
  524 ################################################################################
  525 # set+set_user functions
  526 ################################################################################
  527 
  528 sub getGlobalUserSet($$$) {
  529   my ($self, $userID, $setID) = @_;
  530   my $UserSet = $self->getUserSet($userID, $setID);
  531   return unless $UserSet;
  532   my $GlobalSet = $self->getGlobalSet($setID);
  533   if ($GlobalSet) {
  534     foreach ($UserSet->FIELDS()) {
  535       next unless $GlobalSet->can($_);
  536       next if $UserSet->$_();
  537       $UserSet->$_($GlobalSet->$_());
  538     }
  539   }
  540   return $UserSet;
  541 }
  542 
  543 ################################################################################
  544 # problem+problem_user functions
  545 ################################################################################
  546 
  547 sub getGlobalUserProblem($$$$) {
  548   my ($self, $userID, $setID, $problemID) = @_;
  549   my $UserProblem = $self->getUserProblem($userID, $setID, $problemID);
  550   return unless $UserProblem;
  551   my $GlobalProblem = $self->getGlobalProblem($setID, $problemID);
  552   if ($GlobalProblem) {
  553     foreach ($UserProblem->FIELDS()) {
  554       next unless $GlobalProblem->can($_);
  555       next if $UserProblem->$_();
  556       $UserProblem->$_($GlobalProblem->$_());
  557     }
  558   }
  559   return $UserProblem;
  560 }
  561 
  562 ################################################################################
  563 # enforcement
  564 ################################################################################
  565 
  566 ################################################################################
  567 # debugging
  568 ################################################################################
  569 
  570 sub dumpDB($$) {
  571   my ($self, $table) = @_;
  572   return $self->{$table}->dumpDB();
  573 }
  574 
  575 =head1 AUTHOR
  576 
  577 Written by Sam Hathaway, sh002i (at) math.rochester.edu.
  578 
  579 =cut
  580 
  581 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9