Skip to content

Commit

Permalink
Merge pull request #1653 from drgrice1/load-opl-set-defs-from-file
Browse files Browse the repository at this point in the history
Load OPL set definitions from files instead of searching the OPL directory.
  • Loading branch information
pstaabp authored Apr 8, 2022
2 parents adc0f54 + 1ecdb0d commit 801c826
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 60 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";
103 changes: 103 additions & 0 deletions bin/generate-OPL-set-def-lists.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#!/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.
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";

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
25 changes: 1 addition & 24 deletions lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm
Original file line number Diff line number Diff line change
Expand Up @@ -154,25 +154,6 @@ sub list_pg_files {
return sortByName(undef,@pgs);
}

## Search for set definition files

sub get_set_defs {
my $topdir = shift;
my @found_set_defs;
# get_set_defs_wanted is a closure over @found_set_defs
my $get_set_defs_wanted = sub {
#my $fn = $_;
#my $fdir = $File::Find::dir;
#return() if($fn !~ /^set.*\.def$/);
##return() if(not -T $fn);
#push @found_set_defs, "$fdir/$fn";
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;
return @found_set_defs;
}

## Try to make reading of set defs more flexible. Additional strategies
## for fixing a path can be added here.

Expand Down Expand Up @@ -957,11 +938,7 @@ sub browse_setdef_panel {
my $ce = $r->ce;
my $library_selected = shift // '';

# In the following line, the parens after sort are important. If they are
# omitted, sort will interpret get_set_defs as the name of the comparison
# function, and ($ce->{courseDirs}{templates}) as a single element list to
# be sorted.
my @list_of_set_defs = sort(get_set_defs($ce->{courseDirs}{templates}));
my @list_of_set_defs = $self->getDefList;
my $labels_for_set_defs = { map { $_ => $_ } @list_of_set_defs };

if (scalar(@list_of_set_defs) == 0) {
Expand Down

0 comments on commit 801c826

Please sign in to comment.