Parent Directory
|
Revision Log
Revision 3141 - (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 | 3075 | # $CVSHeader: webwork2/bin/ww_db_v2_to_v3,v 1.4 2004/12/09 16:30:12 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 : | sh002i | 3021 | ww_db_v2_to_v3 -crsuv course ... |
| 25 : | sh002i | 3012 | |
| 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 : | sh002i | 3021 | WWDBv2 user IDs are converted to login IDs. |
| 140 : | sh002i | 3012 | |
| 141 : | =item * | ||
| 142 : | |||
| 143 : | sh002i | 3021 | Users with the same v2 user ID in different courses are assumed to be the same |
| 144 : | sh002i | 3012 | user. |
| 145 : | |||
| 146 : | sh002i | 3021 | =item * |
| 147 : | |||
| 148 : | A user's permission level is used to determine the role to assign to their v3 | ||
| 149 : | participant record. (See L<ROLES>.) If the user has an empty permission level, | ||
| 150 : | they are assigned the role associated with permission level "0". | ||
| 151 : | |||
| 152 : | =item * | ||
| 153 : | |||
| 154 : | A user's status abbreviation is used to determine the status to assign to their | ||
| 155 : | v3 participant record. (See L<STATUSES>.) If the user has an empty status, they | ||
| 156 : | are assigned the status "Enrolled". | ||
| 157 : | |||
| 158 : | =item * | ||
| 159 : | |||
| 160 : | If a user has a non-empty section or recitation, their v3 participant record | ||
| 161 : | will be assigned to the section or recitation with a matching name. | ||
| 162 : | |||
| 163 : | sh002i | 3012 | =back |
| 164 : | |||
| 165 : | =cut | ||
| 166 : | |||
| 167 : | use strict; | ||
| 168 : | use warnings; | ||
| 169 : | use Data::Dumper; | ||
| 170 : | sh002i | 3021 | use DateTime; |
| 171 : | sh002i | 3012 | use Getopt::Std; |
| 172 : | |||
| 173 : | BEGIN { | ||
| 174 : | die "WEBWORK_ROOT not found in environment.\n" | ||
| 175 : | unless exists $ENV{WEBWORK_ROOT}; | ||
| 176 : | } | ||
| 177 : | |||
| 178 : | use lib "$ENV{WEBWORK_ROOT}/lib"; | ||
| 179 : | use WeBWorK::CourseEnvironment; | ||
| 180 : | use WeBWorK::DB; | ||
| 181 : | use WeBWorK::DBv3; | ||
| 182 : | |||
| 183 : | sh002i | 3015 | # map statuses from course environment to sets of status privileges |
| 184 : | use constant STATUS_MAP => { | ||
| 185 : | Enrolled => { allow_course_access => 1, include_in_assignment => 1, include_in_stats => 1, include_in_scoring => 1 }, | ||
| 186 : | Audit => { allow_course_access => 1, include_in_assignment => 1, include_in_stats => 1, include_in_scoring => 0 }, | ||
| 187 : | Drop => { allow_course_access => 0, include_in_assignment => 0, include_in_stats => 0, include_in_scoring => 0 }, | ||
| 188 : | }; | ||
| 189 : | |||
| 190 : | sh002i | 3021 | use constant DEFAULT_STATUS => "C"; |
| 191 : | sh002i | 3016 | use constant DEFAULT_PERMISSION_LEVEL => "0"; |
| 192 : | |||
| 193 : | sh002i | 3012 | our ($opt_c, $opt_r, $opt_s, $opt_u, $opt_v); |
| 194 : | getopts("crsuv"); | ||
| 195 : | |||
| 196 : | sub debug { print STDERR @_ if $opt_v } | ||
| 197 : | sh002i | 3015 | sub usage { print STDERR "usage: $0 [-crsuv] course ...\n"; exit 1 } |
| 198 : | sh002i | 3012 | |
| 199 : | main(@ARGV); | ||
| 200 : | |||
| 201 : | sub main { | ||
| 202 : | my (@courseIDs) = @_; | ||
| 203 : | |||
| 204 : | usage() unless @courseIDs; | ||
| 205 : | |||
| 206 : | my $ce = WeBWorK::CourseEnvironment->new({webwork_dir => $ENV{WEBWORK_ROOT}}); | ||
| 207 : | |||
| 208 : | WeBWorK::DBv3::init($ce->{wwdbv3_settings}); | ||
| 209 : | |||
| 210 : | sh002i | 3016 | my %abbrev_to_status_id = set_up_statuses($ce->{siteDefaults}{status}); |
| 211 : | warn "abbrev_to_status_id: ", Dumper(\%abbrev_to_status_id); | ||
| 212 : | |||
| 213 : | sh002i | 3012 | my %level_to_role_id = set_up_roles($ce->{permissionLevels}); |
| 214 : | sh002i | 3016 | warn "level_to_role_id: ", Dumper(\%level_to_role_id); |
| 215 : | sh002i | 3012 | |
| 216 : | foreach my $courseID (@courseIDs) { | ||
| 217 : | sh002i | 3016 | eval { copy_course_data($courseID, \%abbrev_to_status_id, \%level_to_role_id) }; |
| 218 : | sh002i | 3012 | if ($@) { |
| 219 : | warn "An error occured while copying data from course '$courseID':\n\n$@\n\n"; | ||
| 220 : | if ($opt_c) { | ||
| 221 : | warn "Continuing with the next course...\n"; | ||
| 222 : | } else { | ||
| 223 : | warn "Exiting.\n"; | ||
| 224 : | exit 2; | ||
| 225 : | } | ||
| 226 : | } | ||
| 227 : | } | ||
| 228 : | } | ||
| 229 : | |||
| 230 : | ################################################################################ | ||
| 231 : | |||
| 232 : | sh002i | 3021 | sub reverse_hash { |
| 233 : | my (%hash) = @_; | ||
| 234 : | |||
| 235 : | my %reverse_hash; | ||
| 236 : | foreach my $key (keys %hash) { | ||
| 237 : | my $value = $hash{$key}; | ||
| 238 : | if (defined $value and not ref $value) { | ||
| 239 : | push @{ $reverse_hash{$value} }, $key; | ||
| 240 : | #} else { | ||
| 241 : | # my $val_string = defined $value ? $value : "UNDEF"; | ||
| 242 : | # warn "pair ( $key => $val_string ) skipped.\n"; | ||
| 243 : | } | ||
| 244 : | } | ||
| 245 : | |||
| 246 : | return %reverse_hash; | ||
| 247 : | } | ||
| 248 : | |||
| 249 : | sub listeq { | ||
| 250 : | my ($a, $b) = @_; | ||
| 251 : | return "" unless @$a == @$b; | ||
| 252 : | for (my $i = 0; $i < @$a; $i++) { | ||
| 253 : | return "" unless $a->[$i] eq $b->[$i]; | ||
| 254 : | } | ||
| 255 : | return 1; | ||
| 256 : | } | ||
| 257 : | |||
| 258 : | sub is_empty { | ||
| 259 : | my ($val) = @_; | ||
| 260 : | return (not defined $val or $val eq ""); | ||
| 261 : | } | ||
| 262 : | |||
| 263 : | ################################################################################ | ||
| 264 : | |||
| 265 : | sh002i | 3012 | sub set_up_roles { |
| 266 : | my ($permissionLevels) = @_; | ||
| 267 : | my %permissionLevels = %$permissionLevels; | ||
| 268 : | |||
| 269 : | my %level_to_role_id; | ||
| 270 : | |||
| 271 : | sh002i | 3015 | # reverse the permission levels hash, resulting in a hash mapping |
| 272 : | # permissions levels to arrayrefs containing privileges | ||
| 273 : | my %levels = reverse_hash(%permissionLevels); | ||
| 274 : | sh002i | 3012 | |
| 275 : | # copy up the privileges at each level to the next-higher level | ||
| 276 : | # also sort each level | ||
| 277 : | sh002i | 3015 | my @level_names = sort { $a <=> $b } keys %levels; |
| 278 : | sh002i | 3012 | foreach my $i (0 .. $#level_names-1) { |
| 279 : | my $this_level = $level_names[$i]; | ||
| 280 : | my $next_level = $level_names[$i+1]; | ||
| 281 : | push @{ $levels{$next_level} }, @{ $levels{$this_level} }; | ||
| 282 : | } | ||
| 283 : | |||
| 284 : | # sort the privileges in each level | ||
| 285 : | debug("I found the following permission levels:\n"); | ||
| 286 : | foreach my $level (keys %levels) { | ||
| 287 : | my @sorted = sort @{ $levels{$level} }; | ||
| 288 : | $levels{$level} = [ @sorted ]; | ||
| 289 : | debug("\t$level => @sorted\n"); | ||
| 290 : | } | ||
| 291 : | |||
| 292 : | # keep track of role names so we know if we need to rename any of our new ones | ||
| 293 : | my %role_names; | ||
| 294 : | |||
| 295 : | # look at existing roles to see if we can avoid adding some new ones | ||
| 296 : | my $i = retrieve_all WeBWorK::DBv3::Role; | ||
| 297 : | while (my $Role = $i->next) { | ||
| 298 : | $role_names{$Role->name} = 1; | ||
| 299 : | sh002i | 3075 | my @role_privs = sort $Role->privs_list; |
| 300 : | sh002i | 3012 | |
| 301 : | foreach my $level (keys %levels) { | ||
| 302 : | if (listeq($levels{$level}, \@role_privs)) { | ||
| 303 : | debug("Permission level '$level' is already represented as role '", | ||
| 304 : | sh002i | 3015 | $Role->name, "' (ID $Role) -- skipping.\n"); |
| 305 : | sh002i | 3012 | delete $levels{$level}; |
| 306 : | $level_to_role_id{$level} = $Role->id; | ||
| 307 : | } | ||
| 308 : | } | ||
| 309 : | } | ||
| 310 : | |||
| 311 : | if ($opt_r) { | ||
| 312 : | debug("Updating role table (as per -r switch).\n"); | ||
| 313 : | foreach my $level (keys %levels) { | ||
| 314 : | my $name = "Legacy permission level $level"; | ||
| 315 : | if (exists $role_names{$name}) { | ||
| 316 : | my $i = 2; | ||
| 317 : | while (1) { | ||
| 318 : | my $try_name = "$name (#$i)"; | ||
| 319 : | if (not exists $role_names{$try_name}) { | ||
| 320 : | $name = $try_name; | ||
| 321 : | last; | ||
| 322 : | } | ||
| 323 : | } | ||
| 324 : | } | ||
| 325 : | |||
| 326 : | my @privs = @{ $levels{$level} }; | ||
| 327 : | |||
| 328 : | sh002i | 3075 | my $Role = create WeBWorK::DBv3::Role({name => $name}); |
| 329 : | $Role->privs_list(@privs); | ||
| 330 : | sh002i | 3012 | $Role->update; |
| 331 : | sh002i | 3015 | debug("Added role '", $Role->name, "' (ID $Role) with privileges '@privs'.\n"); |
| 332 : | sh002i | 3012 | $level_to_role_id{$level} = $Role->id; |
| 333 : | } | ||
| 334 : | } else { | ||
| 335 : | debug("Not updating role table (as per lack of -r switch).\n"); | ||
| 336 : | debug("I might run into users with permission levels that don't map to roles later.\n"); | ||
| 337 : | } | ||
| 338 : | |||
| 339 : | return %level_to_role_id; | ||
| 340 : | } | ||
| 341 : | |||
| 342 : | sub set_up_statuses { | ||
| 343 : | sh002i | 3015 | my ($abbrevs) = @_; |
| 344 : | my %abbrevs = %$abbrevs; | ||
| 345 : | |||
| 346 : | my %abbrev_to_status_id; | ||
| 347 : | |||
| 348 : | # reverse the statuses hash, resulting in a hash mapping statuses to | ||
| 349 : | # arrayrefs containing abbreviations | ||
| 350 : | my %statuses = reverse_hash(%abbrevs); | ||
| 351 : | |||
| 352 : | # look at existing statuses to see if we can avoid adding some new ones | ||
| 353 : | my $i = retrieve_all WeBWorK::DBv3::Status; | ||
| 354 : | while (my $Status = $i->next) { | ||
| 355 : | if (exists $statuses{$Status->name}) { | ||
| 356 : | debug("Status '", $Status->name, "' (ID $Status) already exists in the database -- skipping.\n"); | ||
| 357 : | # add entries mapping abbreviations to the ID of this status | ||
| 358 : | foreach my $abbrev (@{$statuses{$Status->name}}) { | ||
| 359 : | $abbrev_to_status_id{$abbrev} = $Status->id; | ||
| 360 : | } | ||
| 361 : | |||
| 362 : | delete $statuses{$Status->name}; | ||
| 363 : | } | ||
| 364 : | } | ||
| 365 : | |||
| 366 : | if ($opt_s) { | ||
| 367 : | debug("Updating status table (as per -s switch).\n"); | ||
| 368 : | foreach my $status (keys %statuses) { | ||
| 369 : | my %flags; | ||
| 370 : | %flags = %{ STATUS_MAP->{$status} } if exists STATUS_MAP->{$status}; | ||
| 371 : | my $Status = create WeBWorK::DBv3::Status({name => $status, %flags}); | ||
| 372 : | |||
| 373 : | my @flags = grep { $flags{$_} } keys %flags; | ||
| 374 : | debug("Added status '", $Status->name, "' (ID $Status) with flags '@flags'.\n"); | ||
| 375 : | |||
| 376 : | # add entries mapping abbreviations to the ID of this status | ||
| 377 : | foreach my $abbrev (@{$statuses{$status}}) { | ||
| 378 : | $abbrev_to_status_id{$abbrev} = $Status->id; | ||
| 379 : | } | ||
| 380 : | } | ||
| 381 : | } else { | ||
| 382 : | debug("Not updating status table (as per lack of -s switch).\n"); | ||
| 383 : | debug("I might run into users with status abbreviations that don't map to statuses later.\n"); | ||
| 384 : | } | ||
| 385 : | |||
| 386 : | return %abbrev_to_status_id; | ||
| 387 : | sh002i | 3012 | } |
| 388 : | |||
| 389 : | ################################################################################ | ||
| 390 : | |||
| 391 : | sub copy_course_data { | ||
| 392 : | sh002i | 3016 | my ($courseID, $abbrev_to_status_id, $level_to_role_id) = @_; |
| 393 : | sh002i | 3012 | |
| 394 : | debug("Processing course '$courseID'...\n"); | ||
| 395 : | |||
| 396 : | my $course_ce = WeBWorK::CourseEnvironment->new({ | ||
| 397 : | webwork_dir => $ENV{WEBWORK_ROOT}, | ||
| 398 : | courseName => $courseID, | ||
| 399 : | }); | ||
| 400 : | |||
| 401 : | my $course_db = WeBWorK::DB->new($course_ce->{dbLayout}); | ||
| 402 : | |||
| 403 : | sh002i | 3075 | debug("Adding course '$courseID' to v3 DB.\n"); |
| 404 : | my $v3Course = eval { create WeBWorK::DBv3::Course({name => $courseID}) }; | ||
| 405 : | $@ =~ /Duplicate entry/ and die "Course '$courseID' exists in v3 DB.\n"; | ||
| 406 : | $@ and die $@; | ||
| 407 : | sh002i | 3012 | |
| 408 : | sh002i | 3016 | copy_users($course_db, $v3Course, $abbrev_to_status_id, $level_to_role_id); |
| 409 : | |||
| 410 : | sh002i | 3075 | # { $globalSetID => [ $v3AbsSet->id, { $globalProblemID => $vAbsProb->id, ... }, ... } |
| 411 : | my %global_set_id_to_abstract_set_data = copy_abstract_data($course_db, $v3Course); | ||
| 412 : | sh002i | 3016 | } |
| 413 : | |||
| 414 : | sh002i | 3021 | ################################################################################ |
| 415 : | |||
| 416 : | sh002i | 3016 | sub copy_users { |
| 417 : | my ($course_db, $v3Course, $abbrev_to_status_id, $level_to_role_id) = @_; | ||
| 418 : | |||
| 419 : | sh002i | 3021 | my $DefaultStatus = find_status(DEFAULT_STATUS, $abbrev_to_status_id); |
| 420 : | die "Default status '", DEFAULT_STATUS, "' does not correspond to any v3 status.\n" | ||
| 421 : | unless $DefaultStatus; | ||
| 422 : | |||
| 423 : | my $DefaultRole = find_role(DEFAULT_PERMISSION_LEVEL, $level_to_role_id); | ||
| 424 : | die "Default permission level '", DEFAULT_PERMISSION_LEVEL, "' does not correspond to any v3 role.\n" | ||
| 425 : | unless $DefaultRole; | ||
| 426 : | |||
| 427 : | sh002i | 3012 | my @userIDs = $course_db->listUsers; |
| 428 : | my %Users; @Users{@userIDs} = $course_db->getUsers(@userIDs); | ||
| 429 : | sh002i | 3016 | my %Passwords; @Passwords{@userIDs} = $course_db->getPasswords(@userIDs); |
| 430 : | my %PermissionLevels; @PermissionLevels{@userIDs} = $course_db->getPermissionLevels(@userIDs); | ||
| 431 : | sh002i | 3012 | |
| 432 : | foreach my $userID (keys %Users) { | ||
| 433 : | my $User = $Users{$userID}; | ||
| 434 : | sh002i | 3016 | my $Password = $Passwords{$userID}; |
| 435 : | my $PermissionLevel = $PermissionLevels{$userID}; | ||
| 436 : | |||
| 437 : | sh002i | 3012 | unless (defined $User) { |
| 438 : | debug("User record for user ID '$userID' not found -- skipping.\n"); | ||
| 439 : | next; | ||
| 440 : | } | ||
| 441 : | |||
| 442 : | sh002i | 3016 | debug("Processing user '$userID'...\n"); |
| 443 : | |||
| 444 : | # create/update user record | ||
| 445 : | sh002i | 3021 | my ($v3User) = search WeBWorK::DBv3::User(login_id => $userID); |
| 446 : | sh002i | 3012 | if ($v3User) { |
| 447 : | sh002i | 3016 | debug("A user with login_id '$userID' exists in v3 database -- "); |
| 448 : | sh002i | 3012 | if ($opt_u) { |
| 449 : | sh002i | 3021 | # password record might not exist (annoying...) |
| 450 : | my $password = defined $Password ? $Password->password : ""; | ||
| 451 : | |||
| 452 : | sh002i | 3016 | debug("updating (as per -u switch).\n"); |
| 453 : | sh002i | 3021 | $v3User->first_name($User->first_name) unless is_empty($User->first_name); |
| 454 : | $v3User->last_name($User->first_name) unless is_empty($User->last_name); | ||
| 455 : | $v3User->email_address($User->email_address) unless is_empty($User->email_address); | ||
| 456 : | $v3User->student_id($User->student_id) unless is_empty($User->student_id); | ||
| 457 : | $v3User->password($password) unless is_empty($password); | ||
| 458 : | sh002i | 3012 | $v3User->update; |
| 459 : | } else { | ||
| 460 : | sh002i | 3016 | debug("not updating (as per lack of -u switch).\n"); |
| 461 : | sh002i | 3012 | } |
| 462 : | } else { | ||
| 463 : | sh002i | 3021 | # password record might not exist (annoying...) |
| 464 : | my $password = defined $Password ? $Password->password : ""; | ||
| 465 : | |||
| 466 : | sh002i | 3016 | debug("No user with login_id '$userID' exists in v3 database -- adding.\n"); |
| 467 : | sh002i | 3021 | $v3User = create WeBWorK::DBv3::User({ |
| 468 : | sh002i | 3016 | first_name => $User->first_name, |
| 469 : | last_name => $User->last_name, | ||
| 470 : | email_address => $User->email_address, | ||
| 471 : | student_id => $User->student_id, | ||
| 472 : | login_id => $User->user_id, | ||
| 473 : | sh002i | 3021 | password => $password, |
| 474 : | sh002i | 3012 | }); |
| 475 : | } | ||
| 476 : | sh002i | 3016 | |
| 477 : | # get status | ||
| 478 : | sh002i | 3021 | my $v3Status = find_status($User->status, $abbrev_to_status_id); |
| 479 : | unless ($v3Status) { | ||
| 480 : | debug("Using default status '", $DefaultStatus->name, "'.\n"); | ||
| 481 : | $v3Status = $DefaultStatus; | ||
| 482 : | sh002i | 3016 | } |
| 483 : | |||
| 484 : | # get role | ||
| 485 : | sh002i | 3021 | my $level = defined $PermissionLevel ? $PermissionLevel->permission : ""; |
| 486 : | my $v3Role = find_role($level, $level_to_role_id); | ||
| 487 : | unless ($v3Role) { | ||
| 488 : | debug("Using default role '", $DefaultRole->name, "'.\n"); | ||
| 489 : | $v3Role = $DefaultRole; | ||
| 490 : | sh002i | 3016 | } |
| 491 : | |||
| 492 : | # find/create section record | ||
| 493 : | my $section = $User->section; | ||
| 494 : | my $v3Section; | ||
| 495 : | sh002i | 3021 | if (is_empty($section)) { |
| 496 : | sh002i | 3016 | debug("This user has section '$section'.\n"); |
| 497 : | sh002i | 3021 | ($v3Section) = search WeBWorK::DBv3::Section(course => $v3Course, name => $section); |
| 498 : | sh002i | 3016 | if ($v3Section) { |
| 499 : | debug("This corresponds to existing section ID $v3Section in v3 database.\n"); | ||
| 500 : | } else { | ||
| 501 : | debug("No corresponding section exists in v3 DB -- adding.\n"); | ||
| 502 : | sh002i | 3021 | $v3Section = create WeBWorK::DBv3::Section({ |
| 503 : | course => $v3Course, | ||
| 504 : | sh002i | 3016 | name => $section, |
| 505 : | }); | ||
| 506 : | debug("Added section '", $v3Section->name, "' (ID $v3Section).\n"); | ||
| 507 : | } | ||
| 508 : | } else { | ||
| 509 : | debug("This user has no section.\n"); | ||
| 510 : | } | ||
| 511 : | |||
| 512 : | # find/create recitation record | ||
| 513 : | my $recitation = $User->recitation; | ||
| 514 : | my $v3Recitation; | ||
| 515 : | sh002i | 3021 | if (is_empty($recitation)) { |
| 516 : | sh002i | 3016 | debug("This user has recitation '$recitation'.\n"); |
| 517 : | sh002i | 3021 | ($v3Recitation) = search WeBWorK::DBv3::Recitation(course => $v3Course, name => $User->recitation); |
| 518 : | sh002i | 3016 | if ($v3Recitation) { |
| 519 : | debug("This correponds to existing recitation ID $v3Recitation in v3 database.\n"); | ||
| 520 : | } else { | ||
| 521 : | debug("No corresponding recitation exists in v3 DB -- adding.\n"); | ||
| 522 : | sh002i | 3021 | $v3Recitation = create WeBWorK::DBv3::Recitation({ |
| 523 : | course => $v3Course, | ||
| 524 : | sh002i | 3016 | name => $User->recitation, |
| 525 : | }); | ||
| 526 : | debug("Added recitation '", $v3Recitation->name, "' (ID $v3Recitation).\n"); | ||
| 527 : | } | ||
| 528 : | } else { | ||
| 529 : | debug("This user has no recitation.\n"); | ||
| 530 : | } | ||
| 531 : | |||
| 532 : | # create participant record | ||
| 533 : | sh002i | 3021 | debug("Adding participant record for user '$userID'..."); |
| 534 : | #my $sectionID = $v3Section->id if defined $v3Section; | ||
| 535 : | #my $recitationID = $v3Recitation->id if defined $v3Recitation; | ||
| 536 : | my $v3Participant = create WeBWorK::DBv3::Participant({ | ||
| 537 : | course => $v3Course, | ||
| 538 : | user => $v3User, | ||
| 539 : | status => $v3Status, | ||
| 540 : | role => $v3Role, | ||
| 541 : | section => $v3Section, | ||
| 542 : | recitation => $v3Recitation, | ||
| 543 : | sh002i | 3016 | comment => $User->comment, |
| 544 : | }); | ||
| 545 : | sh002i | 3021 | debug(" added participant ID $v3Participant.\n"); |
| 546 : | sh002i | 3012 | } |
| 547 : | } | ||
| 548 : | |||
| 549 : | sh002i | 3021 | sub find_status { |
| 550 : | my ($status, $abbrev_to_status_id) = @_; | ||
| 551 : | sh002i | 3016 | |
| 552 : | sh002i | 3021 | return if is_empty($status); |
| 553 : | sh002i | 3016 | |
| 554 : | sh002i | 3021 | my $v3Status_id = $abbrev_to_status_id->{$status}; |
| 555 : | my $v3Status; | ||
| 556 : | if (defined $v3Status_id) { | ||
| 557 : | #debug("Status '$status' maps to v3 status ID '$v3Status_id'.\n"); | ||
| 558 : | $v3Status = retrieve WeBWorK::DBv3::Status($v3Status_id); | ||
| 559 : | } else { | ||
| 560 : | #debug("Status '$status' doesn't map to any v3 status.\n"); | ||
| 561 : | } | ||
| 562 : | |||
| 563 : | return $v3Status; | ||
| 564 : | sh002i | 3016 | } |
| 565 : | |||
| 566 : | sh002i | 3021 | sub find_role { |
| 567 : | my ($level, $level_to_role_id) = @_; | ||
| 568 : | |||
| 569 : | return if is_empty($level); | ||
| 570 : | |||
| 571 : | my $v3Role_id = $level_to_role_id->{$level}; | ||
| 572 : | my $v3Role; | ||
| 573 : | if (defined $v3Role_id) { | ||
| 574 : | #debug("Permission level '$level' maps to v3 role ID '$v3Role_id'.\n"); | ||
| 575 : | $v3Role = retrieve WeBWorK::DBv3::Role($v3Role_id); | ||
| 576 : | } else { | ||
| 577 : | #debug("Permission level '$level' doesn't map to any v3 role.\n"); | ||
| 578 : | } | ||
| 579 : | |||
| 580 : | return $v3Role; | ||
| 581 : | } | ||
| 582 : | |||
| 583 : | sh002i | 3012 | ################################################################################ |
| 584 : | |||
| 585 : | sh002i | 3021 | sub copy_abstract_data { |
| 586 : | my ($course_db, $v3Course) = @_; | ||
| 587 : | |||
| 588 : | sh002i | 3075 | my %global_set_id_to_abstract_set_data; |
| 589 : | |||
| 590 : | sh002i | 3021 | my @globalSetIDs = $course_db->listGlobalSets; |
| 591 : | my %GlobalSets; @GlobalSets{@globalSetIDs} = $course_db->getGlobalSets(@globalSetIDs); | ||
| 592 : | |||
| 593 : | foreach my $globalSetID (keys %GlobalSets) { | ||
| 594 : | my $GlobalSet = $GlobalSets{$globalSetID}; | ||
| 595 : | |||
| 596 : | unless (defined $GlobalSet) { | ||
| 597 : | debug("Global set record for global set ID '$globalSetID' not found -- skipping.\n"); | ||
| 598 : | next; | ||
| 599 : | } | ||
| 600 : | |||
| 601 : | debug("Processing global set '$globalSetID'...\n"); | ||
| 602 : | |||
| 603 : | # set up some fields that need setting up | ||
| 604 : | # (if the conditional is false, the variable is left undefined) | ||
| 605 : | |||
| 606 : | # convert empty strings to undefined values | ||
| 607 : | my $set_header = $GlobalSet->set_header unless is_empty($GlobalSet->set_header); | ||
| 608 : | my $hardcopy_header = $GlobalSet->hardcopy_header unless is_empty($GlobalSet->hardcopy_header); | ||
| 609 : | |||
| 610 : | # convert | ||
| 611 : | my $open_date = DateTime->from_epoch(epoch => $GlobalSet->open_date); | ||
| 612 : | my $due_date = DateTime->from_epoch(epoch => $GlobalSet->due_date); | ||
| 613 : | my $answer_date = DateTime->from_epoch(epoch => $GlobalSet->answer_date); | ||
| 614 : | |||
| 615 : | # create abstract_set record | ||
| 616 : | debug("Adding abstract_set record for global set '$globalSetID'..."); | ||
| 617 : | my $v3AbsSet = create WeBWorK::DBv3::AbstractSet({ | ||
| 618 : | course => $v3Course, | ||
| 619 : | name => $GlobalSet->set_id, | ||
| 620 : | set_header => $set_header, | ||
| 621 : | hardcopy_header => $hardcopy_header, | ||
| 622 : | open_date => $open_date, | ||
| 623 : | due_date => $due_date, | ||
| 624 : | answer_date => $answer_date, | ||
| 625 : | published => $GlobalSet->published, | ||
| 626 : | }); | ||
| 627 : | debug(" added abstract_set ID '$v3AbsSet'.\n"); | ||
| 628 : | |||
| 629 : | sh002i | 3075 | |
| 630 : | my %problem_mapping; | ||
| 631 : | |||
| 632 : | sh002i | 3021 | my @globalProblemIDs = sort { $a <=> $b } $course_db->listGlobalProblems($globalSetID); |
| 633 : | warn "globalProblemIDs=@globalProblemIDs\n"; | ||
| 634 : | my %GlobalProblems; @GlobalProblems{@globalProblemIDs} | ||
| 635 : | = $course_db->getGlobalProblems(map { [ $globalSetID, $_ ] } @globalProblemIDs); | ||
| 636 : | |||
| 637 : | my @problem_order; | ||
| 638 : | |||
| 639 : | foreach my $globalProblemID (@globalProblemIDs) { | ||
| 640 : | my $GlobalProblem = $GlobalProblems{$globalProblemID}; | ||
| 641 : | |||
| 642 : | unless (defined $GlobalProblem) { | ||
| 643 : | warn "Global problem record for global problem ID '$globalProblemID' in set ID '$globalSetID' not found -- skipping.\n"; | ||
| 644 : | next; | ||
| 645 : | } | ||
| 646 : | |||
| 647 : | debug("Processing global problem '$globalProblemID'...\n"); | ||
| 648 : | |||
| 649 : | # convert max_attempts of -1 to undef | ||
| 650 : | my $max_attempts_per_version = $GlobalProblem->max_attempts | ||
| 651 : | if $GlobalProblem->max_attempts >= 0; | ||
| 652 : | |||
| 653 : | # create abstract_problem record | ||
| 654 : | debug("Adding abstract_set record for global problem '$globalProblemID'..."); | ||
| 655 : | my $v3AbsProb = create WeBWorK::DBv3::AbstractProblem({ | ||
| 656 : | abstract_set => $v3AbsSet, | ||
| 657 : | name => "Legacy problem $globalProblemID", | ||
| 658 : | source_type => "file", | ||
| 659 : | source_file => $GlobalProblem->source_file, | ||
| 660 : | weight => $GlobalProblem->value, | ||
| 661 : | max_attempts_per_version => $max_attempts_per_version, | ||
| 662 : | version_creation_interval => undef, | ||
| 663 : | versions_per_interval => 1, | ||
| 664 : | version_due_date_offset => undef, | ||
| 665 : | version_answer_date_offset => undef, | ||
| 666 : | }); | ||
| 667 : | debug(" added abstract_problem ID '$v3AbsProb'.\n"); | ||
| 668 : | |||
| 669 : | push @problem_order, $v3AbsProb->id; | ||
| 670 : | sh002i | 3075 | |
| 671 : | $problem_mapping{$globalProblemID} = $v3AbsProb->id; | ||
| 672 : | sh002i | 3021 | } |
| 673 : | |||
| 674 : | # update problem order | ||
| 675 : | debug("Setting problem order to: '@problem_order'..."); | ||
| 676 : | $v3AbsSet->problem_order_list(@problem_order); | ||
| 677 : | $v3AbsSet->update; | ||
| 678 : | debug(" done.\n"); | ||
| 679 : | sh002i | 3075 | |
| 680 : | $global_set_id_to_abstract_set_data{$globalSetID} = [ $v3AbsSet->id, \%problem_mapping ]; | ||
| 681 : | sh002i | 3012 | } |
| 682 : | sh002i | 3075 | |
| 683 : | return %global_set_id_to_abstract_set_data; | ||
| 684 : | sh002i | 3012 | } |
| 685 : | sh002i | 3021 | |
| 686 : | sh002i | 3075 | ################################################################################ |
| 687 : | sh002i | 3021 | |
| 688 : | sh002i | 3075 | sub copy_assignment_data { |
| 689 : | my ($course_db, $v3Course, $global_set_id_to_abstract_set_data) = @_; | ||
| 690 : | |||
| 691 : | my $participant_iter = WeBWorK::DBv3::Participant->search(course => $v3Course); | ||
| 692 : | |||
| 693 : | while (my $Participant = $participant_iter->next) { | ||
| 694 : | my @userSetIDs = $course_db->listUserSets($Participant->user->login_id); | ||
| 695 : | $v3AbsSet-> | ||
| 696 : | } | ||
| 697 : | } | ||
| 698 : | sh002i | 3021 | |
| 699 : | sh002i | 3075 | sub copy_single_assignment { |
| 700 : | my ($course_db, $v3Course, $v3Participant, $v3AbsSet, $global_set_id_to_abstract_set_data) = @_; | ||
| 701 : | |||
| 702 : | |||
| 703 : | } |
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |