Skip to content

Commit

Permalink
Load OPL set definitions from files instead of searching the OPL dire…
Browse files Browse the repository at this point in the history
…ctory.

This is done in the getDefList method of lib/WeBWorK/ContentGenerator/Instructor.pm.  The only time that method is called is when the homework sets editor is loaded.  Note that the course's templates directory is still searched, but the Library and Contrib subdirectories are pruned and so not look into.

A new script `bin/generate-OPL-set-def-lists.pl` is created that generates the needed data files by searching the OPL directory.  Note that this script is executed by `bin/OPL-update`.  For now, this scrit is executed in docker-entrypoint.sh when the docker container is started.  However, these files should be added to the OPL release built by webwork2/bin/OPL_releases/release.sh, and dealt with in the same way as the other files in webwork2/htdocs/DATA.  Of course, that whole process needs to be updated and removed from the personal fork it is currently in.  The release there is now more than two years old.

The defaults.config option $option{useOPLdefFiles} still is checked to determine if OPL set defintion files will be loaded and listed, but note that the $options{setDefSearchDepth} is ignored for this.  The new defaults.config option $option{useContribDefFiles} determines if set definition files from the Contrib directory will be loaded and listed.  This also ignores depth.  So the search depth option only applies to local directories for the course.
  • Loading branch information
drgrice1 committed Apr 7, 2022
1 parent 943664f commit 30e04f8
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 36 deletions.
2 changes: 2 additions & 0 deletions bin/OPL-update
Original file line number Diff line number Diff line change
Expand Up @@ -931,5 +931,7 @@ if ($ce->{problemLibrary}{showLibraryLocalStats} ||
do $ENV{WEBWORK_ROOT}.'/bin/load-OPL-global-statistics.pl';
}

# Generate set definition list files.
do $ENV{WEBWORK_ROOT} . '/bin/generate-OPL-set-def-lists.pl';

print "\nDone.\n";
107 changes: 107 additions & 0 deletions bin/generate-OPL-set-def-lists.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/usr/bin/env perl

=head1 NAME
generate-OPL-set-def-list - find all set definition files in the OPL and Contrib
=head1 SYNOPSIS
generate-OPL-set-def-list
The environment variable $WEBWORK_ROOT must be set with the location of
webwork2, and either the environment variable $PG_ROOT must be set with the
location of pg, or pg must be located in the parent directory of the webwork2
location.
=head1 DESCRIPTION
This script will find all set definition files in the OpenProblemLibrary and
Contrib subdirectories of the webwork-open-problem-library and list them in the
files $WEBWORK_ROOT/htdocs/DATA/library-set-defs.json and
$WEBWORK_ROOT/htdocs/DATA/contrib-set-defs.json.
=cut

use strict;
use warnings;

use Pod::Usage;
use File::Find;

my $pg_root;

BEGIN {
pod2usage(2) unless exists $ENV{WEBWORK_ROOT};
$pg_root = $ENV{PG_ROOT} // "$ENV{WEBWORK_ROOT}/../pg";
pod2usage(2) unless (-e $pg_root);
}

use lib "$ENV{WEBWORK_ROOT}/lib";
use lib "$pg_root/lib";
use lib "$ENV{WEBWORK_ROOT}/bin";

use OPLUtils qw/writeJSONtoFile/;
use WeBWorK::CourseEnvironment;

my $ce = new WeBWorK::CourseEnvironment({ webwork_dir => $ENV{WEBWORK_ROOT} });
my $libraryRoot = $ce->{problemLibrary}{root};
my $contribRoot = $ce->{contribLibrary}{root};

print "Using WeBWorK root: $ENV{WEBWORK_ROOT}\n";
print "Using PG root: $pg_root\n";
print "Using library root: $libraryRoot\n";
print "Using contrib root: $contribRoot\n";

# Search the OPL directory for set definition files.
my @opl_set_defs;
find(
{
wanted => sub {
push @opl_set_defs, $_ =~ s|^$libraryRoot/?|Library/|r if m|/set[^/]*\.def$|;
},
follow_fast => 1,
no_chdir => 1
},
$libraryRoot
);

