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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1035 - (view) (download) (as text)

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 :     / 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 : 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 904 use Data::Dumper;
131 : sh002i 775 use WeBWorK::Utils qw(runtime_use);
132 :    
133 : sh002i 798 use constant TABLES => qw(password permission key user set set_user problem problem_user);
134 : sh002i 775
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 956 =cut
152 :    
153 : sh002i 775 sub new($$) {
154 : sh002i 814 my ($invocant, $ce) = @_;
155 : sh002i 775 my $class = ref($invocant) || $invocant;
156 : sh002i 798 my $self = {};
157 : sh002i 931 bless $self, $class; # bless this here so we can pass it to the schema
158 : sh002i 775
159 :     # load the modules required to handle each table, and create driver
160 :     foreach my $table (TABLES) {
161 : sh002i 798 unless (defined $ce->{dbLayout}->{$table}) {
162 : sh002i 931 warn "ignoring table $table: layout not specified in dbLayout"; # ***
163 : sh002i 798 next;
164 :     }
165 : sh002i 775
166 :     my $layout = $ce->{dbLayout}->{$table};
167 : sh002i 798 my $record = $layout->{record};
168 : sh002i 775 my $schema = $layout->{schema};
169 :     my $driver = $layout->{driver};
170 :     my $source = $layout->{source};
171 : sh002i 808 my $params = $layout->{params};
172 : sh002i 775
173 : sh002i 798 runtime_use($record);
174 :     runtime_use($schema);
175 :     runtime_use($driver);
176 : sh002i 931 $self->{$table} = $schema->new(
177 :     $self,
178 :     $driver->new($source, $params),
179 :     $table,
180 :     $record,
181 :     $params
182 :     );
183 : sh002i 775 }
184 :    
185 :     return $self;
186 :     }
187 :    
188 : sh002i 1012 =head1 METHODS
189 :    
190 :     =cut
191 :    
192 : sh002i 775 ################################################################################
193 :     # password functions
194 :     ################################################################################
195 :    
196 : sh002i 1012 =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 : sh002i 775 sub listPasswords($) {
207 :     my ($self) = @_;
208 : sh002i 808 return map { $_->[0] }
209 :     $self->{password}->list(undef);
210 : sh002i 775 }
211 :    
212 : sh002i 1012 =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 : sh002i 808 sub addPassword($$) {
222 : sh002i 775 my ($self, $Password) = @_;
223 : sh002i 1012 die __PACKAGE__, ": addPassword($Password) failed: user not found.\n"
224 : sh002i 775 unless $self->{user}->exists($Password->user_id);
225 :     return $self->{password}->add($Password);
226 :     }
227 :    
228 : sh002i 1012 =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 : sh002i 775 sub getPassword($$) {
237 :     my ($self, $userID) = @_;
238 : sh002i 1012 die __PACKAGE__, ": getPassword() failed: you must specify a userID.\n"
239 :     unless $userID;
240 : sh002i 775 return $self->{password}->get($userID);
241 :     }
242 :    
243 : sh002i 1012 =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 : sh002i 775 sub putPassword($$) {
253 :     my ($self, $Password) = @_;
254 :     return $self->{password}->put($Password);
255 :     }
256 :    
257 : sh002i 1012 =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 : sh002i 775 sub deletePassword($$) {
266 :     my ($self, $userID) = @_;
267 :     return $self->{password}->delete($userID);
268 :     }
269 :    
270 : sh002i 1012 =back
271 :    
272 :     =cut
273 :    
274 : sh002i 775 ################################################################################
275 :     # permission functions
276 :     ################################################################################
277 :    
278 :     sub listPermissionLevels($) {
279 :     my ($self) = @_;
280 : sh002i 808 return map { $_->[0] }
281 :     $self->{permission}->list(undef);
282 : sh002i 775 }
283 :    
284 : sh002i 808 sub addPermissionLevel($$) {
285 : sh002i 775 my ($self, $PermissionLevel) = @_;
286 : sh002i 808 die "addPermissionLevel failed: user ", $PermissionLevel->user_id, " does not exist.\n"
287 : sh002i 775 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 : sh002i 808 return map { $_->[0] }
313 :     $self->{key}->list(undef);
314 : sh002i 775 }
315 :    
316 : sh002i 808 sub addKey($$) {
317 : sh002i 775 my ($self, $Key) = @_;
318 : sh002i 808 die "addKey failed: user ", $Key->user_id, " does not exist.\n"
319 : sh002i 775 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 : sh002i 808 return map { $_->[0] }
345 :     $self->{user}->list(undef);
346 : sh002i 775 }
347 :    
348 : sh002i 808 sub addUser($$) {
349 : sh002i 775 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 : sh002i 808 return map { $_->[0] }
380 :     $self->{set}->list(undef);
381 : sh002i 775 }
382 :    
383 : sh002i 808 sub addGlobalSet($$) {
384 : sh002i 775 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 : sh002i 909 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 : sh002i 775 my ($self, $userID) = @_;
419 : sh002i 808 return map { $_->[1] } # extract set_id
420 :     $self->{set_user}->list($userID, undef);
421 : sh002i 775 }
422 :    
423 : sh002i 808 sub addUserSet($$) {
424 : sh002i 775 my ($self, $UserSet) = @_;
425 : sh002i 808 die "addUserSet failed: user ", $UserSet->user_id, " does not exist.\n"
426 : sh002i 775 unless $self->{user}->exists($UserSet->user_id);
427 : sh002i 808 die "addUserSet failed: set ", $UserSet->set_id, " does not exist.\n"
428 : sh002i 775 unless $self->{set}->exists($UserSet->set_id);
429 :     return $self->{set_user}->add($UserSet);
430 :     }
431 :    
432 : sh002i 909 sub getUserSet($$$) {
433 : sh002i 775 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 : sh002i 909 sub deleteUserSet($$$) {
443 : sh002i 775 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 : sh002i 953 #grep { $_->[0] eq $setID }
457 :     $self->{problem}->list($setID, undef);
458 : sh002i 775 }
459 :    
460 : sh002i 808 sub addGlobalProblem($$) {
461 : sh002i 775 my ($self, $GlobalProblem) = @_;
462 : sh002i 808 die "addGlobalProblem failed: set ", $GlobalProblem->set_id, " does not exist.\n"
463 : sh002i 775 unless $self->{set}->exists($GlobalProblem->set_id);
464 :     return $self->{problem}->add($GlobalProblem);
465 :     }
466 :    
467 :     sub getGlobalProblem($$$) {
468 :     my ($self, $setID, $problemID) = @_;
469 : sh002i 916 return $self->{problem}->get($setID, $problemID);
470 : sh002i 775 }
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 : sh002i 923 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 : sh002i 775 sub listUserProblems($$$) {
495 :     my ($self, $userID, $setID) = @_;
496 : sh002i 923 return map { $_->[2] } # extract problem_id
497 : sh002i 808 $self->{problem_user}->list($userID, $setID, undef);
498 : sh002i 775 }
499 :    
500 : sh002i 808 sub addUserProblem($$) {
501 : sh002i 775 my ($self, $UserProblem) = @_;
502 : sh002i 808 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 : sh002i 914 unless $self->{problem}->exists($UserProblem->set_id, $UserProblem->problem_id);
506 : sh002i 775 return $self->{problem_user}->add($UserProblem);
507 :     }
508 :    
509 : sh002i 798 sub getUserProblem($$$$) {
510 : sh002i 775 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 : sh002i 798 sub deleteUserProblem($$$$) {
520 : sh002i 775 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 : sh002i 798 sub getGlobalUserSet($$$) {
529 :     my ($self, $userID, $setID) = @_;
530 : sh002i 814 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 : sh002i 798 }
542 : sh002i 775
543 :     ################################################################################
544 :     # problem+problem_user functions
545 :     ################################################################################
546 :    
547 : sh002i 798 sub getGlobalUserProblem($$$$) {
548 :     my ($self, $userID, $setID, $problemID) = @_;
549 : sh002i 814 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 : sh002i 798 }
561 : sh002i 775
562 : sh002i 808 ################################################################################
563 : sh002i 958 # enforcement
564 :     ################################################################################
565 :    
566 :     ################################################################################
567 : sh002i 808 # debugging
568 :     ################################################################################
569 :    
570 :     sub dumpDB($$) {
571 :     my ($self, $table) = @_;
572 :     return $self->{$table}->dumpDB();
573 :     }
574 :    
575 : sh002i 1012 =head1 AUTHOR
576 :    
577 :     Written by Sam Hathaway, sh002i (at) math.rochester.edu.
578 :    
579 : sh002i 1035 =cut
580 : gage 1023
581 : sh002i 775 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9