Skip to content

Commit 7e6dbde

Browse files
committed
Speed up achievement scoring.
First get all of the necessary achievement data from the database, then process it and print it to the file. Comparing scoring all course achievements for all users in a course with 5000 users shows a considerable speed improvement. It takes more than three minutes for this with the develop branch, and less than 3 seconds with this pull request. Obviously the generated scoring files are identical.
1 parent 688fd34 commit 7e6dbde

File tree

1 file changed

+31
-38
lines changed

1 file changed

+31
-38
lines changed

lib/WeBWorK/ContentGenerator/Instructor/AchievementList.pm

+31-38
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,20 @@ sub score_handler ($c) {
252252
my $scope = $c->param('action.score.scope');
253253
my @achievementsToScore = $scope eq 'all' ? @{ $c->{allAchievementIDs} } : $c->param('selected_achievements');
254254

255+
# First get everything that is needed from the database.
256+
my @achievements = sortAchievements($db->getAchievements(@achievementsToScore));
257+
my @users = $db->getUsersWhere({ user_id => { not_like => 'set_id:%' } }, [qw(section last_name)]);
258+
259+
my %globalUserAchievements = map { $_->user_id => $_ } $db->getGlobalUserAchievementsWhere;
260+
261+
my %userAchievements;
262+
for (@achievements) {
263+
$userAchievements{ $_->user_id }{ $_->achievement_id } = $_
264+
for $db->getUserAchievementsWhere({ achievement_id => $_->achievement_id });
265+
}
266+
255267
# Define file name
256-
my $scoreFileName = $courseName . "_achievement_scores.csv";
268+
my $scoreFileName = $courseName . '_achievement_scores.csv';
257269
my $scoreFilePath = $ce->{courseDirs}{scoring} . '/' . $scoreFileName;
258270

259271
# Back up existing file
@@ -265,60 +277,41 @@ sub score_handler ($c) {
265277
# Check path and open the file
266278
$scoreFilePath = surePathToFile($ce->{courseDirs}{scoring}, $scoreFilePath);
267279

268-
my $SCORE = Mojo::File->new($scoreFilePath)->open('>:encoding(UTF-8)')
269-
or return (0, $c->maketext("Failed to open [_1]", $scoreFilePath));
280+
my $scoreFile = Mojo::File->new($scoreFilePath)->open('>:encoding(UTF-8)')
281+
or return (0, $c->maketext('Failed to open [_1]', $scoreFilePath));
270282

271283
# Print out header info
272-
print $SCORE $c->maketext("username, last name, first name, section, achievement level, achievement score,");
273-
274-
my @achievements = $db->getAchievements(@achievementsToScore);
275-
@achievements = sortAchievements(@achievements);
284+
print $scoreFile $c->maketext('username, last name, first name, section, achievement level, achievement score,');
276285

277286
for my $achievement (@achievements) {
278-
print $SCORE $achievement->achievement_id . ", ";
279-
}
280-
print $SCORE "\n";
281-
282-
my @users = $db->listUsers;
283-
284-
# Get user records
285-
my @userRecords = ();
286-
for my $currentUser (@users) {
287-
my $userObj = $db->getUser($currentUser);
288-
die "Unable to find user object for $currentUser. " unless $userObj;
289-
push(@userRecords, $userObj);
287+
print $scoreFile $achievement->achievement_id . ', ';
290288
}
291-
292-
@userRecords =
293-
sort { (lc($a->section) cmp lc($b->section)) || (lc($a->last_name) cmp lc($b->last_name)) } @userRecords;
289+
print $scoreFile "\n";
294290

295291
# Print out achievement information for each user
296-
for my $userRecord (@userRecords) {
292+
for my $userRecord (@users) {
297293
my $user_id = $userRecord->user_id;
298-
next unless $db->existsGlobalUserAchievement($user_id);
299-
next if ($userRecord->{status} eq 'D' || $userRecord->{status} eq 'A');
300-
print $SCORE "$user_id, $userRecord->{last_name}, $userRecord->{first_name}, $userRecord->{section}, ";
301-
my $globalUserAchievement = $db->getGlobalUserAchievement($user_id);
302-
my $level_id = $globalUserAchievement->level_achievement_id;
303-
$level_id = ' ' unless $level_id;
304-
my $points = $globalUserAchievement->achievement_points;
305-
$points = 0 unless $points;
306-
print $SCORE "$level_id, $points, ";
294+
next if !$globalUserAchievements{$user_id} || $userRecord->{status} eq 'D' || $userRecord->{status} eq 'A';
295+
296+
print $scoreFile "$user_id, $userRecord->{last_name}, $userRecord->{first_name}, $userRecord->{section}, ";
297+
298+
my $level_id = $globalUserAchievements{$user_id}->level_achievement_id || ' ';
299+
my $points = $globalUserAchievements{$user_id}->achievement_points || 0;
300+
print $scoreFile "$level_id, $points, ";
307301

308302
for my $achievement (@achievements) {
309303
my $achievement_id = $achievement->achievement_id;
310-
if ($db->existsUserAchievement($user_id, $achievement_id)) {
311-
my $userAchievement = $db->getUserAchievement($user_id, $achievement_id);
312-
print $SCORE $userAchievement->earned ? "1, " : "0, ";
304+
if ($userAchievements{$user_id}{$achievement_id}) {
305+
print $scoreFile $userAchievements{$user_id}{$achievement_id}->earned ? '1, ' : '0, ';
313306
} else {
314-
print $SCORE ", ";
307+
print $scoreFile ', ';
315308
}
316309
}
317310

318-
print $SCORE "\n";
311+
print $scoreFile "\n";
319312
}
320313

321-
$SCORE->close;
314+
$scoreFile->close;
322315

323316
# Include a download link
324317
return (

0 commit comments

Comments
 (0)