Skip to content

Commit

Permalink
be able to copy more things from a course when adding a new course
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex-Jordan committed Dec 19, 2023
1 parent 2b193de commit c3dec66
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 90 deletions.
14 changes: 9 additions & 5 deletions conf/defaults.config
Original file line number Diff line number Diff line change
Expand Up @@ -678,13 +678,17 @@ $courseFiles{logs}{activity_log} = '';
# Site defaults (Usually overridden in localOverrides.conf)
################################################################################

# The default_templates_course is used by default to create a new course.
# The contents of the templates directory are copied from this course
# to the new course being created.
$siteDefaults{default_templates_course} ="modelCourse";
# The default_copy_from_course is used by default when creating a new course.
# Its templates folder, html folder, course.conf file, and simple.conf file
# might be copied into a new course. This course might not be a true course;
# it might only have a directory structure and no presense in the database.
# If it is a real course, then also its title, institution, non-student users,
# achievements, and sets can be copied.
$siteDefaults{default_copy_from_course} ="modelCourse";

# Provide a list of model courses which are not real courses, but from which
# the templates for a new course can be copied.
# the templates for a new course can be copied. This list helps exclude such
# non-real courses when that is appopriate.
$modelCoursesForCopy = [ "modelCourse" ];

################################################################################
Expand Down
27 changes: 20 additions & 7 deletions lib/WeBWorK/ContentGenerator/CourseAdmin.pm
Original file line number Diff line number Diff line change
Expand Up @@ -306,8 +306,16 @@ sub do_add_course ($c) {
my $add_initial_lastName = trim_spaces($c->param('add_initial_lastName')) || '';
my $add_initial_email = trim_spaces($c->param('add_initial_email')) || '';

my $add_templates_course = trim_spaces($c->param('add_templates_course')) || '';
my $add_config_file = trim_spaces($c->param('add_config_file')) || '';
my $copy_from_course = trim_spaces($c->param('copy_from_course')) || '';

my $copy_templates_html = $c->param('copy_templates_html') || '';
my $copy_simple_config_file = $c->param('copy_simple_config_file') || '';
my $copy_config_file = $c->param('copy_config_file') || '';
my $copy_non_students = $c->param('copy_non_students') || '';
my $copy_sets = $c->param('copy_sets') || '';
my $copy_achievements = $c->param('copy_achievements') || '';
my $copy_title = $c->param('copy_title') || '';
my $copy_institution = $c->param('copy_institution') || '';

my $add_dbLayout = trim_spaces($c->param('add_dbLayout')) || '';

Expand Down Expand Up @@ -363,11 +371,16 @@ sub do_add_course ($c) {

# Include any optional arguments, including a template course and the course title and course institution.
my %optional_arguments;
if ($add_templates_course ne '') {
$optional_arguments{templatesFrom} = $add_templates_course;
}
if ($add_config_file ne '') {
$optional_arguments{copySimpleConfig} = $add_config_file;
if ($copy_from_course ne '') {
$optional_arguments{copyFrom} = $copy_from_course;
$optional_arguments{copyTemplatesHtml} = $copy_templates_html;
$optional_arguments{copySimpleConfig} = $copy_simple_config_file;
$optional_arguments{copyConfig} = $copy_config_file;
$optional_arguments{copyNonStudents} = $copy_non_students;
$optional_arguments{copySets} = $copy_sets;
$optional_arguments{copyAchievements} = $copy_achievements;
$optional_arguments{copyTitle} = $copy_title;
$optional_arguments{copyInstitution} = $copy_institution;
}
if ($add_courseTitle ne '') {
$optional_arguments{courseTitle} = $add_courseTitle;
Expand Down
238 changes: 175 additions & 63 deletions lib/WeBWorK/Utils/CourseManagement.pm
Original file line number Diff line number Diff line change
Expand Up @@ -143,18 +143,27 @@ sub listArchivedCourses {
%options must contain:
courseID => $courseID,
ce => $ce,
courseOptions => $courseOptions,
users => $users
courseID => course ID for the new course,
ce => a course environment for the new course,
courseOptions => hash ref explained below
users => array ref explained below
%options may contain:
templatesFrom => $templatesCourseID,
courseTitle => $courseTitle
courseInstitution => $courseInstitution
copyFrom => some course ID to copy things from,
courseTitle => a title for the new course
courseInstitution => institution for the new course
copyTemplatesHtml => boolean
copySimpleConfig => boolean
copyConfig => boolean
copyNonStudents => boolean
copySets => boolean
copyAchievements => boolean
copyTitle => boolean
copyInstitution => boolean
Create a new course named $courseID.
Create a new course with ID $courseID.
$ce is a WeBWorK::CourseEnvironment object that describes the new course's
environment.
Expand All @@ -173,8 +182,18 @@ PermissionLevel record for a single user:
These users are added to the course.
$templatesCourseID indicates the ID of a course from which the contents of the
templates directory will be copied to the new course.
C<copyFrom> indicates the ID of a course from which various things may be
copied into the new course. Which things are copied are controlled by the
boolean options:
* copyTemplatesHtml (contents of Templates and HTML folders)
* copySimpleConfig (simple.conf file)
* copyConfig (course.conf file)
* copyNonStudents (all non-student users, their permission level, and password)
* copySets (all global sets, global set locations, and global problems)
* copyAchievements (all achievements)
* copyTitle (the course title, which will override courseTitle)
* copyInstitution (the course institution, which will override courseInstitution)
=cut

Expand All @@ -188,6 +207,7 @@ sub addCourse {
}

my $courseID = $options{courseID};
my $sourceCourse = $options{copyFrom} // '';
my $ce = $options{ce};
my %courseOptions = %{ $options{courseOptions} };
my @users = exists $options{users} ? @{ $options{users} } : ();
Expand Down Expand Up @@ -293,15 +313,42 @@ sub addCourse {

##### step 3: populate course database #####

# db object for course to copy things from
my $db0;
if (
$sourceCourse ne ''
&& !(grep { $sourceCourse eq $_ } @{ $ce->{modelCoursesForCopy} })
&& ($options{copyNonStudents}
|| $options{copySets}
|| $options{copyAchievements}
|| $options{copyTitle}
|| $options{copyInstitution})
)
{
my $ce0 = WeBWorK::CourseEnvironment->new({ courseName => $sourceCourse });
$db0 = WeBWorK::DB->new($ce0->{dbLayouts}{$dbLayoutName});
}

# add users (users that were directly passed to addCourse() as well as those copied from a source course)
if ($ce->{dbLayouts}{$dbLayoutName}{user}{params}{non_native}) {
debug("not adding users to the course database: 'user' table is non-native.\n");
} else {
# see above
#my $db = WeBWorK::DB->new($ce->{dbLayouts}->{$dbLayoutName});
if ($db0 && $options{copyNonStudents}) {
my @non_student_ids =
map {@$_} ($db0->listPermissionLevelsWhere({ permission => { not_like => '0' } }, 'user_id'));
my %original_users = map { $_->[0]{user_id} => 1 } (@users);

for my $user_id (@non_student_ids) {
next if $original_users{$user_id};
my @User = $db0->getUsersWhere({ user_id => $user_id });
my @Password = $db0->getPasswordsWhere({ user_id => $user_id });
my @PermissionLevel = $db0->getPermissionLevelsWhere({ user_id => $user_id });
push @users, [ $User[0], $Password[0], $PermissionLevel[0] ];
}
}

foreach my $userTriple (@users) {
my ($User, $Password, $PermissionLevel) = @$userTriple;

eval { $db->addUser($User) };
warn $@ if $@;
eval { $db->addPassword($Password) };
Expand All @@ -311,11 +358,56 @@ sub addCourse {
}
}

if (exists $options{courseTitle}) {
$db->setSettingValue('courseTitle', $options{courseTitle});
# add sets
if ($db0 && $options{copySets}) {
if ($ce->{dbLayouts}{$dbLayoutName}{set}{params}{non_native}) {
debug("not adding sets to the course database: 'set' table is non-native.\n");
} else {
my @set_ids = $db0->listGlobalSets;
for my $set_id (@set_ids) {
eval { $db->addGlobalSet($db0->getGlobalSet($set_id)) };
warn $@ if $@;

my @Problem = $db0->getGlobalProblemsWhere({ set_id => $set_id });
for my $problem (@Problem) {
eval { $db->addGlobalProblem($problem) };
warn $@ if $@;
}

my @Location = $db0->getGlobalSetLocationsWhere({ set_id => $set_id });
for my $location (@Location) {
eval { $db->addGlobalSetLocation($location) };
warn $@ if $@;
}
}
}
}
if (exists $options{courseInstitution}) {
$db->setSettingValue('courseInstitution', $options{courseInstitution});

# add achievements
if ($db0 && $options{copyAchievements}) {
if ($ce->{dbLayouts}{$dbLayoutName}{achievement}{params}{non_native}) {
debug("not adding achievements to the course database: 'achievement' table is non-native.\n");
} else {
my @achievement_ids = $db0->listAchievements;
for my $achievement_id (@achievement_ids) {
eval { $db->addAchievement($db0->getAchievement($achievement_id)) };
warn $@ if $@;
}
}
}

# copy title and/or institution if requested
if ($db0 && ($options{copyTitle} || $options{copyInstitution})) {
if ($ce->{dbLayouts}{$dbLayoutName}{setting}{params}{non_native}) {
debug("not copying settings to the course database: 'setting' table is non-native.\n");
} else {
$db->setSettingValue('courseTitle', $db0->getSettingValue('courseTitle')) if ($options{copyTitle});
$db->setSettingValue('courseInstitution', $db0->getSettingValue('{courseInstitution'))
if ($options{copyInstitution});
}
} else {
$db->setSettingValue('courseTitle', $options{courseTitle}) if (exists $options{courseTitle});
$db->setSettingValue('courseInstitution', $options{courseInstitution}) if (exists $options{courseInstitution});
}

##### step 4: write course.conf file #####
Expand All @@ -326,55 +418,57 @@ sub addCourse {
writeCourseConf($fh, $ce, %courseOptions);
close $fh;

##### step 5: copy templates, html, and simple.conf if desired #####

if (exists $options{templatesFrom}) {
my $sourceCourse = $options{templatesFrom};
my $sourceCE = WeBWorK::CourseEnvironment->new({ get_SeedCE($ce), courseName => $sourceCourse });
my $sourceDir = $sourceCE->{courseDirs}->{templates};
## copy templates ##
if (-d $sourceDir) {
my $destDir = $ce->{courseDirs}{templates};
my $cp_cmd = "2>&1 "
. $ce->{externalPrograms}{cp} . " -R "
. shell_quote($sourceDir) . "/* "
. shell_quote($destDir);
my $cp_out = readpipe $cp_cmd;
if ($?) {
my $exit = $? >> 8;
my $signal = $? & 127;
my $core = $? & 128;
warn
"Failed to copy templates from course '$sourceCourse' with command '$cp_cmd' (exit=$exit signal=$signal core=$core): $cp_out\n";
##### step 5: copy templates, html, simple.conf, course.conf if desired #####

if ($sourceCourse ne '') {
my $sourceCE = WeBWorK::CourseEnvironment->new({ get_SeedCE($ce), courseName => $sourceCourse });
my $sourceDir = $sourceCE->{courseDirs}{templates};
if ($options{copyTemplatesHtml}) {
## copy templates ##
if (-d $sourceDir) {
my $destDir = $ce->{courseDirs}{templates};
my $cp_cmd = "2>&1 "
. $ce->{externalPrograms}{cp} . " -R "
. shell_quote($sourceDir) . "/* "
. shell_quote($destDir);
my $cp_out = readpipe $cp_cmd;
if ($?) {
my $exit = $? >> 8;
my $signal = $? & 127;
my $core = $? & 128;
warn "Failed to copy templates from course '$sourceCourse' "
. "with command '$cp_cmd' (exit=$exit signal=$signal core=$core): $cp_out\n";
}
} else {
warn "Failed to copy templates from course '$sourceCourse': "
. "templates directory '$sourceDir' does not exist.\n";
}
} else {
warn
"Failed to copy templates from course '$sourceCourse': templates directory '$sourceDir' does not exist.\n";
}
## copy html ##
## this copies the html/tmp directory as well which is not optimal
$sourceDir = $sourceCE->{courseDirs}->{html};
if (-d $sourceDir) {
my $destDir = $ce->{courseDirs}{html};
my $cp_cmd = "2>&1 "
. $ce->{externalPrograms}{cp} . " -R "
. shell_quote($sourceDir) . "/* "
. shell_quote($destDir);
my $cp_out = readpipe $cp_cmd;
if ($?) {
my $exit = $? >> 8;
my $signal = $? & 127;
my $core = $? & 128;
warn
"Failed to copy html from course '$sourceCourse' with command '$cp_cmd' (exit=$exit signal=$signal core=$core): $cp_out\n";
## copy html ##
## this copies the html/tmp directory as well which is not optimal
$sourceDir = $sourceCE->{courseDirs}{html};
if (-d $sourceDir) {
my $destDir = $ce->{courseDirs}{html};
my $cp_cmd = "2>&1 "
. $ce->{externalPrograms}{cp} . " -R "
. shell_quote($sourceDir) . "/* "
. shell_quote($destDir);
my $cp_out = readpipe $cp_cmd;
if ($?) {
my $exit = $? >> 8;
my $signal = $? & 127;
my $core = $? & 128;
warn "Failed to copy html from course '$sourceCourse' "
. "with command '$cp_cmd' (exit=$exit signal=$signal core=$core): $cp_out\n";
}
} else {
warn "Failed to copy html from course '$sourceCourse': "
. "html directory '$sourceDir' does not exist.\n";
}
} else {
warn "Failed to copy html from course '$sourceCourse': html directory '$sourceDir' does not exist.\n";
}
## copy config files ##
# this copies the simple.conf file if desired
if (exists $options{copySimpleConfig}) {
my $sourceFile = $sourceCE->{courseFiles}->{simpleConfig};
if ($options{copySimpleConfig}) {
my $sourceFile = $sourceCE->{courseFiles}{simpleConfig};
if (-e $sourceFile) {
my $destFile = $ce->{courseFiles}{simpleConfig};
my $cp_cmd =
Expand All @@ -384,13 +478,31 @@ sub addCourse {
my $exit = $? >> 8;
my $signal = $? & 127;
my $core = $? & 128;
warn
"Failed to copy simple.conf from course '$sourceCourse' with command '$cp_cmd' (exit=$exit signal=$signal core=$core): $cp_out\n";
warn "Failed to copy simple.conf from course '$sourceCourse' "
. "with command '$cp_cmd' (exit=$exit signal=$signal core=$core): $cp_out\n";
}
}
}
# this copies the course.conf file if desired
if ($options{copyConfig}) {
my $sourceFile = $sourceCE->{courseFiles}{environment};
if (-e $sourceFile) {
my $destFile = $ce->{courseFiles}{environment};
my $cp_cmd =
join(" ", ("2>&1", $ce->{externalPrograms}{cp}, shell_quote($sourceFile), shell_quote($destFile)));
my $cp_out = readpipe $cp_cmd;
if ($?) {
my $exit = $? >> 8;
my $signal = $? & 127;
my $core = $? & 128;
warn "Failed to copy course.conf from course '$sourceCourse' "
. "with command '$cp_cmd' (exit=$exit signal=$signal core=$core): $cp_out\n";
}
}
}

}

}

################################################################################
Expand Down
14 changes: 10 additions & 4 deletions templates/ContentGenerator/Base/admin_links.html.ep
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,16 @@
% 'add_course',
% maketext('Add Courses'),
% {
% add_admin_users => 1,
% add_config_file => 1,
% add_dbLayout => 'sql_single',
% add_templates_course => $ce->{siteDefaults}{default_templates_course} || ''
% add_admin_users => 1,
% copy_from_course => $ce->{siteDefaults}{default_copy_from_course} || '',
% copy_templates_html => 1,
% copy_simple_config_file => 1,
% copy_non_students => 1,
% copy_sets => 1,
% copy_achievements => 1,
% copy_title => 1,
% copy_institution => 1,
% add_dbLayout => 'sql_single'
% }
% ],
% [ 'rename_course', maketext('Rename Courses') ],
Expand Down
Loading

0 comments on commit c3dec66

Please sign in to comment.