# Search the Contrib directory for set definition files.
my @contrib_set_defs;
find(
{
wanted => sub {
push @contrib_set_defs, $_ =~ s|^$contribRoot/?|Contrib/|r if m|/set[^/]*\.def$|;
},
follow_fast => 1,
no_chdir => 1
},
$contribRoot
);

sub depth_then_iname_sort {
my $file_list = shift;
my @file_depths;
my @uc_file_names;
for (@$file_list) {
push @file_depths, scalar(@{ [ $_ =~ /\//g ] });
push @uc_file_names, uc($_);
}
@$file_list =
@$file_list[ sort { $file_depths[$a] <=> $file_depths[$b] || $uc_file_names[$a] cmp $uc_file_names[$b] }
0 .. $#$file_list ];
}

# Sort the files first by depth then by path.
depth_then_iname_sort(\@opl_set_defs);
depth_then_iname_sort(\@contrib_set_defs);

# Write the lists to the files in htdocs/DATA.
if ($ce->{options}{useOPLdefFiles}) {
writeJSONtoFile(\@opl_set_defs, "$ce->{webworkDirs}{htdocs}/DATA/library-set-defs.json");
print "Saved OPL set definition list to $ce->{webworkDirs}{htdocs}/DATA/library-set-defs.json.\n";
}

if ($ce->{options}{useContribDefFiles}) {
writeJSONtoFile(\@contrib_set_defs, "$ce->{webworkDirs}{htdocs}/DATA/contrib-set-defs.json");
print "Saved Contrib set definition list to $ce->{webworkDirs}{htdocs}/DATA/contrib-set-defs.json.\n";
}
13 changes: 10 additions & 3 deletions conf/defaults.config
Original file line number Diff line number Diff line change
Expand Up @@ -986,12 +986,19 @@ $pg{displayModes} = [
# Whether the homework editor pages should show options for conditional release
$options{enableConditionalRelease} = 0;

# In the hmwk sets editor, how deep to search within templates for .def files;
# 0 means only within templates
# In the hmwk sets editor, how deep to search within templates for .def files.
# Note that this does not apply to the Library and Contrib directories.
# Those directories are not searched in any case (see below).
# 0 means only within templates.
$options{setDefSearchDepth} = 4;

# In the hmwk sets editor, delve into the OPL while searching for .def files?
# In the hmwk sets editor, also list OPL or Contrib set defintion files. Note
# that the directories are not searched, but these lists are loaded from the
# files htdocs/DATA/library-set-defs.json and htdocs/DATA/contrib-set-defs.json
# which are generated by running bin/generate-OPL-set-def-lists.pl (which is
# also run if you run bin/OPL-update).
$options{useOPLdefFiles} = 1;
$options{useContribDefFiles} = 1;


##########################################################################################
Expand Down
12 changes: 12 additions & 0 deletions docker-config/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,18 @@ if [ "$1" = 'apache2' ]; then
fi
rm $APP_ROOT/libraries/Restore_or_build_OPL_tables
fi

# Run generate-OPL-set-def-lists.pl if necessary.
# Note that these files will be restored above if they exist from a previous run.
if [ ! -f "$APP_ROOT/libraries/webwork-open-problem-library/JSON-SAVED/library-set-defs.json" ] ||
[ ! -f "$APP_ROOT/libraries/webwork-open-problem-library/JSON-SAVED/contrib-set-defs.json" ]
then
echo "The library-set-defs.json or contrib-set-defs.json file is missing."
echo "These files will be generated by executing bin/generate-OPL-set-def-lists.pl."
$WEBWORK_ROOT/bin/generate-OPL-set-def-lists.pl
cp -a $WEBWORK_ROOT/htdocs/DATA/*.json $APP_ROOT/libraries/webwork-open-problem-library/JSON-SAVED
fi

# Compile chromatic/color.c if necessary - may be needed for PG directory mounted from outside image
if [ ! -f "$APP_ROOT/pg/lib/chromatic/color" ]; then
cd $APP_ROOT/pg/lib/chromatic
Expand Down
82 changes: 49 additions & 33 deletions lib/WeBWorK/ContentGenerator/Instructor.pm
Original file line number Diff line number Diff line change
Expand Up @@ -662,40 +662,56 @@ sub getCSVList {
return grep { not m/^\./ and m/\.lst$/ and -f "$dir/$_" } WeBWorK::Utils::readDirectory($dir);
}

sub loadSetDefListFile {
my $file = shift;

if (-r $file) {
my $data = do {
open(my $fh, "<:encoding(UTF-8)", $file)
or die "FATAL: Unable to open '$file'!";
local $/;
<$fh>;
};

return @{ JSON->new->decode($data) };
}
}

sub getDefList {
my ($self) = @_;
my $ce = $self->{ce};
my $topdir = $ce->{courseDirs}->{templates};
my @b = $topdir =~ /\//g; #count slashes to gauge depth
my $base_depth = @b;
my $max_depth = $ce->{options}->{setDefSearchDepth}+$base_depth;
my $searchOPL = $ce->{options}->{useOPLdefFiles};
my @found_set_defs;
# get_set_defs_wanted is a closure over @found_set_defs
my $get_set_defs_wanted = sub {
return if ($File::Find::dir =~ /$topdir\/Library/) and not $searchOPL;
my @d = $File::Find::dir =~ /\//g; #count slashes to gauge depth
my $depth = @d;
$depth-- if $_ eq $topdir;
$File::Find::prune = 1 if $depth >= $max_depth;
push @found_set_defs, $_ if m|/set[^/]*\.def$|;
};
find({ wanted => $get_set_defs_wanted, follow_fast=>1, no_chdir=>1}, $topdir);
map { $_ =~ s|^$topdir/?|| } @found_set_defs;
my @slashes = ();
my @caps = ();
for (@found_set_defs) {
my @s = $_ =~ /\//g;
my $slashcount = @s;
push @slashes, ( $slashcount );
push @caps, uc($_);
}
return @found_set_defs[ sort {
$slashes[$a] <=> $slashes[$b]
||
$caps[$a] cmp $caps[$b]
} 0..$#found_set_defs
];
my $self = shift;
my $ce = $self->{ce};
my $topdir = $ce->{courseDirs}{templates};

# Search to a depth of the setDefSearchDepth value plus the depth of the templates directory.
my $max_depth = $ce->{options}{setDefSearchDepth} + @{ [ $topdir =~ /\//g ] };

my @found_set_defs;

# get_set_defs_wanted is a closure over @found_set_defs
my $get_set_defs_wanted = sub {
$File::Find::prune = 1, return
if $File::Find::dir =~ /^$topdir\/Library/ || $File::Find::dir =~ /^$topdir\/Contrib/;
$File::Find::prune = 1, return if @{ [ $File::Find::dir =~ /\//g ] } > $max_depth;
push @found_set_defs, $_ =~ s|^$topdir/?||r if m|/set[^/]*\.def$|;
};

find({ wanted => $get_set_defs_wanted, follow_fast => 1, no_chdir => 1 }, $topdir);

# Load the OPL set definition files from the list file.
push(@found_set_defs, loadSetDefListFile("$ce->{webworkDirs}{htdocs}/DATA/library-set-defs.json"))
if ($ce->{options}{useOPLdefFiles});

# Load the Contrib set definition files from the list file.
push(@found_set_defs, loadSetDefListFile("$ce->{webworkDirs}{htdocs}/DATA/contrib-set-defs.json"))
if ($ce->{options}{useContribDefFiles});

my @depths;
my @caps;
for (@found_set_defs) {
push @depths, scalar(@{ [ $_ =~ /\//g ] });
push @caps, uc($_);
}
return @found_set_defs[ sort { $depths[$a] <=> $depths[$b] || $caps[$a] cmp $caps[$b] } 0 .. $#found_set_defs ];
}

sub getScoringFileList {
Expand Down

0 comments on commit 30e04f8

Please sign in to comment.