[system] / trunk / webwork2 / bin / ww_db_v2_to_v3 Repository:
ViewVC logotype

Annotation of /trunk/webwork2/bin/ww_db_v2_to_v3

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3016 - (view) (download)

1 : sh002i 3012 #!/usr/bin/env perl
2 :     ################################################################################
3 :     # WeBWorK Online Homework Delivery System
4 :     # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/
5 : sh002i 3015 # $CVSHeader: webwork2/bin/ww_db_v2_to_v3,v 1.1 2004/11/25 05:50:51 sh002i Exp $
6 : sh002i 3012 #
7 :     # This program is free software; you can redistribute it and/or modify it under
8 :     # the terms of either: (a) the GNU General Public License as published by the
9 :     # Free Software Foundation; either version 2, or (at your option) any later
10 :     # version, or (b) the "Artistic License" which comes with this package.
11 :     #
12 :     # This program is distributed in the hope that it will be useful, but WITHOUT
13 :     # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 :     # FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
15 :     # Artistic License for more details.
16 :     ################################################################################
17 :    
18 :     =head1 NAME
19 :    
20 :     ww_db_v2_to_v3 - convert a WWDBv2 database to a WWDBv3 database.
21 :    
22 :     =head1 SYNOPSIS
23 :    
24 :     ww_db_v2_to_v3 course ...
25 :    
26 :     =head1 DESCRIPTION
27 :    
28 :     Copies course data from legacy WWDBv2 database(s) to a WWDBv3 database. This may
29 :     take a long time.
30 :    
31 :     You must disallow login to the WeBWorK system while the transfer is taking
32 :     place. To disable logins for all courses, set the permission level necessary for
33 :     C<login> to $nobody in F<global.conf>. (It is usually set to $student.)
34 :    
35 :     =head1 OPTIONS
36 :    
37 :     =over
38 :    
39 :     =item -c
40 :    
41 :     If an error occurs while copying a course's data, continue copying with the next
42 :     course.
43 :    
44 :     =item -r
45 :    
46 :     Update role table in WWDBv3 database from permission level information in
47 :     F<global.conf>.
48 :    
49 :     =item -s
50 :    
51 :     Update status table in WWDBv3 database from the status information in
52 :     F<global.conf>.
53 :    
54 :     =item -u
55 :    
56 :     When importing a user that already exists in the WWDBv3 database, replace the
57 :     existing information (including the password) with the information in the user
58 :     record being imported.
59 :    
60 :     If this option is not specified, existing users are not updated.
61 :    
62 :     =item -v
63 :    
64 :     Verbose operation.
65 :    
66 :     =item course ...
67 :    
68 :     Data from these courses will be copied.
69 :    
70 :     =back
71 :    
72 :     =head1 BEHAVIOR
73 :    
74 :     =head2 ROLES
75 :    
76 :     =over
77 :    
78 :     =item *
79 :    
80 : sh002i 3015 Roles created with the -r switch are created as system-wide roles.
81 :    
82 :     =item *
83 :    
84 : sh002i 3012 Roles are created by observing the %permissionLevels hash in F<global.conf>, and
85 :     collecting the privileges granted at each permission level into sets. Each set
86 :     of privileges becomes a WWDBv3 role record.
87 :    
88 :     =item *
89 :    
90 :     When a role with the same set of permissions already exists in the WWDBv3
91 :     database, a new one is not created.
92 :    
93 :     =back
94 :    
95 : sh002i 3015 =head2 STATUSES
96 :    
97 :     =over
98 :    
99 :     =item *
100 :    
101 :     Statuses created with the -s switch are created as system-wide statuses.
102 :    
103 :     =item *
104 :    
105 :     Statuses are created by observing the %{$siteDefaults{status}} hash in
106 :     F<global.conf>, and
107 :    
108 :     =item *
109 :    
110 :     A status named "Enrolled" is imported into the database with the
111 :     C<allow_course_access>, C<include_in_assignment>, C<include_in_stats>, and
112 :     C<include_in_scoring> flags set.
113 :    
114 :     =item *
115 :    
116 :     A status named "Audit" is imported into the database with the
117 :     C<allow_course_access>, C<include_in_assignment>, and C<include_in_stats>, flags
118 :     set, and the C<include_in_scoring> flag unset.
119 :    
120 :     =item *
121 :    
122 :     A status named "Drop" is imported into the database with the
123 :     C<allow_course_access>, C<include_in_assignment>, C<include_in_stats>, and
124 :     C<include_in_scoring> flags unset.
125 :    
126 :     =item *
127 :    
128 :     Statuses with other names are imported into the database with the same flags set
129 :     as the "Enrolled" flag.
130 :    
131 :     =back
132 :    
133 : sh002i 3012 =head2 USERS
134 :    
135 :     =over
136 :    
137 :     =item *
138 :    
139 :     WWDBv2 user IDs are converted to login IDs. WWDBv2 set IDs are converted to set
140 :     names. WWDBv2 problem IDs are used to determine the C<problem_order> in the
141 :     problem's abstract set.
142 :    
143 :     =item *
144 :    
145 :     Users with the same user ID in different courses are assumed to be the same
146 :     user.
147 :    
148 :     =back
149 :    
150 :     =cut
151 :    
152 :     use strict;
153 :     use warnings;
154 :     use Data::Dumper;
155 :     use Getopt::Std;
156 :    
157 :     BEGIN {
158 :     die "WEBWORK_ROOT not found in environment.\n"
159 :     unless exists $ENV{WEBWORK_ROOT};
160 :     }
161 :    
162 :     use lib "$ENV{WEBWORK_ROOT}/lib";
163 :     use WeBWorK::CourseEnvironment;
164 :     use WeBWorK::DB;
165 :     use WeBWorK::DBv3;
166 :    
167 : sh002i 3015 # map statuses from course environment to sets of status privileges
168 :     use constant STATUS_MAP => {
169 :     Enrolled => { allow_course_access => 1, include_in_assignment => 1, include_in_stats => 1, include_in_scoring => 1 },
170 :     Audit => { allow_course_access => 1, include_in_assignment => 1, include_in_stats => 1, include_in_scoring => 0 },
171 :     Drop => { allow_course_access => 0, include_in_assignment => 0, include_in_stats => 0, include_in_scoring => 0 },
172 :     };
173 :    
174 : sh002i 3016 use constant DEFAULT_STATUS => "Enrolled";
175 :     use constant DEFAULT_PERMISSION_LEVEL => "0";
176 :    
177 : sh002i 3012 our ($opt_c, $opt_r, $opt_s, $opt_u, $opt_v);
178 :     getopts("crsuv");
179 :    
180 :     sub debug { print STDERR @_ if $opt_v }
181 : sh002i 3015 sub usage { print STDERR "usage: $0 [-crsuv] course ...\n"; exit 1 }
182 : sh002i 3012
183 :     main(@ARGV);
184 :    
185 :     sub main {
186 :     my (@courseIDs) = @_;
187 :    
188 :     usage() unless @courseIDs;
189 :    
190 :     my $ce = WeBWorK::CourseEnvironment->new({webwork_dir => $ENV{WEBWORK_ROOT}});
191 :    
192 :     WeBWorK::DBv3::init($ce->{wwdbv3_settings});
193 :    
194 : sh002i 3016 my %abbrev_to_status_id = set_up_statuses($ce->{siteDefaults}{status});
195 :     warn "abbrev_to_status_id: ", Dumper(\%abbrev_to_status_id);
196 :    
197 : sh002i 3012 my %level_to_role_id = set_up_roles($ce->{permissionLevels});
198 : sh002i 3016 warn "level_to_role_id: ", Dumper(\%level_to_role_id);
199 : sh002i 3012
200 :     foreach my $courseID (@courseIDs) {
201 : sh002i 3016 eval { copy_course_data($courseID, \%abbrev_to_status_id, \%level_to_role_id) };
202 : sh002i 3012 if ($@) {
203 :     warn "An error occured while copying data from course '$courseID':\n\n$@\n\n";
204 :     if ($opt_c) {
205 :     warn "Continuing with the next course...\n";
206 :     } else {
207 :     warn "Exiting.\n";
208 :     exit 2;
209 :     }
210 :     }
211 :     }
212 :     }
213 :    
214 :     ################################################################################
215 :    
216 :     sub set_up_roles {
217 :     my ($permissionLevels) = @_;
218 :     my %permissionLevels = %$permissionLevels;
219 :    
220 :     my %level_to_role_id;
221 :    
222 : sh002i 3015 # reverse the permission levels hash, resulting in a hash mapping
223 :     # permissions levels to arrayrefs containing privileges
224 :     my %levels = reverse_hash(%permissionLevels);
225 : sh002i 3012
226 :     # copy up the privileges at each level to the next-higher level
227 :     # also sort each level
228 : sh002i 3015 my @level_names = sort { $a <=> $b } keys %levels;
229 : sh002i 3012 foreach my $i (0 .. $#level_names-1) {
230 :     my $this_level = $level_names[$i];
231 :     my $next_level = $level_names[$i+1];
232 :     push @{ $levels{$next_level} }, @{ $levels{$this_level} };
233 :     }
234 :    
235 :     # sort the privileges in each level
236 :     debug("I found the following permission levels:\n");
237 :     foreach my $level (keys %levels) {
238 :     my @sorted = sort @{ $levels{$level} };
239 :     $levels{$level} = [ @sorted ];
240 :     debug("\t$level => @sorted\n");
241 :     }
242 :    
243 :     # keep track of role names so we know if we need to rename any of our new ones
244 :     my %role_names;
245 :    
246 :     # look at existing roles to see if we can avoid adding some new ones
247 :     my $i = retrieve_all WeBWorK::DBv3::Role;
248 :     while (my $Role = $i->next) {
249 :     $role_names{$Role->name} = 1;
250 :     my @role_privs = sort $Role->priv_list;
251 :    
252 :     foreach my $level (keys %levels) {
253 :     if (listeq($levels{$level}, \@role_privs)) {
254 :     debug("Permission level '$level' is already represented as role '",
255 : sh002i 3015 $Role->name, "' (ID $Role) -- skipping.\n");
256 : sh002i 3012 delete $levels{$level};
257 :     $level_to_role_id{$level} = $Role->id;
258 :     }
259 :     }
260 :     }
261 :    
262 :     if ($opt_r) {
263 :     debug("Updating role table (as per -r switch).\n");
264 :     foreach my $level (keys %levels) {
265 :     my $name = "Legacy permission level $level";
266 :     if (exists $role_names{$name}) {
267 :     my $i = 2;
268 :     while (1) {
269 :     my $try_name = "$name (#$i)";
270 :     if (not exists $role_names{$try_name}) {
271 :     $name = $try_name;
272 :     last;
273 :     }
274 :     }
275 :     }
276 :    
277 :     my @privs = @{ $levels{$level} };
278 :    
279 :     my $Role = create WeBWorK::DBv3::Role({name => $name });
280 :     $Role->priv_list(@privs);
281 :     $Role->update;
282 : sh002i 3015 debug("Added role '", $Role->name, "' (ID $Role) with privileges '@privs'.\n");
283 : sh002i 3012 $level_to_role_id{$level} = $Role->id;
284 :     }
285 :     } else {
286 :     debug("Not updating role table (as per lack of -r switch).\n");
287 :     debug("I might run into users with permission levels that don't map to roles later.\n");
288 :     }
289 :    
290 :     return %level_to_role_id;
291 :     }
292 :    
293 :     sub set_up_statuses {
294 : sh002i 3015 my ($abbrevs) = @_;
295 :     my %abbrevs = %$abbrevs;
296 :    
297 :     my %abbrev_to_status_id;
298 :    
299 :     # reverse the statuses hash, resulting in a hash mapping statuses to
300 :     # arrayrefs containing abbreviations
301 :     my %statuses = reverse_hash(%abbrevs);
302 :    
303 :     # look at existing statuses to see if we can avoid adding some new ones
304 :     my $i = retrieve_all WeBWorK::DBv3::Status;
305 :     while (my $Status = $i->next) {
306 :     if (exists $statuses{$Status->name}) {
307 :     debug("Status '", $Status->name, "' (ID $Status) already exists in the database -- skipping.\n");
308 :     # add entries mapping abbreviations to the ID of this status
309 :     foreach my $abbrev (@{$statuses{$Status->name}}) {
310 :     $abbrev_to_status_id{$abbrev} = $Status->id;
311 :     }
312 :    
313 :     delete $statuses{$Status->name};
314 :     }
315 :     }
316 :    
317 :     if ($opt_s) {
318 :     debug("Updating status table (as per -s switch).\n");
319 :     foreach my $status (keys %statuses) {
320 :     my %flags;
321 :     %flags = %{ STATUS_MAP->{$status} } if exists STATUS_MAP->{$status};
322 :     my $Status = create WeBWorK::DBv3::Status({name => $status, %flags});
323 :    
324 :     my @flags = grep { $flags{$_} } keys %flags;
325 :     debug("Added status '", $Status->name, "' (ID $Status) with flags '@flags'.\n");
326 :    
327 :     # add entries mapping abbreviations to the ID of this status
328 :     foreach my $abbrev (@{$statuses{$status}}) {
329 :     $abbrev_to_status_id{$abbrev} = $Status->id;
330 :     }
331 :     }
332 :     } else {
333 :     debug("Not updating status table (as per lack of -s switch).\n");
334 :     debug("I might run into users with status abbreviations that don't map to statuses later.\n");
335 :     }
336 :    
337 :     return %abbrev_to_status_id;
338 : sh002i 3012 }
339 :    
340 : sh002i 3015 sub reverse_hash {
341 :     my (%hash) = @_;
342 :    
343 :     my %reverse_hash;
344 :     foreach my $key (keys %hash) {
345 :     my $value = $hash{$key};
346 :     if (defined $value and not ref $value) {
347 :     push @{ $reverse_hash{$value} }, $key;
348 :     #} else {
349 :     # my $val_string = defined $value ? $value : "UNDEF";
350 :     # warn "pair ( $key => $val_string ) skipped.\n";
351 :     }
352 :     }
353 :    
354 :     return %reverse_hash;
355 :     }
356 :    
357 : sh002i 3012 ################################################################################
358 :    
359 :     sub copy_course_data {
360 : sh002i 3016 my ($courseID, $abbrev_to_status_id, $level_to_role_id) = @_;
361 : sh002i 3012
362 :     debug("Processing course '$courseID'...\n");
363 :    
364 :     my $course_ce = WeBWorK::CourseEnvironment->new({
365 :     webwork_dir => $ENV{WEBWORK_ROOT},
366 :     courseName => $courseID,
367 :     });
368 :    
369 :     my $course_db = WeBWorK::DB->new($course_ce->{dbLayout});
370 :    
371 :     # First we see if this course already exists. If it does, there's a problem
372 :     # and we throw an exception.
373 :     if (WeBWorK::DBv3::Course->search(name => $courseID)) {
374 : sh002i 3015 die "Course '$courseID' exists in v3 DB";
375 : sh002i 3012 }
376 :    
377 : sh002i 3016 debug("Course '$courseID' doesn't exist in v3 DB -- adding.\n");
378 :     my $v3Course = WeBWorK::DBv3::Course->create({name => $courseID});
379 :    
380 :     copy_users($course_db, $v3Course, $abbrev_to_status_id, $level_to_role_id);
381 :    
382 :     copy_abstract_data($course_db, $v3Course);
383 :     }
384 :    
385 :     sub copy_users {
386 :     my ($course_db, $v3Course, $abbrev_to_status_id, $level_to_role_id) = @_;
387 :    
388 : sh002i 3012 my @userIDs = $course_db->listUsers;
389 :     my %Users; @Users{@userIDs} = $course_db->getUsers(@userIDs);
390 : sh002i 3016 my %Passwords; @Passwords{@userIDs} = $course_db->getPasswords(@userIDs);
391 :     my %PermissionLevels; @PermissionLevels{@userIDs} = $course_db->getPermissionLevels(@userIDs);
392 : sh002i 3012
393 :     foreach my $userID (keys %Users) {
394 :     my $User = $Users{$userID};
395 : sh002i 3016 my $Password = $Passwords{$userID};
396 :     my $PermissionLevel = $PermissionLevels{$userID};
397 :    
398 : sh002i 3012 unless (defined $User) {
399 :     debug("User record for user ID '$userID' not found -- skipping.\n");
400 :     next;
401 :     }
402 :    
403 : sh002i 3016 debug("Processing user '$userID'...\n");
404 :    
405 :     # create/update user record
406 :     my ($v3User) = WeBWorK::DBv3::User->search(login_id => $userID);
407 : sh002i 3012 if ($v3User) {
408 : sh002i 3016 debug("A user with login_id '$userID' exists in v3 database -- ");
409 : sh002i 3012 if ($opt_u) {
410 : sh002i 3016 debug("updating (as per -u switch).\n");
411 : sh002i 3012 $v3User->first_name($User->first_name) if $User->first_name ne "";
412 :     $v3User->last_name($User->first_name) if $User->last_name ne "";
413 :     $v3User->email_address($User->email_address) if $User->email_address ne "";
414 :     $v3User->student_id($User->student_id) if $User->student_id ne "";
415 : sh002i 3016 $v3User->password($Password->password) if $Password->password ne "";
416 : sh002i 3012 $v3User->update;
417 :     } else {
418 : sh002i 3016 debug("not updating (as per lack of -u switch).\n");
419 : sh002i 3012 }
420 :     } else {
421 : sh002i 3016 debug("No user with login_id '$userID' exists in v3 database -- adding.\n");
422 :     $v3User = WeBWorK::DBv3::User->create({
423 :     first_name => $User->first_name,
424 :     last_name => $User->last_name,
425 :     email_address => $User->email_address,
426 :     student_id => $User->student_id,
427 :     login_id => $User->user_id,
428 :     password => $Password->password,
429 : sh002i 3012 });
430 :     }
431 : sh002i 3016
432 :     # get status
433 :     my $status = $User->status;
434 :     if ($status eq "") {
435 :     $status = DEFAULT_STATUS;
436 :     debug("This user has no status -- using '$status'.\n");
437 :     }
438 :     my $v3Status_id = $abbrev_to_status_id->{$status};
439 :     my $v3Status;
440 :     if (defined $v3Status_id) {
441 :     debug("This user has status '", $User->status, "', which maps to v3 status ID '$v3Status_id'.\n");
442 :     $v3Status = WeBWorK::DBv3::Status->retrieve($v3Status_id);
443 :     } else {
444 :     debug("This user has status '", $User->status, "', which doesn't map to any v3 status.\n");
445 :     }
446 :    
447 :     # get role
448 :     my $level = $PermissionLevel->permission;
449 :     if ($level eq "") {
450 :     $level = DEFAULT_PERMISSION_LEVEL;
451 :     debug("This user has no permission level -- using '$level'.\n");
452 :     }
453 :     my $v3Role_id = $level_to_role_id->{$level};
454 :     my $v3Role;
455 :     if (defined $v3Role_id) {
456 :     debug("This user has permission level '", $PermissionLevel->permission, "', which maps to v3 role ID '$v3Role_id'.\n");
457 :     $v3Role = WeBWorK::DBv3::Role->retrieve($v3Role_id);
458 :     } else {
459 :     debug("This user has permission level '", $PermissionLevel->permission, "', which doesn't map to any v3 role.\n");
460 :     }
461 :    
462 :     # find/create section record
463 :     my $section = $User->section;
464 :     my $v3Section;
465 :     if ($section ne "") {
466 :     debug("This user has section '$section'.\n");
467 :     ($v3Section) = WeBWorK::DBv3::Section->search(course => $v3Course->id, name => $section);
468 :     if ($v3Section) {
469 :     debug("This corresponds to existing section ID $v3Section in v3 database.\n");
470 :     } else {
471 :     debug("No corresponding section exists in v3 DB -- adding.\n");
472 :     $v3Section = WeBWorK::DBv3::Section->create({
473 :     course => $v3Course->id,
474 :     name => $section,
475 :     });
476 :     debug("Added section '", $v3Section->name, "' (ID $v3Section).\n");
477 :     }
478 :     } else {
479 :     debug("This user has no section.\n");
480 :     }
481 :    
482 :    
483 :     # find/create recitation record
484 :     my $recitation = $User->recitation;
485 :     my $v3Recitation;
486 :     if ($recitation ne "") {
487 :     debug("This user has recitation '$recitation'.\n");
488 :     ($v3Recitation) = WeBWorK::DBv3::Recitation->search(course => $v3Course->id, name => $User->recitation);
489 :     if ($v3Recitation) {
490 :     debug("This correponds to existing recitation ID $v3Recitation in v3 database.\n");
491 :     } else {
492 :     debug("No corresponding recitation exists in v3 DB -- adding.\n");
493 :     $v3Recitation = WeBWorK::DBv3::Recitation->create({
494 :     course => $v3Course->id,
495 :     name => $User->recitation,
496 :     });
497 :     debug("Added recitation '", $v3Recitation->name, "' (ID $v3Recitation).\n");
498 :     }
499 :     } else {
500 :     debug("This user has no recitation.\n");
501 :     }
502 :    
503 :     # create participant record
504 :     debug("Adding participant record for user '", $v3User->login_id, "' (ID $v3User).\n");
505 :     my $sectionID = $v3Section->id if defined $v3Section;
506 :     my $recitationID = $v3Recitation->id if defined $v3Recitation;
507 :     my $v3Participant = WeBWorK::DBv3::Participant->create({
508 :     course => $v3Course->id,
509 :     user => $v3User->id,
510 :     status => $v3Status->id,
511 :     role => $v3Role->id,
512 :     section => $sectionID,
513 :     recitation => $recitationID,
514 :     comment => $User->comment,
515 :     });
516 : sh002i 3012 }
517 :     }
518 :    
519 : sh002i 3016 sub copy_abstract_data {
520 :     my ($course_db, $v3Course) = @_;
521 :    
522 :    
523 :     }
524 :    
525 : sh002i 3012 ################################################################################
526 :    
527 :     sub listeq {
528 :     my ($a, $b) = @_;
529 :     return "" unless @$a == @$b;
530 :     for (my $i = 0; $i < @$a; $i++) {
531 :     return "" unless $a->[$i] eq $b->[$i];
532 :     }
533 :     return 1;
534 :     }

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9