From 88408f6572052a8e4912ea610550c266029b5311 Mon Sep 17 00:00:00 2001 From: Nathan Wallach Date: Sun, 5 Aug 2018 18:44:07 +0300 Subject: [PATCH 01/10] Add code to support multiple libraries in the lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm library browser. Needed to change how the config settings are made in localOverrides.conf so settings for multiple libraries can be made, and to read in the settings using the new layout. Changes were also mode to htdocs/js/apps/SetMaker/setmaker.js to support the choice of a library or "All Libraries". --- .gitignore | 6 +- bin/OPL-update | 38 +-- bin/OPLUtils.pm | 59 +++-- bin/setfilepermissions | 2 +- bin/update-OPL-statistics | 8 +- conf/defaults.config | 18 +- conf/localOverrides.conf.dist | 132 +++++++++-- conf/site.conf.dist | 5 +- htdocs/js/apps/SetMaker/setmaker.js | 36 ++- lib/WeBWorK/ContentGenerator/CourseAdmin.pm | 10 +- .../ContentGenerator/Instructor/SetMaker.pm | 216 +++++++++++++++--- .../ContentGenerator/Instructor/SetMaker2.pm | 11 +- .../Instructor/SetMakernojs.pm | 36 +-- lib/WeBWorK/ContentGenerator/Problem.pm | 8 +- lib/WeBWorK/Utils/LibraryStats.pm | 14 +- lib/WeBWorK/Utils/ListingDB.pm | 202 ++++++++++++++-- lib/WebworkWebservice/LibraryActions.pm | 67 +++++- 17 files changed, 722 insertions(+), 146 deletions(-) diff --git a/.gitignore b/.gitignore index 2129ff5078..8dca4e4871 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,9 @@ applets tmp logs courses.dist -library-directory-tree.json -library-subject-tree.json -textbook-tree.json +*library-directory-tree.json +*library-subject-tree.json +*textbook-tree.json development.yml *~ !tmp/README diff --git a/bin/OPL-update b/bin/OPL-update index 72382f38cd..502826ec08 100755 --- a/bin/OPL-update +++ b/bin/OPL-update @@ -22,6 +22,9 @@ use Cwd; use DBI; +my $myLib = "OPL" ; # default value + + #(maximum varchar length is 255 for mysql version < 5.0.3. #You can increase path length to 4096 for mysql > 5.0.3) @@ -96,9 +99,9 @@ my $dbh = DBI->connect( }, ); -my $libraryRoot = $ce->{problemLibrary}->{root}; +my $libraryRoot = $ce->{problemLibrary}->{$myLib}->{root}; $libraryRoot =~ s|/+$||; -my $libraryVersion = $ce->{problemLibrary}->{version}; +my $libraryVersion = $ce->{problemLibrary}->{$myLib}->{version}; my $db_storage_engine = $ce->{problemLibrary_db}->{storage_engine}; my $verbose = 0; @@ -431,7 +434,7 @@ if(open(IN, "$libraryRoot/Textbooks")) { close(IN); } else { print "Textbooks file was not found in library $libraryRoot. If the path to the problem library doesn't seem - correct, make modifications in webwork2/conf/site.conf (\$problemLibrary{root}). If that is correct then + correct, make modifications in webwork2/conf/localOverrides.conf (\$problemLibrary{OPL}{root}). If that is correct then updating from git should download the Textbooks file.\n"; } #### End of textbooks @@ -453,7 +456,7 @@ if(open(IN, "$libraryRoot/Taxonomy2")) { $canopenfile = 1; } else { print "Taxonomy file was not found in library $libraryRoot. If the path to the problem library doesn't seem - correct, make modifications in webwork2/conf/site.conf (\$problemLibrary{root}). If that is correct then + correct, make modifications in webwork2/conf/localOverrides.conf (\$problemLibrary{OPL}{root}). If that is correct then updating from git should download the Taxonomy file.\n"; } @@ -514,7 +517,12 @@ if($canopenfile) { #### Save the official taxonomy in json format my $webwork_htdocs = $ce->{webwork_dir}."/htdocs"; -my $file = "$webwork_htdocs/DATA/tagging-taxonomy.json"; + +# Get the filename for the taxo_file which is set for THIS library via the +# settings in conf/defaults.config and/or conf/localOverrides.conf +my $taxo_file = $ce->{problemLibrary}->{$myLib}->{taxo}; + +my $file = "$webwork_htdocs/DATA/${taxo_file}"; open(OUTF, ">$file") or die "Cannot open $file"; print OUTF to_json($tagtaxo,{pretty=>1}) or die "Cannot write to $file"; close(OUTF); @@ -886,7 +894,7 @@ for my $chap (@{$dbchaps}) { } # Now run the script build-library-tree -# This is used to create the file library-tree.json which can be used to +# This is used to create the file ${myLib}-library-tree.json which can be used to # load in subject-chapter-section information for the OPL use strict; @@ -960,16 +968,20 @@ foreach my $i (0..$#subjects){ push (@subject_tree, $clone); } -build_library_directory_tree($ce); -build_library_subject_tree($ce,$dbh); -build_library_textbook_tree($ce,$dbh); +build_library_directory_tree($myLib,$ce); +build_library_subject_tree($myLib,$ce,$dbh); +build_library_textbook_tree($myLib,$ce,$dbh); $dbh->disconnect; -if ($ce->{problemLibrary}{showLibraryLocalStats} || - $ce->{problemLibrary}{showLibraryGlobalStats}) { - print "\nUpdating Library Statistics.\n"; - do $ENV{WEBWORK_ROOT}.'/bin/update-OPL-statistics'; +if ( $myLib eq "OPL" ) { + # Only the "real" OPL has Library statistics + + if ($ce->{problemLibrary}{$myLib}{showLibraryLocalStats} || + $ce->{problemLibrary}{$myLib}{showLibraryGlobalStats}) { + print "\nUpdating Library Statistics.\n"; + do $ENV{WEBWORK_ROOT}.'/bin/update-OPL-statistics'; + } } diff --git a/bin/OPLUtils.pm b/bin/OPLUtils.pm index 323436ab48..539e990deb 100644 --- a/bin/OPLUtils.pm +++ b/bin/OPLUtils.pm @@ -6,11 +6,13 @@ use base qw(Exporter); # This file contains the subroutines that build JSON files from the database to help speed up the client side. # # The following files are created: -# 1. $webwork_htdocs/DATA/library-directory-tree.json (the directory structure of the library) -# 2. $webwork_htdocs/DATA/library-subject-tree.json (the subject/chapter/section struture of the library) -# 3. +# 1. $webwork_htdocs/DATA/LIBNAME-library-directory-tree.json (the directory structure of the library) +# 2. $webwork_htdocs/DATA/LIBNAME-library-subject-tree.json (the subject/chapter/section struture of the library) +# 3. $webwork_htdocs/DATA/LIBNAME-textbook-tree.json (textbook data) -# This is used to create the file library-directory-tree.json which can be used to load in +# The filenames are set via hash values for the current library in conf/defaults.config and/or conf/localOverrides.conf + +# This is used to create the file LIBNAME-library-directory-tree.json which can be used to load in # directory information for the OPL. It writes the file as a JSON of directories to be easily loaded. use strict; @@ -25,6 +27,10 @@ use JSON; our @EXPORT = (); our @EXPORT_OK = qw(build_library_directory_tree build_library_subject_tree build_library_textbook_tree); +# what library are we handling, now is first argument of the function +my $myLib = shift; + + ### Data for creating the database tables @@ -65,16 +71,17 @@ my %NPLtables = ( - - sub build_library_directory_tree { + # what library are we handling, now is first argument of the function + my $myLib = shift; + my $ce = shift; print "Creating the Directory Tree\n"; - my $libraryRoot = $ce->{problemLibrary}->{root}; + my $libraryRoot = $ce->{problemLibrary}->{$myLib}->{root}; $libraryRoot =~ s|/+$||; - my $libraryVersion = $ce->{problemLibrary}->{version}; + my $libraryVersion = $ce->{problemLibrary}->{$myLib}->{version}; my($filename, $directories) = fileparse($libraryRoot); @@ -82,7 +89,12 @@ sub build_library_directory_tree { push(@dirArray,buildTree($libraryRoot)); my $webwork_htdocs = $ce->{webwork_dir}."/htdocs"; - my $file = "$webwork_htdocs/DATA/library-directory-tree.json"; + + # Determine the proper json file names to use for THIS library via the + # settings in conf/defaults.config and/or conf/localOverrides.conf + my $jsonFile = $ce->{problemLibrary}->{$myLib}->{tree}; + + my $file = "$webwork_htdocs/DATA/${jsonFile}"; # use a variable for the file handle my $OUTFILE; @@ -144,11 +156,12 @@ sub buildTree { sub build_library_subject_tree { - my ($ce,$dbh) = @_; + # what library are we handling, now is first argument of the function + my ($myLib,$ce,$dbh) = @_; - my $libraryRoot = $ce->{problemLibrary}->{root}; + my $libraryRoot = $ce->{problemLibrary}->{$myLib}->{root}; $libraryRoot =~ s|/+$||; - my $libraryVersion = $ce->{problemLibrary}->{version}; + my $libraryVersion = $ce->{problemLibrary}->{$myLib}->{version}; my %tables = ($libraryVersion eq '2.5')? %OPLtables : %NPLtables; @@ -272,7 +285,12 @@ sub build_library_subject_tree { print "\n"; my $webwork_htdocs = $ce->{webwork_dir}."/htdocs"; - my $file = "$webwork_htdocs/DATA/library-subject-tree.json"; + + # Determine the proper json file names to use for THIS library via the + # settings in conf/defaults.config and/or conf/localOverrides.conf + my $jsonFile = $ce->{problemLibrary}->{$myLib}->{subj}; + + my $file = "$webwork_htdocs/DATA/${jsonFile}"; # use a variable for the file handle my $OUTFILE; @@ -292,12 +310,12 @@ sub build_library_subject_tree { } sub build_library_textbook_tree { + # what library are we handling, now is first argument of the function + my ($myLib,$ce,$dbh) = @_; - my ($ce,$dbh) = @_; - - my $libraryRoot = $ce->{problemLibrary}->{root}; + my $libraryRoot = $ce->{problemLibrary}->{$myLib}->{root}; $libraryRoot =~ s|/+$||; - my $libraryVersion = $ce->{problemLibrary}->{version}; + my $libraryVersion = $ce->{problemLibrary}->{$myLib}->{version}; my %tables = ($libraryVersion eq '2.5')? %OPLtables : %NPLtables; @@ -376,7 +394,12 @@ sub build_library_textbook_tree { print "\n"; my $webwork_htdocs = $ce->{webwork_dir}."/htdocs"; - my $file = "$webwork_htdocs/DATA/textbook-tree.json"; + + # Determine the proper json file names to use for THIS library via the + # settings in conf/defaults.config and/or conf/localOverrides.conf + my $jsonFile = $ce->{problemLibrary}->{$myLib}->{text}; + + my $file = "$webwork_htdocs/DATA/${jsonFile}"; # use a variable for the file handle my $OUTFILE; diff --git a/bin/setfilepermissions b/bin/setfilepermissions index eb9ab9e03c..00854c9d69 100755 --- a/bin/setfilepermissions +++ b/bin/setfilepermissions @@ -72,7 +72,7 @@ system("chmod g+w ".$ce->{pg_dir}."/lib/chromatic"); # The server should not be able to write to the OPL (for most sites) -my $libroot = $ce->{problemLibrary}->{root}; +my $libroot = $ce->{problemLibrary}->{OPL}->{root}; system("chown -R $me $libroot"); system("chmod -R 755 $libroot"); diff --git a/bin/update-OPL-statistics b/bin/update-OPL-statistics index 528b165f1c..35efd8cdaa 100755 --- a/bin/update-OPL-statistics +++ b/bin/update-OPL-statistics @@ -182,7 +182,8 @@ $dbh->commit(); # check to see if the global statistics file exists and if it does, upload it. -my $global_sql_file = $ce->{problemLibrary}{root}.'/OPL_global_statistics.sql'; +# FIXME - currently hardwired for OPL +my $global_sql_file = $ce->{problemLibrary}{OPL}{root}.'/OPL_global_statistics.sql'; if (-e $global_sql_file) { @@ -201,6 +202,9 @@ DROP TABLE IF EXISTS OPL_global_statistics; EOS $dbh->commit(); + # Added NSW + $dbh->disconnect(); + $dbuser = shell_quote($dbuser); $dbpass = shell_quote($dbpass); $db = shell_quote($db); @@ -209,6 +213,8 @@ EOS `$mysql_command --host=$host --port=$port --user=$dbuser --password=$dbpass $db < $global_sql_file`; +} else { + $dbh->disconnect(); } 1; diff --git a/conf/defaults.config b/conf/defaults.config index a442618fcd..bca858df9c 100644 --- a/conf/defaults.config +++ b/conf/defaults.config @@ -572,6 +572,10 @@ $maxCourseIdLength = 40; # # The problemLibrary configuration data should now be set in localOverrides.conf +# 2018 - in order to support multiple installed libraries, there are changes to +# the structure of the configuration settings. +# the "{OPL}" was added to the path to the records for the main OPL settings. + # For configuration instructions, see: # http://webwork.maa.org/wiki/National_Problem_Library # The directory containing the natinal problem library files. @@ -582,8 +586,8 @@ $maxCourseIdLength = 40; #RE-CONFIGURE problemLibrary values in localOverrides.conf ################################################# -$problemLibrary{root} ="/opt/webwork/libraries/webwork-open-problem-library/OpenProblemLibrary"; -$problemLibrary{version} ="2.5"; # 2.0 for NPL, 2.5 for OPL +$problemLibrary{OPL}{root} ="/opt/webwork/libraries/webwork-open-problem-library/OpenProblemLibrary"; +$problemLibrary{OPL}{version} ="2.5"; # 2.0 for NPL, 2.5 for OPL ########################################################### # Problem Library SQL database connection information @@ -594,14 +598,16 @@ $problemLibrary_db = { storage_engine => 'MYISAM', }; -$problemLibrary{tree} = 'library-directory-tree.json'; +$problemLibrary{OPL}{tree} = 'OPL-library-directory-tree.json'; +$problemLibrary{OPL}{taxo} = 'OPL-tagging-taxonomy.json'; + # These flags control if statistics on opl problems are shown in the library # browser. If you want to include local statistics you will need to # run webwork2/bin/update-OPL-statistics on a regular basis. -$problemLibrary{showLibraryLocalStats} = 1; -# This flag controls whether global statistics will0be displayed -$problemLibrary{showLibraryGlobalStats} = 1; +$problemLibrary{OPL}{showLibraryLocalStats} = 1; +# This flag controls whether global statistics will be displayed +$problemLibrary{OPL}{showLibraryGlobalStats} = 1; ################################################################################ # Logs diff --git a/conf/localOverrides.conf.dist b/conf/localOverrides.conf.dist index 16cf72334e..e3db32aa6b 100644 --- a/conf/localOverrides.conf.dist +++ b/conf/localOverrides.conf.dist @@ -121,26 +121,130 @@ $webworkFiles{screenSnippets}{setHeader} = "$webworkDirs{conf}/snippets/ # NationalProblemLibrary -- OpenProblemLibrary ################################################################################ +# 2018 - in order to support multiple installed libraries, there are changes to +# the structure of the configuration settings. +# the "{OPL}" was added to the path to the records for the main OPL settings. + +$problemLibrary{OPL} = { }; # Create an empty hash for the OPL library records # For configuration instructions, see: # http://webwork.maa.org/wiki/National_Problem_Library # The directory containing the natinal problem library files. Set to "" if no problem # library is installed. # NationalProblemLibrary (NPL) has been renamed to OpenProblemLibrary (OPL) -#$problemLibrary{root} = "/opt/webwork/libraries/NationalProblemLibrary"; -#$problemLibrary{version} = "2"; -# uncomment these lines below and comment out the line above if using OPL instead of NPL +#$problemLibrary{OPL}{root} = "/opt/webwork/libraries/NationalProblemLibrary"; +#$problemLibrary{OPL}{version} = "2"; +# uncomment the 2 lines above and comment the 2 below if using NPL instead of OPL + +$problemLibrary{OPL}{root} = "/opt/webwork/libraries/webwork-open-problem-library/OpenProblemLibrary"; +$contribLibrary{OPL}{root} = "/opt/webwork/libraries/webwork-open-problem-library/Contrib"; +$problemLibrary{OPL}{version} = "2.5"; + +# What is the "displayed" name of this library +$problemLibrary{OPL}{name} = "Open Problem Library"; + -$problemLibrary{root} = "/opt/webwork/libraries/webwork-open-problem-library/OpenProblemLibrary"; -$contribLibrary{root} = "/opt/webwork/libraries/webwork-open-problem-library/Contrib"; -$problemLibrary{version} = "2.5"; +# What is the name of the symbolic link from inside course template directories +# to this library. For the main OPL the value "Library" should be used. Some +# PG files which load other files have that value hard-coded in. +$problemLibrary{OPL}{linkname} = "Library"; # Do NOT change this for the OPL + +# Used to get internal short name from the "linkname": +$problemLibrary{LookupTable}{Library} = "OPL"; + +# JSON files for this library +$problemLibrary{OPL}{tree} = 'OPL-library-directory-tree.json'; +$problemLibrary{OPL}{taxo} = 'OPL-tagging-taxonomy.json'; +$problemLibrary{OPL}{subj} = 'OPL-library-subject-tree.json'; +$problemLibrary{OPL}{text} = 'OPL-textbook-tree.json'; # These flags control if statistics on opl problems are shown in the library # browser. If you want to include local statistics you will need to # run webwork2/bin/update-OPL-statistics on a regular basis. -$problemLibrary{showLibraryLocalStats} = 1; +$problemLibrary{OPL}{showLibraryLocalStats} = 1; # This flag controls whether global statistics will be displayed -$problemLibrary{showLibraryGlobalStats} = 1; +$problemLibrary{OPL}{showLibraryGlobalStats} = 1; + +# ====================================================== + +# Sample records for an additional SEARCHABLE library + +#$problemLibrary{TSTL1} = { }; # Empty hash for the TSTL1 library records + +# Where is the library root directory on the server +#$problemLibrary{TSTL1}{root} = "/opt/webwork/libraries/tl-01"; + +# What table version should we use. Should match that of the OTHER libraries +# so we use the value set for the primary libraries +#$problemLibrary{TSTL1}{version} = "$problemLibrary{OPL}{version}"; + +# What is the "displayed" name of this library +#$problemLibrary{TSTL1}{name} = "Test Library 1"; + +# What is the name of the symbolic link from inside course template directories +# to this library. +# Warning: Do NOT use the value "Library" for anything except the main OPL. +# Some PG files in the OPL reference/load other problems under the +# assumption that the OPL is under "Library" so we should NOT put something else there. +#$problemLibrary{TSTL1}{linkname} = "TestLibrary1"; # Do not call this "Library" that should be reserved for the main OPL only, as linked PG problems in the OPL expect that specific value + +# Set the LookupTable values used to get internal short name from the "linkname": +#$problemLibrary{LookupTable}{TestLibrary1} = "TSTL1"; + +# JSON files for this library +#$problemLibrary{TSTL1}{tree} = 'TSTL1-library-directory-tree.json'; +#$problemLibrary{TSTL1}{taxo} = 'TSTL1-tagging-taxonomy.json'; +#$problemLibrary{TSTL1}{subj} = 'TSTL1-library-subject-tree.json'; +#$problemLibrary{TSTL1}{text} = 'TSTL1-textbook-tree.json'; + +# A present it is assumed that additional libraries will NOT have stats supports, +# so we disable the following settings. +#$problemLibrary{TSTL1}{showLibraryLocalStats} = 0; +#$problemLibrary{TSTL1}{showLibraryGlobalStats} = 0; + +# End of sample records for an additional SEARCHABLE library + +# ====================================================== + +# Sample records for an additional SEARCHABLE library + +#$problemLibrary{TSTL2} = { }; # Empty hash for the TSTL1 library records + +# Where is the library root directory on the server +#$problemLibrary{TSTL2}{root} = "/opt/webwork/libraries/tl-02"; + +# What table version should we use. Should match that of the OTHER libraries +# so we use the value set for the primary libraries +#$problemLibrary{TSTL2}{version} = "$problemLibrary{OPL}{version}"; + +# What is the "displayed" name of this library +#$problemLibrary{TSTL2}{name} = "Test Library 2"; + +# What is the name of the symbolic link from inside course template directories +# to this library. +# Warning: Do NOT use the value "Library" for anything except the main OPL. +# Some PG files in the OPL reference/load other problems under the +# assumption that the OPL is under "Library" so we should NOT put something else there. +#$problemLibrary{TSTL2}{linkname} = "TestLibrary2"; # Do not call this "Library" that should be reserved for the main OPL only, as linked PG problems in the OPL expect that specific value + +# Set the LookupTable values used to get internal short name from the "linkname": +#$problemLibrary{LookupTable}{TestLibrary2} = "TSTL2"; + +# JSON files for this library +#$problemLibrary{TSTL2}{tree} = 'TSTL2-library-directory-tree.json'; +#$problemLibrary{TSTL2}{taxo} = 'TSTL2-tagging-taxonomy.json'; +#$problemLibrary{TSTL2}{subj} = 'TSTL2-library-subject-tree.json'; +#$problemLibrary{TSTL2}{text} = 'TSTL2-textbook-tree.json'; + +# A present it is assumed that additional libraries will NOT have stats supports, +# so we disable the following settings. +#$problemLibrary{TSTL2}{showLibraryLocalStats} = 0; +#$problemLibrary{TSTL2}{showLibraryGlobalStats} = 0; + +# End of sample records for an additional SEARCHABLE library + +# ====================================================== + # Additional library buttons can be added to the Library Browser (SetMaker.pm) # by adding the libraries you want to the following line. For each key=>value @@ -322,12 +426,12 @@ $pg{options}{periodicRandomizationPeriod} = 5; ################################################################################ $pg{specialPGEnvironmentVars}{DragMath} = 0; - $pg{specialPGEnvironmentVars}{CAPA_Tools} = "$courseDirs{templates}/Contrib/CAPA/macros/CAPA_Tools/", - $pg{specialPGEnvironmentVars}{CAPA_MCTools} = "$courseDirs{templates}/Contrib/CAPA/macros/CAPA_MCTools/", - $pg{specialPGEnvironmentVars}{CAPA_GraphicsDirectory} = "$courseDirs{templates}/Contrib/CAPA/CAPA_Graphics/", - push @{$pg{directories}{macrosPath}}, - "$courseDirs{templates}/Contrib/CAPA/macros/CAPA_Tools", - "$courseDirs{templates}/Contrib/CAPA/macros/CAPA_MCTools"; + #$pg{specialPGEnvironmentVars}{CAPA_Tools} = "$courseDirs{templates}/Contrib/CAPA/macros/CAPA_Tools/", + #$pg{specialPGEnvironmentVars}{CAPA_MCTools} = "$courseDirs{templates}/Contrib/CAPA/macros/CAPA_MCTools/", + #$pg{specialPGEnvironmentVars}{CAPA_GraphicsDirectory} = "$courseDirs{templates}/Contrib/CAPA/CAPA_Graphics/", + #push @{$pg{directories}{macrosPath}}, + # "$courseDirs{templates}/Contrib/CAPA/macros/CAPA_Tools", + # "$courseDirs{templates}/Contrib/CAPA/macros/CAPA_MCTools"; # The link Contrib in the course templates directory should point to ../webwork-open-problem-library/Contrib diff --git a/conf/site.conf.dist b/conf/site.conf.dist index 2f479c169b..9e0be2c3bb 100644 --- a/conf/site.conf.dist +++ b/conf/site.conf.dist @@ -250,6 +250,9 @@ $mail{tls_allowed} = 0; # # The problemLibrary configuration data should now be set in localOverrides.conf +# 2018 - in order to support multiple installed libraries, there are changes to +# the structure of the configuration settings. + # For configuration instructions, see: # http://webwork.maa.org/wiki/National_Problem_Library # The directory containing the Open Problem Library files. @@ -260,7 +263,7 @@ $mail{tls_allowed} = 0; #CONFIGURE problemLibrary values in this file. These settings override defaults in default.config ################################################# -$problemLibrary{root} ="/opt/webwork/libraries/webwork-open-problem-library/OpenProblemLibrary"; +$problemLibrary{OPL}{root} ="/opt/webwork/libraries/webwork-open-problem-library/OpenProblemLibrary"; ########################################################### ################################################################################ diff --git a/htdocs/js/apps/SetMaker/setmaker.js b/htdocs/js/apps/SetMaker/setmaker.js index 47b78150e6..ecd7c676b0 100644 --- a/htdocs/js/apps/SetMaker/setmaker.js +++ b/htdocs/js/apps/SetMaker/setmaker.js @@ -94,20 +94,24 @@ function init_webservice(command) { } function lib_update(who, what) { - var child = { subjects : 'chapters', chapters : 'sections', sections : 'count'}; + var child = { myLibrary : 'subjects', subjects : 'chapters', chapters : 'sections', sections : 'count'}; nomsg(); var all = 'All ' + capFirstLetter(who); + if(who=='myLibrary') { all = 'All Libraries'; } + var mydefaultRequestObject = init_webservice('searchLib'); if(mydefaultRequestObject == null) { // We failed // console.log("Could not get webservice request object"); return false; } + var myLb = $('[name="library_name"] option:selected').val(); var subj = $('[name="library_subjects"] option:selected').val(); var chap = $('[name="library_chapters"] option:selected').val(); var sect = $('[name="library_sections"] option:selected').val(); + if(myLb == 'All Libraries'){ myLb = '';}; if(subj == 'All Subjects') { subj = '';}; if(chap == 'All Chapters') { chap = '';}; if(sect == 'All Sections') { sect = '';}; @@ -117,12 +121,16 @@ function lib_update(who, what) { if(lib_text == 'All Textbooks') { lib_text = '';}; if(lib_textchap == 'All Chapters') { lib_textchap = '';}; if(lib_textsect == 'All Sections') { lib_textsect = '';}; + + mydefaultRequestObject.library_name = myLb; + mydefaultRequestObject.library_subjects = subj; mydefaultRequestObject.library_chapters = chap; mydefaultRequestObject.library_sections = sect; mydefaultRequestObject.library_textbooks = lib_text; mydefaultRequestObject.library_textchapter = lib_textchap; mydefaultRequestObject.library_textsection = lib_textsect; + if(who == 'count') { mydefaultRequestObject.command = 'countDBListings'; // console.log(mydefaultRequestObject); @@ -131,6 +139,7 @@ function lib_update(who, what) { data: mydefaultRequestObject, timeout: 10000, //milliseconds success: function (data) { + if (data.match(/WeBWorK error/)) { reportWWerror(data); } @@ -139,11 +148,21 @@ function lib_update(who, what) { // console.log(response); var arr = response.result_data; arr = arr[0]; - var line = "There are "+ arr +" matching WeBWorK problems" - if(arr == "1") { - line = "There is 1 matching WeBWorK problem" + + if(myLb == ''){ myLb = 'All Libraries';}; + + + var line = "There are "+ arr +" matching WeBWorK problems in " + myLb + if (arr == "1") { + line = "There is 1 matching WeBWorK problem in " + myLb + } else if (arr == "0") { + line = "There are no matching WeBWorK problem in " + myLb + } $('#library_count_line').html(line); + + if(myLb == 'All Libraries'){ myLb = '';}; + return true; }, error: function (data) { @@ -157,9 +176,18 @@ function lib_update(who, what) { setselect('library_'+who, [all]); return lib_update(child[who], 'clear'); } + if(who=='subjects' && myLb=='') { return lib_update(who, 'clear'); } if(who=='chapters' && subj=='') { return lib_update(who, 'clear'); } if(who=='sections' && chap=='') { return lib_update(who, 'clear'); } + +// if(who=='myLibrary'){ subcommand = "getAllLibraries";} + + if(who=='myLibrary'){ subcommand = "getAllLibraries";} + if(who=='subjects') { subcommand = "getAllDBsubjects";} + if(who=='chapters') { subcommand = "getAllDBchapters";} + if(who=='sections') { subcommand = "getSectionListings";} + mydefaultRequestObject.command = subcommand; // console.log(mydefaultRequestObject); return $.ajax({type:'post', diff --git a/lib/WeBWorK/ContentGenerator/CourseAdmin.pm b/lib/WeBWorK/ContentGenerator/CourseAdmin.pm index 118902dae1..f1f9eba8c4 100644 --- a/lib/WeBWorK/ContentGenerator/CourseAdmin.pm +++ b/lib/WeBWorK/ContentGenerator/CourseAdmin.pm @@ -3384,8 +3384,8 @@ sub upgrade_notification { } } - die "Couldn't find ".$ce->{problemLibrary}{root}.'. Are you sure $problemLibrary{root} is set correctly in localOverrides.conf?' unless - chdir($ce->{problemLibrary}{root}); + die "Couldn't find ".$ce->{problemLibrary}{OPL}{root}.'. Are you sure $problemLibrary{OPL}{root} is set correctly in localOverrides.conf?' unless + chdir($ce->{problemLibrary}{OPL}{root}); if ($LibraryRemote && $LibraryBranch) { # Check if there is an updated version of the OPL available @@ -3421,11 +3421,11 @@ sub upgrade_notification { # Check to see if the OPL_update script has been run more recently # than the last pull of the library. # this json file is (re)-created every time OPL-update is run. - my $jsonfile = $ce->{webworkDirs}{htdocs}.'/DATA/'.$ce->{problemLibrary}{tree}; + my $jsonfile = $ce->{webworkDirs}{htdocs}.'/DATA/'.$ce->{problemLibrary}{OPL}{tree}; # If no json file then the OPL script needs to be run unless (-e $jsonfile) { $upgradesAvailable = 1; - $upgradeMessage .= CGI::Tr(CGI::td($r->maketext('There is no library tree file for the library, you will need to run OPL-update.'))); + $upgradeMessage .= CGI::Tr(CGI::td($r->maketext('There is no library tree file for the OPL library, you will need to run OPL-update (on the main OPL).'))); # otherwise we need to check to see if the date on the tree file # is after the date on the last commit in the library } else { @@ -3435,7 +3435,7 @@ sub upgrade_notification { my $lastcommit = `git log -1 --pretty=format:%at`; if ($lastcommit > $opldate) { $upgradesAvailable = 1; - $upgradeMessage .= CGI::Tr(CGI::td($r->maketext('The library index is older than the library, you need to run OPL-update.'))); + $upgradeMessage .= CGI::Tr(CGI::td($r->maketext('The OPL library index is older than the OPL library, you need to run OPL-update(on the main OPL).'))); } } } diff --git a/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm b/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm index 59bdf70598..3c9b229c94 100644 --- a/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm +++ b/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm @@ -56,13 +56,14 @@ use constant ALL_SECTIONS => 'All Sections'; use constant ALL_TEXTBOOKS => 'All Textbooks'; use constant LIB2_DATA => { - 'dbchapter' => {name => 'library_chapters', all => 'All Chapters'}, + 'dbLibrary' => {name => 'library_name', all => 'All Libraries'}, + 'dbchapter' => {name => 'library_chapters', all => 'All Chapters'}, 'dbsection' => {name => 'library_sections', all =>'All Sections' }, 'dbsubject' => {name => 'library_subjects', all => 'All Subjects' }, - 'textbook' => {name => 'library_textbook', all => 'All Textbooks'}, - 'textchapter' => {name => 'library_textchapter', all => 'All Chapters'}, - 'textsection' => {name => 'library_textsection', all => 'All Sections'}, - 'keywords' => {name => 'library_keywords', all => '' }, + 'textbook' => {name => 'library_textbook', all => 'All Textbooks'}, + 'textchapter'=> {name => 'library_textchapter', all => 'All Chapters'}, + 'textsection'=> {name => 'library_textsection', all => 'All Sections'}, + 'keywords' => {name => 'library_keywords', all => '' }, }; ## Flags for operations on files @@ -122,6 +123,9 @@ sub get_library_sets { my @pgdirs; my @dirs = grep {!$ignoredir{$_} and -d "$dir/$_"} @lis; if ($top == 1) {@dirs = grep {!$problib{$_}} @dirs} + + # FIXME - "Library" is hardwired + # Never include Library at the top level if ($top == 1) {@dirs = grep {$_ ne 'Library'} @dirs} foreach my $subdir (@dirs) { @@ -205,6 +209,8 @@ sub getDBextras { my $r = shift; my $sourceFileName = shift; + # FIXME - "Library" is hardwired + if($sourceFileName =~ /^Library/) { return @{WeBWorK::Utils::ListingDB::getDBextras($r, $sourceFileName)}; } @@ -472,21 +478,23 @@ sub browse_library_panel { my $r = $self->r; my $ce = $r->ce; - # See if the problem library is installed - my $libraryRoot = $r->{ce}->{problemLibrary}->{root}; + # See if the problem library is installed - we check for OPL + my $libraryRoot = $r->{ce}->{problemLibrary}->{OPL}->{root}; + + my $linkName = $r->{ce}->{problemLibrary}->{OPL}->{linkname}; unless($libraryRoot) { print CGI::Tr(CGI::td(CGI::div({class=>'ResultsWithError', align=>"center"}, - "The problem library has not been installed."))); + "The OPL problem library has not been installed."))); return; } # Test if the Library directory link exists. If not, try to make it - unless(-d "$ce->{courseDirs}->{templates}/Library") { - unless(symlink($libraryRoot, "$ce->{courseDirs}->{templates}/Library")) { + unless(-d "$ce->{courseDirs}->{templates}/${linkName}") { + unless(symlink($libraryRoot, "$ce->{courseDirs}->{templates}/${linkName}")) { my $msg = <<"HERE"; -You are missing the directory templates/Library, which is needed +You are missing the directory templates/${linkName}, which is needed for the Problem Library to function. It should be a link pointing to -$libraryRoot, which you set in conf/site.conf. +$libraryRoot, which you set in conf/localOverrides.conf. I tried to make the link for you, but that failed. Check the permissions in your templates directory. HERE @@ -495,7 +503,7 @@ HERE } # Now check what version we are supposed to use - my $libraryVersion = $r->{ce}->{problemLibrary}->{version} || 1; + my $libraryVersion = $r->{ce}->{problemLibrary}->{OPL}->{version} || 1; if($libraryVersion == 1) { return $self->browse_library_panel1; } elsif($libraryVersion >= 2) { @@ -555,6 +563,11 @@ sub browse_library_panel2 { my $r = $self->r; my $ce = $r->ce; + my @libs = WeBWorK::Utils::ListingDB::getAllLibraries($r); + unshift @libs, LIB2_DATA->{dbLibrary}{all}; + + my %libNames = WeBWorK::Utils::ListingDB::getAllLibrariesNameHash($r); + my @subjs = WeBWorK::Utils::ListingDB::getAllDBsubjects($r); unshift @subjs, LIB2_DATA->{dbsubject}{all}; @@ -565,23 +578,58 @@ sub browse_library_panel2 { @sects = WeBWorK::Utils::ListingDB::getAllDBsections($r); unshift @sects, LIB2_DATA->{dbsection}{all}; + my $libName_selected = $r->param('library_name') || LIB2_DATA->{dbLibrary}{all}; my $subject_selected = $r->param('library_subjects') || LIB2_DATA->{dbsubject}{all}; my $chapter_selected = $r->param('library_chapters') || LIB2_DATA->{dbchapter}{all}; my $section_selected = $r->param('library_sections') || LIB2_DATA->{dbsection}{all}; my $view_problem_line = view_problems_line('lib_view', $r->maketext('View Problems'), $self->r); - my $count_line = WeBWorK::Utils::ListingDB::countDBListings($r); + my $count_line = 0; + if ( $libName_selected eq LIB2_DATA->{dbLibrary}{all} ) { + # LOOP over all libraries + my $ll; + my $cc; + foreach $ll ( keys( %{$ce->{problemLibrary}} ) ) { + # skip "LookupTable" + next if ( ($ll eq "LookupTable") || ( $ll eq "" ) ) ; + $r->param( 'library_name' => "$ce->{problemLibrary}->{$ll}->{linkname}" ); + + # FIXME should check here that this library is installed for this course + + $count_line += WeBWorK::Utils::ListingDB::countDBListings($r); + $r->param( 'library_name' => LIB2_DATA->{dbLibrary}{all} ); + } + } else { + $count_line = WeBWorK::Utils::ListingDB::countDBListings($r); + } if($count_line==0) { - $count_line = $r->maketext("There are no matching WeBWorK problems"); + $count_line = $r->maketext("There are no matching WeBWorK problems in [_1]", $libName_selected); + } elsif ( $count_line == 1 ) { + $count_line = $r->maketext("There is 1 matching WeBWorK problems in [_1]", $libName_selected); } else { - $count_line = $r->maketext("There are [_1] matching WeBWorK problems", $count_line); + $count_line = $r->maketext("There are [_1] matching WeBWorK problems in [_2]", $count_line, $libName_selected); } print CGI::Tr({}, CGI::td({-class=>"InfoPanel", -align=>"left"}, CGI::hidden(-name=>"library_is_basic", -default=>1,-override=>1), CGI::start_table({-width=>"100%"}), + + CGI::Tr({}, + CGI::td([$r->maketext("Library:"), + CGI::popup_menu(-name=> 'library_name', + -values=>\@libs, + -labels=>\%libNames, + -default=> $libName_selected, + -onchange=>"lib_update('count', 'clear');return true" + )]), + #CGI::td({-colspan=>2, -align=>"left"}, + # CGI::button(-name=>"change_library", -value=>$r->maketext("Change library"), # Not needed + # -onclick=>"lib_update('subjects', 'get');return true" + # )) + ), + CGI::Tr({}, CGI::td([$r->maketext("Subject:"), CGI::popup_menu(-name=> 'library_subjects', @@ -609,6 +657,10 @@ sub browse_library_panel2 { -default=> $section_selected, -onchange=>"lib_update('count', 'clear');return true" )]), + #CGI::td({-colspan=>2, -align=>"left"}, + # CGI::button(-name=>"rerun_search", -value=>$r->maketext("Run search again"), # Not needed + # -onclick=>"lib_update('count', 'clear');return true" + # )) ), CGI::Tr(CGI::td({-colspan=>3}, $view_problem_line)), CGI::Tr(CGI::td({-colspan=>3, -align=>"center", -id=>"library_count_line"}, $count_line)), @@ -623,6 +675,14 @@ sub browse_library_panel2adv { my $ce = $r->ce; my $right_button_style = "width: 18ex"; + my @libs = WeBWorK::Utils::ListingDB::getAllLibraries($r); + if(! grep { $_ eq $r->param('library_name') } @libs) { + $r->param('library_name', 'Library'); # Fall back value FIXME + } + unshift @libs, LIB2_DATA->{dbLibrary}{all}; + + my %libNames = WeBWorK::Utils::ListingDB::getAllLibrariesNameHash($r); + my @subjs = WeBWorK::Utils::ListingDB::getAllDBsubjects($r); if(! grep { $_ eq $r->param('library_subjects') } @subjs) { $r->param('library_subjects', ''); @@ -668,7 +728,7 @@ sub browse_library_panel2adv { unshift @textsecs, LIB2_DATA->{textsection}{all}; my %selected = (); - for my $j (qw( dbsection dbchapter dbsubject textbook textchapter textsection )) { + for my $j (qw( dbLibrary dbsection dbchapter dbsubject textbook textchapter textsection )) { $selected{$j} = $r->param(LIB2_DATA->{$j}{name}) || LIB2_DATA->{$j}{all}; } @@ -683,11 +743,31 @@ sub browse_library_panel2adv { my $view_problem_line = view_problems_line('lib_view', $r->maketext('View Problems'), $self->r); - my $count_line = WeBWorK::Utils::ListingDB::countDBListings($r); + my $libName_selected = $r->param('library_name') || LIB2_DATA->{dbLibrary}{all}; + + my $count_line = 0; + if ( $libName_selected eq LIB2_DATA->{dbLibrary}{all} ) { + # LOOP over all libraries + my $ll; + foreach $ll ( keys( %{$ce->{problemLibrary}} ) ) { + # skip "LookupTable" + next if ( ($ll eq "LookupTable") || ( $ll eq "" ) ) ; + $r->param( 'library_name' => "$ce->{problemLibrary}->{$ll}->{linkname}" ); + + # FIXME should check here that this library is installed for this course + + $count_line += WeBWorK::Utils::ListingDB::countDBListings($r); + $r->param( 'library_name' => LIB2_DATA->{dbLibrary}{all} ); + } + } else { + $count_line = WeBWorK::Utils::ListingDB::countDBListings($r); + } if($count_line==0) { - $count_line = "There are no matching WeBWorK problems"; + $count_line = $r->maketext("There are no matching WeBWorK problems in [_1]", $libName_selected); + } elsif ( $count_line == 1 ) { + $count_line = $r->maketext("There is 1 matching WeBWorK problems in [_1]", $libName_selected); } else { - $count_line = "There are $count_line matching WeBWorK problems"; + $count_line = $r->maketext("There are [_1] matching WeBWorK problems in [_2]", $count_line, $libName_selected); } # Formatting level checkboxes by hand @@ -713,6 +793,21 @@ sub browse_library_panel2adv { CGI::start_table({-width=>"100%"}), # Html done by hand since it is temporary CGI::Tr(CGI::td({-colspan=>4, -align=>"center"}, $r->maketext('All Selected Constraints Joined by "And"'))), + + CGI::Tr({}, + CGI::td([$r->maketext("Library:"), + CGI::popup_menu(-name=> 'library_name', + -values=>\@libs, + -labels=>\%libNames, + -default=> $selected{dbLibrary}, + -onchange=>"lib_update('count', 'clear');return true" + )]), + #CGI::td({-colspan=>2, -align=>"left"}, + # CGI::button(-name=>"change_library", -value=>$r->maketext("Change library"), # Not needed + # -onclick=>"lib_update('subjects', 'get');return true" + # )) + ), + CGI::Tr({}, CGI::td([$r->maketext("Subject:"), CGI::popup_menu(-name=> 'library_subjects', @@ -755,6 +850,10 @@ sub browse_library_panel2adv { -default=> $selected{textchapter}, -onchange=>"submit();return true" )]), + #CGI::td({-colspan=>2, -align=>"left"}, + # CGI::button(-name=>"rerun_search", -value=>$r->maketext("Run search again"), # Not needed + # -onclick=>"lib_update('count', 'clear');return true" + # )) ), CGI::Tr({}, CGI::td([$r->maketext("Text section:"), @@ -883,7 +982,7 @@ sub make_top_row { print CGI::Tr(CGI::td({-class=>"InfoPanel", -align=>"center"}, $r->maketext("Browse").' ', - CGI::submit(-name=>"browse_npl_library", -value=>$r->maketext("Open Problem Library"), -style=>$these_widths, @dis1), + CGI::submit(-name=>"browse_npl_library", -value=>$r->maketext("Searchable Libraries"), -style=>$these_widths, @dis1), CGI::submit(-name=>"browse_local", -value=>$r->maketext("Local Problems"), -style=>$these_widths, @dis2), CGI::submit(-name=>"browse_mysets", -value=>$r->maketext("From This Course"), -style=>$these_widths, @dis3), CGI::submit(-name=>"browse_setdefs", -value=>$r->maketext("Set Definition Files"), -style=>$these_widths, @dis4), @@ -959,8 +1058,10 @@ sub make_data_row { my $self = shift; my $r = $self->r; my $ce = $r->{ce}; + my $sourceFileData = shift; my $sourceFileName = $sourceFileData->{filepath}; + my $libCode = $sourceFileData->{libCode}; my $pg = shift; my $isstatic = $sourceFileData->{static}; my $isMO = $sourceFileData->{MO}; @@ -1069,7 +1170,9 @@ sub make_data_row { # get statistics to display my $global_problem_stats = ''; - if ($ce->{problemLibrary}{showLibraryGlobalStats}) { + + # use $libCode below + if ($ce->{problemLibrary}{$libCode}{showLibraryGlobalStats}) { my $stats = $self->{library_stats_handler}->getGlobalStats($sourceFileName); if ($stats->{students_attempted}) { $global_problem_stats = $self->helpMacro("Global_Usage_Data",$r->maketext('GLOBAL Usage')).': '. @@ -1083,7 +1186,9 @@ sub make_data_row { my $local_problem_stats = ''; - if ($ce->{problemLibrary}{showLibraryLocalStats}) { + + # use $libCode below + if ($ce->{problemLibrary}{$libCode}{showLibraryLocalStats}) { my $stats = $self->{library_stats_handler}->getLocalStats($sourceFileName); if ($stats->{students_attempted}) { $local_problem_stats = $self->helpMacro("Local_Usage_Data",$r->maketext('LOCAL Usage')).': '. @@ -1147,8 +1252,21 @@ sub process_search { # Build a hash of MLT entries keyed by morelt_id my %mlt = (); my $mltind; + + # Find library (directory) name and then lookup the code name + my $reqLib = $r->param('library_name'); + my $libCode; + my $libSymLink; + if ( exists( $r->ce->{problemLibrary}->{LookupTable}->{$reqLib} ) ) { + $libCode = $r->ce->{problemLibrary}->{LookupTable}->{$reqLib}; + $libSymLink = $r->ce->{problemLibrary}->{$libCode}->{linkname}; + } else { + # Fall back to old default + $libSymLink = "Library"; + } + for my $indx (0..$#dbsearch) { - $dbsearch[$indx]->{filepath} = "Library/".$dbsearch[$indx]->{path}."/".$dbsearch[$indx]->{filename}; + $dbsearch[$indx]->{filepath} = "${libSymLink}/".$dbsearch[$indx]->{path}."/".$dbsearch[$indx]->{filename}; # For debugging $dbsearch[$indx]->{oindex} = $indx; if($mltind = $dbsearch[$indx]->{morelt}) { @@ -1409,8 +1527,39 @@ sub pre_header_initialize { } elsif ($r->param('lib_view')) { @pg_files=(); - my @dbsearch = WeBWorK::Utils::ListingDB::getSectionListings($r); - @pg_files = process_search($r, @dbsearch); + my @dbsearch; + + # When "All Libraries" is selected, the value of $r->param('library_name') + # is an empty string... + + if ( ( $r->param('library_name') eq "" ) || + ( $r->param('library_name') eq LIB2_DATA->{dbLibrary}{all} ) ) { + # search all libraries + my @libs = WeBWorK::Utils::ListingDB::getAllLibraries($r); + + # FIXME should check here which libraries are installed for this course + + my $cl; + my @tmp1; + my $tmpRec; + + foreach $cl ( @libs ) { + $r->param( 'library_name' => "$cl" ); + + @dbsearch = WeBWorK::Utils::ListingDB::getSectionListings($r); + @tmp1 = process_search($r, @dbsearch); + #foreach $tmpRec ( @tmp1 ) { + # push( @pg_files, $tmpRec ); + #} + push( @pg_files, @tmp1 ); + + } + $r->param( 'library_name' => LIB2_DATA->{dbLibrary}{all} ); # Set it back + } else { + # regular, single library search + @dbsearch = WeBWorK::Utils::ListingDB::getSectionListings($r); + @pg_files = process_search($r, @dbsearch); + } $use_previous_problems=0; ##### View a set from a set*.def @@ -1585,8 +1734,9 @@ sub pre_header_initialize { my $library_stats_handler = ''; - if ($ce->{problemLibrary}{showLibraryGlobalStats} || - $ce->{problemLibrary}{showLibraryLocalStats} ) { +# FIXME NSW - need correct library code + if ($ce->{problemLibrary}{OPL}{showLibraryGlobalStats} || + $ce->{problemLibrary}{OPL}{showLibraryLocalStats} ) { $library_stats_handler = WeBWorK::Utils::LibraryStats->new($ce); } @@ -1760,14 +1910,20 @@ sub output_JS { if ($self->r->authz->hasPermissions(scalar($self->r->param('user')), "modify_tags")) { my $site_url = $ce->{webworkURLs}->{htdocs}; print qq!!; - if (open(TAXONOMY, $ce->{webworkDirs}{root}.'/htdocs/DATA/tagging-taxonomy.json') ) { + + + # Determine the proper taxonomy file to use + my $tagging_from = "OPL"; # FIXME, should come from settings + my $taxofile = $ce->{problemLibrary}->{$tagging_from}->{taxo}; + + if (open(TAXONOMY, $ce->{webworkDirs}{root}.'/htdocs/DATA/${taxofile}') ) { my $taxo = '[]'; $taxo = join("", ); close TAXONOMY; print qq!\n!; } else { print qq!\n!; - print qq!\n!; + print qq!\n!; } } return ''; diff --git a/lib/WeBWorK/ContentGenerator/Instructor/SetMaker2.pm b/lib/WeBWorK/ContentGenerator/Instructor/SetMaker2.pm index dbe7169fdc..92a5e8c755 100644 --- a/lib/WeBWorK/ContentGenerator/Instructor/SetMaker2.pm +++ b/lib/WeBWorK/ContentGenerator/Instructor/SetMaker2.pm @@ -539,11 +539,12 @@ sub browse_library_panel { my $ce = $r->ce; # See if the problem library is installed - my $libraryRoot = $r->{ce}->{problemLibrary}->{root}; + # FIXME NSW + my $libraryRoot = $r->{ce}->{problemLibrary}->{OPL}->{root}; unless($libraryRoot) { print CGI::div({class=>'ResultsWithError', align=>"center"}, - "The problem library has not been installed."); + "The OPL problem library has not been installed."); return; } # Test if the Library directory link exists. If not, try to make it @@ -551,8 +552,8 @@ sub browse_library_panel { unless(symlink($libraryRoot, "$ce->{courseDirs}->{templates}/Library")) { my $msg = <<"HERE"; You are missing the directory templates/Library, which is needed -for the Problem Library to function. It should be a link pointing to -$libraryRoot, which you set in conf/site.conf. +for the Open Problem Library to function. It should be a link pointing to +$libraryRoot, which you set in conf/localOverrides.conf. I tried to make the link for you, but that failed. Check the permissions in your templates directory. HERE @@ -561,7 +562,7 @@ HERE } # Now check what version we are supposed to use - my $libraryVersion = $r->{ce}->{problemLibrary}->{version} || 1; + my $libraryVersion = $r->{ce}->{problemLibrary}->{OPL}->{version} || 1; if($libraryVersion == 1) { return $self->browse_library_panel1; } elsif($libraryVersion >= 2) { diff --git a/lib/WeBWorK/ContentGenerator/Instructor/SetMakernojs.pm b/lib/WeBWorK/ContentGenerator/Instructor/SetMakernojs.pm index 6aef538306..692db0f57c 100644 --- a/lib/WeBWorK/ContentGenerator/Instructor/SetMakernojs.pm +++ b/lib/WeBWorK/ContentGenerator/Instructor/SetMakernojs.pm @@ -32,34 +32,36 @@ use warnings; use WeBWorK::CGI; use WeBWorK::Debug; use WeBWorK::Form; -use WeBWorK::Utils qw(readDirectory max sortByName); +use WeBWorK::Utils qw(readDirectory max sortByName wwRound x); use WeBWorK::Utils::Tasks qw(renderProblems); use File::Find; require WeBWorK::Utils::ListingDB; +# we use x to mark strings for maketext use constant SHOW_HINTS_DEFAULT => 0; use constant SHOW_SOLUTIONS_DEFAULT => 0; use constant MAX_SHOW_DEFAULT => 20; -use constant NO_LOCAL_SET_STRING => 'No sets in this course yet'; -use constant SELECT_SET_STRING => 'Select a Set from this Course'; -use constant SELECT_LOCAL_STRING => 'Select a Problem Collection'; -use constant MY_PROBLEMS => ' My Problems '; -use constant MAIN_PROBLEMS => ' Unclassified Problems '; -use constant CREATE_SET_BUTTON => 'Create New Set'; +use constant NO_LOCAL_SET_STRING => x('No sets in this course yet'); +use constant SELECT_SET_STRING => x('Select a Set from this Course'); +use constant SELECT_LOCAL_STRING => x('Select a Problem Collection'); +use constant MY_PROBLEMS => x('My Problems'); +use constant MAIN_PROBLEMS => x('Unclassified Problems'); +use constant CREATE_SET_BUTTON => x('Create New Set'); use constant ALL_CHAPTERS => 'All Chapters'; use constant ALL_SUBJECTS => 'All Subjects'; use constant ALL_SECTIONS => 'All Sections'; use constant ALL_TEXTBOOKS => 'All Textbooks'; use constant LIB2_DATA => { - 'dbchapter' => {name => 'library_chapters', all => 'All Chapters'}, + 'dbLibrary' => {name => 'library_name', all => 'All Libraries'}, + 'dbchapter' => {name => 'library_chapters', all => 'All Chapters'}, 'dbsection' => {name => 'library_sections', all =>'All Sections' }, 'dbsubject' => {name => 'library_subjects', all => 'All Subjects' }, - 'textbook' => {name => 'library_textbook', all => 'All Textbooks'}, - 'textchapter' => {name => 'library_textchapter', all => 'All Chapters'}, - 'textsection' => {name => 'library_textsection', all => 'All Sections'}, - 'keywords' => {name => 'library_keywords', all => '' }, + 'textbook' => {name => 'library_textbook', all => 'All Textbooks'}, + 'textchapter'=> {name => 'library_textchapter', all => 'All Chapters'}, + 'textsection'=> {name => 'library_textsection', all => 'All Sections'}, + 'keywords' => {name => 'library_keywords', all => '' }, }; ## Flags for operations on files @@ -407,11 +409,11 @@ sub browse_library_panel { my $ce = $r->ce; # See if the problem library is installed - my $libraryRoot = $r->{ce}->{problemLibrary}->{root}; + my $libraryRoot = $r->{ce}->{problemLibrary}->{OPL}->{root}; unless($libraryRoot) { print CGI::Tr(CGI::td(CGI::div({class=>'ResultsWithError', align=>"center"}, - "The problem library has not been installed."))); + "The OPL problem library has not been installed."))); return; } # Test if the Library directory link exists. If not, try to make it @@ -419,8 +421,8 @@ sub browse_library_panel { unless(symlink($libraryRoot, "$ce->{courseDirs}->{templates}/Library")) { my $msg = <<"HERE"; You are missing the directory templates/Library, which is needed -for the Problem Library to function. It should be a link pointing to -$libraryRoot, which you set in conf/site.conf. +for the Open Problem Library to function. It should be a link pointing to +$libraryRoot, which you set in conf/localOverrides.conf. I tried to make the link for you, but that failed. Check the permissions in your templates directory. HERE @@ -429,7 +431,7 @@ HERE } # Now check what version we are supposed to use - my $libraryVersion = $r->{ce}->{problemLibrary}->{version} || 1; + my $libraryVersion = $r->{ce}->{problemLibrary}->{OPL}->{version} || 1; if($libraryVersion == 1) { return $self->browse_library_panel1; } elsif($libraryVersion >= 2) { diff --git a/lib/WeBWorK/ContentGenerator/Problem.pm b/lib/WeBWorK/ContentGenerator/Problem.pm index 5b90b14a62..c94ac53842 100644 --- a/lib/WeBWorK/ContentGenerator/Problem.pm +++ b/lib/WeBWorK/ContentGenerator/Problem.pm @@ -2183,14 +2183,18 @@ sub output_JS{ # This is for tagging menus (if allowed) if ($r->authz->hasPermissions($r->param('user'), "modify_tags")) { - if (open(TAXONOMY, $ce->{webworkDirs}{root}.'/htdocs/DATA/tagging-taxonomy.json') ) { + # Determine the proper taxonomy file to use + my $tagging_from = "OPL"; # FIXME, should come from settings + my $taxofile = $ce->{problemLibrary}->{$tagging_from}->{taxo}; + + if (open(TAXONOMY, $ce->{webworkDirs}{root}.'/htdocs/DATA/${taxofile}') ) { my $taxo = '[]'; $taxo = join("", ); close TAXONOMY; print qq!\n!; } else { print qq!\n!; - print qq!\n!; + print qq!\n!; } print CGI::start_script({type=>"text/javascript", src=>"$site_url/js/apps/TagWidget/tagwidget.js"}), CGI::end_script(); } diff --git a/lib/WeBWorK/Utils/LibraryStats.pm b/lib/WeBWorK/Utils/LibraryStats.pm index 7ef55e239a..db75fd0ca6 100644 --- a/lib/WeBWorK/Utils/LibraryStats.pm +++ b/lib/WeBWorK/Utils/LibraryStats.pm @@ -66,7 +66,12 @@ sub getLocalStats { unless ($selectstm->execute($source_file)) { if ($selectstm->errstr =~ /Table .* doesn't exist/) { - warn "Couldn't find the OPL local statistics table. Did you download the latest OPL and run update-OPL-statistics?" + warn "Couldn't find the OPL local statistics table. Did you download the latest OPL and run update-OPL-statistics?"; + + # NSW hack - to avoid crash when no statistics + return {source_file => $source_file}; + # END NSW hack + } die $selectstm->errstr; } @@ -92,7 +97,12 @@ sub getGlobalStats { unless ($selectstm->execute($source_file)) { if ($selectstm->errstr =~ /Table .* doesn't exist/) { - warn "Couldn't find the OPL global statistics table. Did you download the latest OPL and run update-OPL-statistics?" + warn "Couldn't find the OPL global statistics table. Did you download the latest OPL and run update-OPL-statistics?"; + + # NSW hack - to avoid crash when no statistics + return {source_file => $source_file}; + # END NSW hack + } die $selectstm->errstr; } diff --git a/lib/WeBWorK/Utils/ListingDB.pm b/lib/WeBWorK/Utils/ListingDB.pm index 987c1e9d9b..a629297e2e 100644 --- a/lib/WeBWorK/Utils/ListingDB.pm +++ b/lib/WeBWorK/Utils/ListingDB.pm @@ -44,7 +44,8 @@ BEGIN &createListing &updateListing &deleteListing &getAllChapters &getAllSections &searchListings &getAllListings &getSectionListings &getAllDBsubjects &getAllDBchapters &getAllDBsections &getDBTextbooks - &getDBListings &countDBListings &getTables &getDBextras + &getDBListings &countDBListings &getTables &getDBextras &getAllLibraries + &getAllLibrariesNameHash ); %EXPORT_TAGS =(); @EXPORT_OK =qw(); @@ -89,14 +90,15 @@ my %NPLtables = ( sub getTables { my $ce = shift; - my $libraryRoot = $ce->{problemLibrary}->{root}; + my $myLib = shift; # Library code name (as used in top level of %problemLibrary) + my %tables; - if($ce->{problemLibrary}->{version} == 2.5) { - %tables = %OPLtables; - } else { + if( $ce->{problemLibrary}->{$myLib}->{version} eq "2" ) { %tables = %NPLtables; - } + } else { + %tables = %OPLtables; + } return %tables; } @@ -200,12 +202,26 @@ Out put is an array reference: [MO, static] sub getDBextras { my $r = shift; + + # Find library (directory) name and then lookup the code name + my $reqLib = $r->param('library_name'); + my $libCode = $r->{ce}->{problemLibrary}->{LookupTable}->{$reqLib}; + my $path = shift; - my %tables = getTables($r->ce); + + # Now depends on the $libCode + my %tables = getTables($r->ce, $libCode); + my $dbh = getDB($r->ce); my ($mo, $static)=(0,0); - $path =~ s|^Library/||; + # The old code assumed that the path header is "Library/" which + # is correct for the main OPL, but not for other libraries. + # OLD: + # $path =~ s|^Library/||; + # NEW: + $path =~ s|^${reqLib}/||; + my $filename = basename $path; $path = dirname $path; my $query = "SELECT pgfile.MO, pgfile.static FROM `$tables{pgfile}` pgfile, `$tables{path}` p WHERE p.path=\"$path\" AND pgfile.path_id=p.path_id AND pgfile.filename=\"$filename\""; @@ -233,15 +249,23 @@ consistent with the DB subject, chapter, section selected. sub getDBTextbooks { my $r = shift; + + # Find library (directory) name and then lookup the code name + my $reqLib = $r->param('library_name'); + my $libCode = $r->{ce}->{problemLibrary}->{LookupTable}->{$reqLib}; + my $thing = shift || 'textbook'; my $dbh = getDB($r->ce); - my %tables = getTables($r->ce); + + # Now depends on the $libCode + my %tables = getTables($r->ce, $libCode); + my $extrawhere = ''; # Handle DB* restrictions my @search_params=(); my $subj = $r->param('library_subjects') || ""; my $chap = $r->param('library_chapters') || ""; - my $sec = $r->param('library_sections') || ""; + my $sec = $r->param('library_sections') || ""; if($subj) { $subj =~ s/'/\\'/g; $extrawhere .= " AND t.name = ?\n"; @@ -327,6 +351,58 @@ sub getDBTextbooks { } } +=item getAllLibraries($r) +Returns an array of Library names + +$r is the Apache request object + +=cut + +sub getAllLibraries { + my $r = shift; + + my $libLookupTable = $r->{ce}->{problemLibrary}->{LookupTable}; + + # The keys are the names used for the symbolic links to the library + # the value for a key is the + + my @results = keys( %$libLookupTable ); + + # Fixme - reorder ??? + # Fixme - want a "human" name rather than symbolic link name + + return @results; +} + +=item getAllLibrariesNameHash($r) +Returns an hash of Library code names to long names + +$r is the Apache request object + +=cut + +sub getAllLibrariesNameHash { + my $r = shift; + + my $libLookupTable = $r->{ce}->{problemLibrary}->{LookupTable}; + + # The keys are the names used for the symbolic links to the library + # the value for a key is the + + my %result; + + my $k; + my $code; + foreach $k ( keys( %$libLookupTable ) ) { + $code = $r->{ce}->{problemLibrary}->{LookupTable}->{$k}; + $result{$k} = $r->{ce}->{problemLibrary}->{$code}->{name}; + } + + $result{"All Libraries"} = "All Libraries"; + + return %result; +} + =item getAllDBsubjects($r) Returns an array of DBsubject names @@ -336,7 +412,14 @@ $r is the Apache request object sub getAllDBsubjects { my $r = shift; - my %tables = getTables($r->ce); + + # Find library (directory) name and then lookup the code name + my $reqLib = $r->param('library_name'); + my $libCode = $r->{ce}->{problemLibrary}->{LookupTable}->{$reqLib}; + + # Now depends on the $libCode + my %tables = getTables($r->ce, $libCode); + my @results=(); my @row; my $query = "SELECT DISTINCT name, DBsubject_id FROM `$tables{dbsubject}` ORDER BY DBsubject_id"; @@ -361,7 +444,14 @@ $r is the Apache request object sub getAllDBchapters { my $r = shift; - my %tables = getTables($r->ce); + + # Find library (directory) name and then lookup the code name + my $reqLib = $r->param('library_name'); + my $libCode = $r->{ce}->{problemLibrary}->{LookupTable}->{$reqLib}; + + # Now depends on the $libCode + my %tables = getTables($r->ce, $libCode); + my $subject = $r->param('library_subjects'); return () unless($subject); my $dbh = getDB($r->ce); @@ -392,7 +482,14 @@ $r is the Apache request object sub getAllDBsections { my $r = shift; - my %tables = getTables($r->ce); + + # Find library (directory) name and then lookup the code name + my $reqLib = $r->param('library_name'); + my $libCode = $r->{ce}->{problemLibrary}->{LookupTable}->{$reqLib}; + + # Now depends on the $libCode + my %tables = getTables($r->ce, $libCode); + my $subject = $r->param('library_subjects'); return () unless($subject); my $chapter = $r->param('library_chapters'); @@ -433,9 +530,48 @@ Here, we search on all known fields out of r sub getDBListings { my $r = shift; + + # Find library (directory) name and then lookup the code name + my $reqLib = $r->param('library_name'); + if ( $reqLib eq "" ) { + # When called via instructorXMLHandler from JavaScript + # there can be a difference between the "param" value and the "hash" value. + # and we want the non-empty value + $reqLib = $r->{library_name}; + } + + # Need to handle case of "All Libraries" which should not get passed + # into this function, but would cause an error below. + if ( $reqLib eq "All Libraries" ) { + my $tmp1amcounter = shift; # 0-1 if I am a counter. + if ( $tmp1amcounter ) { + # If we got here - report 100000 results - to be seen as an unreasonable result + return( 100000 ); + } else { + # If we got here - report empty array of results + my @tmp1 = (); # no results + return @tmp1 ; + } + } + if ( $reqLib eq "" ) { + my $tmp1amcounter = shift; # 0-1 if I am a counter. + if ( $tmp1amcounter ) { + # If we got here - report 1000000 results - to be seen as an unreasonable result + return( 1000000 ); + } else { + # If we got here - report empty array of results + my @tmp1 = (); # no results + return @tmp1 ; + } + } + + my $libCode = $r->{ce}->{problemLibrary}->{LookupTable}->{$reqLib}; + + # Now depends on the $libCode + my %tables = getTables($r->ce, $libCode); + my $amcounter = shift; # 0-1 if I am a counter. my $ce = $r->ce; - my %tables = getTables($ce); my $subj = $r->param('library_subjects') || ""; my $chap = $r->param('library_chapters') || ""; my $sec = $r->param('library_sections') || ""; @@ -562,7 +698,7 @@ sub getDBListings { WHERE p.path_id = pgf.path_id AND pgf.pgfile_id= ? "; my $row = $dbh->selectrow_arrayref($query,{},$pgid); - push @results, {'path' => $row->[0], 'filename' => $row->[1], 'morelt' => $row->[2], 'pgid'=> $row->[3], 'static' => $row->[4], 'MO' => $row->[5] }; + push @results, {'path' => $row->[0], 'filename' => $row->[1], 'morelt' => $row->[2], 'pgid'=> $row->[3], 'static' => $row->[4], 'MO' => $row->[5], 'libCode' => "$libCode" }; } return @results; @@ -576,7 +712,14 @@ sub countDBListings { sub getMLTleader { my $r = shift; my $mltid = shift; - my %tables = getTables($r->ce); + + # Find library (directory) name and then lookup the code name + my $reqLib = $r->param('library_name'); + my $libCode = $r->{ce}->{problemLibrary}->{LookupTable}->{$reqLib}; + + # Now depends on the $libCode + my %tables = getTables($r->ce, $libCode); + my $dbh = getDB($r->ce); my $query = "SELECT leader FROM `$tables{morelt}` WHERE morelt_id=\"$mltid\""; my $row = $dbh->selectrow_arrayref($query); @@ -787,8 +930,17 @@ sub getMLTleader { sub getSectionListings { #print STDERR "ListingDB::getSectionListings(chapter,section)\n"; my $r = shift; + + # Find library (directory) name and then lookup the code name + my $reqLib = $r->param('library_name'); + my $libCode = $r->{ce}->{problemLibrary}->{LookupTable}->{$reqLib}; + + # Now depends on the $libCode + my %tables = getTables($r->ce, $libCode); + + my $ce = $r->ce; - my $version = $ce->{problemLibrary}->{version} || 1; + my $version = $ce->{problemLibrary}->{$libCode}->{version} || 1; if($version => 2) { return(getDBListings($r, 0))} my $subj = $r->param('library_subjects') || ""; my $chap = $r->param('library_chapters') || ""; @@ -818,7 +970,6 @@ sub getSectionListings { FROM classify c, pgfiles p WHERE ? ? c.pgfiles_id = p.pgfiles_id"; my $dbh = getDB($ce); - my %tables = getTables($ce); my $sth = $dbh->prepare($query); $sth->execute($chapstring,$secstring); @@ -846,13 +997,25 @@ sub getSectionListings { # 1 = all ok # # not implemented yet +# currently hacked up to force to "OPL" as it does not get the request object sub deleteListing { my $ce = shift; my $listing_id = shift; #print STDERR "ListingDB::deleteListing(): listing == '$listing_id'\n"; my $dbh = getDB($ce); - my %tables = getTables($ce); + +# FIXME +# # Find library (directory) name and then lookup the code name +# my $reqLib = $r->param('library_name'); +# my $libCode = $r->{ce}->{problemLibrary}->{LookupTable}->{$reqLib}; +# +# # Now depends on the $libCode +# my %tables = getTables($r->ce, $libCode); + +# FIXME - hack to OPL + + my %tables = getTables($ce, "OPL"); return undef; } @@ -904,6 +1067,7 @@ hash has the following format: Written by Bill Ziemer. Modified by John Jones. +Modifed by Nathan Wallach to add support for multiple libraries. =cut diff --git a/lib/WebworkWebservice/LibraryActions.pm b/lib/WebworkWebservice/LibraryActions.pm index 70f5d767dc..3279274388 100644 --- a/lib/WebworkWebservice/LibraryActions.pm +++ b/lib/WebworkWebservice/LibraryActions.pm @@ -284,7 +284,15 @@ sub searchLib { #API for searching the NPL database if($rh->{library_levels}) { $self->{level} = [split(//, $rh->{library_levels})]; } + + # The code which supports multiple libraries needs the problemLibrary data + # data to be passed on into WeBWorK::Utils::ListingDB when called via + # the instructorXMLHandler, so add it here: + $self->{ce}->{problemLibrary} = {%{$ce->{problemLibrary}} }; + + 'getDBTextbooks' eq $subcommand && do { + $self->{library_name} = $rh->{library_name}; $self->{library_subjects} = $rh->{library_subjects}; $self->{library_chapters} = $rh->{library_chapters}; $self->{library_sections} = $rh->{library_sections}; @@ -293,23 +301,32 @@ sub searchLib { #API for searching the NPL database $out->{ra_out} = \@textbooks; return($out); }; + 'getAllLibraries' eq $subcommand && do { + my @libraries = WeBWorK::Utils::ListingDB::getAllLibraries($self); + $out->{ra_out} = \@libraries; + $out->{text} = encode_base64("Libraries loaded."); + return($out); + }; 'getAllDBsubjects' eq $subcommand && do { + $self->{library_name} = $rh->{library_name}; my @subjects = WeBWorK::Utils::ListingDB::getAllDBsubjects($self); $out->{ra_out} = \@subjects; $out->{text} = encode_base64("Subjects loaded."); return($out); }; 'getAllDBchapters' eq $subcommand && do { + $self->{library_name} = $rh->{library_name}; $self->{library_subjects} = $rh->{library_subjects}; my @chaps = WeBWorK::Utils::ListingDB::getAllDBchapters($self); $out->{ra_out} = \@chaps; - $out->{text} = encode_base64("Chapters loaded."); + $out->{text} = encode_base64("Chapters loaded."); return($out); }; 'getDBListings' eq $subcommand && do { my $templateDir = $self->ce->{courseDirs}->{templates}; + $self->{library_name} = $rh->{library_name}; $self->{library_subjects} = $rh->{library_subjects}; $self->{library_chapters} = $rh->{library_chapters}; $self->{library_sections} = $rh->{library_sections}; @@ -318,25 +335,39 @@ sub searchLib { #API for searching the NPL database $self->{library_textchapter} = $rh->{library_textchapter}; $self->{library_textsection} = $rh->{library_textsection}; debug(to_json($rh)); + my @listings = WeBWorK::Utils::ListingDB::getDBListings($self); - my @output = map {$templateDir."/Library/".$_->{path}."/".$_->{filename}} @listings; + + # Find library (directory) name and then lookup the code name + my $reqLib = $rh->{library_name}; + my $libCode = $ce->{problemLibrary}->{LookupTable}->{$reqLib}; + my $linkName = $ce->{problemLibrary}->{$libCode}->{linkname}; # Name of the symbolic link + + # OLD code assumed the directory name "Library" which was for the main OPL + #my @output = map {$templateDir."/Library/".$_->{path}."/".$_->{filename}} @listings; #change the hard coding!!!....just saying + + # NEW: + my @output = map {$templateDir."/${linkName}/".$_->{path}."/".$_->{filename}} @listings; + $out->{ra_out} = \@output; return($out); }; 'getSectionListings' eq $subcommand && do { + $self->{library_name} = $rh->{library_name}; $self->{library_subjects} = $rh->{library_subjects}; $self->{library_chapters} = $rh->{library_chapters}; $self->{library_sections} = $rh->{library_sections}; my @section_listings = WeBWorK::Utils::ListingDB::getAllDBsections($self); $out->{ra_out} = \@section_listings; - $out->{text} = encode_base64("Sections loaded."); + $out->{text} = encode_base64("Sections loaded."); return($out); }; 'countDBListings' eq $subcommand && do { + $self->{library_name} = $rh->{library_name}; $self->{library_subjects} = $rh->{library_subjects}; $self->{library_chapters} = $rh->{library_chapters}; $self->{library_sections} = $rh->{library_sections}; @@ -344,8 +375,33 @@ sub searchLib { #API for searching the NPL database $self->{library_textbook} = $rh->{library_textbook}; $self->{library_textchapter} = $rh->{library_textchapter}; $self->{library_textsection} = $rh->{library_textsection}; - my $count = WeBWorK::Utils::ListingDB::countDBListings($self); - $out->{text} = encode_base64("Count done."); + + # The code which supports multiple libraries needs the problemLibrary data + # to be passed on + #$self->{ce}->{problemLibrary} = {%{$ce->{problemLibrary}} }; + + + my $count = 0; + if ( ( $rh->{library_name} eq "All Libraries" ) || + # ( !defined( $rh->{library_name} ) ) || + ( $rh->{library_name} eq "" ) ) { # "All Libraries gets changes to "" by JS code + my $jjj = $rh->{library_name}; + # LOOP over all libraries + my @libs = keys( %{$ce->{problemLibrary}} ); + my $ll; + $count = ""; + foreach $ll ( @libs ) { + # skip "LookupTable" + next if ( ( $ll eq "LookupTable" ) || ( $ll eq "" ) ); + $self->{library_name} = "$ce->{problemLibrary}->{$ll}->{linkname}"; + + $count += WeBWorK::Utils::ListingDB::countDBListings($self); + } + } else { + $count = WeBWorK::Utils::ListingDB::countDBListings($self); + } + + $out->{text} = encode_base64("Count done."); $out->{ra_out} = [$count]; return($out); }; @@ -392,6 +448,7 @@ sub getProblemDirectories { my %libraries = %{$self->ce->{courseFiles}->{problibs}}; + # FIXME - this is for the OPL with the fixed path my $lib = "Library"; my $source = $ce->{courseDirs}{templates}; my $main = MY_PROBLEMS; my $isTop = 1; From 21df7f9666c4ef772d41939d80ea6c329cd4ef0a Mon Sep 17 00:00:00 2001 From: Nathan Wallach Date: Sun, 5 Aug 2018 18:49:06 +0300 Subject: [PATCH 02/10] Changes code to use different "OPL-like" DB tables for different libraries. OPL-update has a setting to prevent clearing most tables which is to be used when libraries other than the first one are processed. A new OPL download should be reloaded "regularly" and then the additional libraries should be processed. --- bin/OPL-update | 126 ++++++++++++++++++++++++++------- bin/OPLUtils.pm | 28 ++++++++ lib/WeBWorK/Utils/ListingDB.pm | 17 +++++ 3 files changed, 145 insertions(+), 26 deletions(-) diff --git a/bin/OPL-update b/bin/OPL-update index 502826ec08..0bc96d4050 100755 --- a/bin/OPL-update +++ b/bin/OPL-update @@ -21,8 +21,31 @@ use File::Basename; use Cwd; use DBI; +# Command line arguments +# for some reason the first command line argument is disappearing before it gets here my $myLib = "OPL" ; # default value +my $clearAll = 1 ; # default value - drop ALL old tables + +if ( ( 0 + @ARGV ) > 0 ) { + + #print join(" , ", @ARGV ); + + $myLib = shift; + + print "myLib = $myLib \n"; + + my $nextArg = "none"; + if ( @ARGV ) { + $nextArg = shift ; + if ( $nextArg eq "noDrop" ) { + $clearAll = 0 ; + print "Received the noDrop setting.\n"; + } + + } +} + #(maximum varchar length is 255 for mysql version < 5.0.3. @@ -79,6 +102,26 @@ my %NPLtables = ( pgfile_problem => 'NPL-pgfile-problem', ); +# Which tables to ALWAYS drop and recreate and which can remain +# 1 = always drop, 0 = conditional on command-line arguments +my %AlwayDropTables = ( + dbsubject => 0, + dbchapter => 0, + dbsection => 0, + author => 0, + path => 0, + keyword => 0, + textbook => 0, + chapter => 0, + section => 0, + problem => 0, + morelt => 0, + pgfile => 1, + pgfile_keyword => 1, + pgfile_problem => 1, +); + + # Get database connection @@ -133,14 +176,30 @@ if($libraryVersion eq '2.5') { print "Library version is $libraryVersion; NPLtables! \n"; } + +# Modify table names for per-library tables +my @special_tables = qw( pgfile pgfile_keyword pgfile_problem ); +my $tmp1; my $tblName; +foreach $tmp1 ( @special_tables ) { + $tblName = $tables{$tmp1}; + #print "old table name $tblName\n"; + if ( $libraryVersion eq '2.5') { + $tblName =~ s/OPL/${myLib}/; + } else { + $tblName =~ s/NPL/${myLib}/; + } + #print "new table name $tblName\n"; + $tables{$tmp1} = $tblName; +} + @create_tables = ( -[$tables{dbsubject}, ' +["dbsubject",$tables{dbsubject}, ' DBsubject_id int(15) NOT NULL auto_increment, name varchar(255) NOT NULL, KEY DBsubject (name), PRIMARY KEY (DBsubject_id) '], -[$tables{dbchapter}, ' +["dbchapter",$tables{dbchapter}, ' DBchapter_id int(15) NOT NULL auto_increment, name varchar(255) NOT NULL, DBsubject_id int(15) DEFAULT 0 NOT NULL, @@ -148,7 +207,7 @@ if($libraryVersion eq '2.5') { KEY (DBsubject_id), PRIMARY KEY (DBchapter_id) '], -[$tables{dbsection}, ' +["dbsection",$tables{dbsection}, ' DBsection_id int(15) NOT NULL auto_increment, name varchar(255) NOT NULL, DBchapter_id int(15) DEFAULT 0 NOT NULL, @@ -156,7 +215,7 @@ if($libraryVersion eq '2.5') { KEY (DBchapter_id), PRIMARY KEY (DBsection_id) '], -[$tables{author}, ' +["author",$tables{author}, ' author_id int (15) NOT NULL auto_increment, institution tinyblob, lastname varchar (255) NOT NULL, @@ -165,7 +224,7 @@ if($libraryVersion eq '2.5') { KEY author (lastname(100), firstname(100)), PRIMARY KEY (author_id) '], -[$tables{path}, ' +["path",$tables{path}, ' path_id int(15) NOT NULL auto_increment, path varchar(255) NOT NULL, machine varchar(255), @@ -173,7 +232,7 @@ if($libraryVersion eq '2.5') { KEY (path), PRIMARY KEY (path_id) '], -[$tables{pgfile}, ' +["pgfile",$tables{pgfile}, ' pgfile_id int(15) NOT NULL auto_increment, DBsection_id int(15) NOT NULL, author_id int(15), @@ -187,19 +246,19 @@ if($libraryVersion eq '2.5') { MO TINYINT, PRIMARY KEY (pgfile_id) '], -[$tables{keyword}, ' +["keyword",$tables{keyword}, ' keyword_id int(15) NOT NULL auto_increment, keyword varchar(256) NOT NULL, KEY (keyword), PRIMARY KEY (keyword_id) '], -[$tables{pgfile_keyword}, ' +["pgfile_keyword",$tables{pgfile_keyword}, ' pgfile_id int(15) DEFAULT 0 NOT NULL, keyword_id int(15) DEFAULT 0 NOT NULL, KEY pgfile_keyword (keyword_id, pgfile_id), KEY pgfile (pgfile_id) '], -[$tables{textbook}, ' +["textbook",$tables{textbook}, ' textbook_id int (15) NOT NULL auto_increment, title varchar (255) NOT NULL, edition int (15) DEFAULT 0 NOT NULL, @@ -209,7 +268,7 @@ if($libraryVersion eq '2.5') { pubdate varchar (255), PRIMARY KEY (textbook_id) '], -[$tables{chapter}, ' +["chapter",$tables{chapter}, ' chapter_id int (15) NOT NULL auto_increment, textbook_id int (15), number int(3), @@ -219,7 +278,7 @@ if($libraryVersion eq '2.5') { KEY (number), PRIMARY KEY (chapter_id) '], -[$tables{section}, ' +["section",$tables{section}, ' section_id int(15) NOT NULL auto_increment, chapter_id int (15), number int(3), @@ -229,7 +288,7 @@ if($libraryVersion eq '2.5') { KEY (number), PRIMARY KEY section (section_id) '], -[$tables{problem}, ' +["problem",$tables{problem}, ' problem_id int(15) NOT NULL auto_increment, section_id int(15), number int(4) NOT NULL, @@ -238,7 +297,7 @@ if($libraryVersion eq '2.5') { KEY (section_id), PRIMARY KEY (problem_id) '], -[$tables{morelt}, ' +["morelt",$tables{morelt}, ' morelt_id int(15) NOT NULL auto_increment, name varchar(255) NOT NULL, DBsection_id int(15), @@ -246,7 +305,7 @@ if($libraryVersion eq '2.5') { KEY (name), PRIMARY KEY (morelt_id) '], -[$tables{pgfile_problem}, ' +["pgfile_problem",$tables{pgfile_problem}, ' pgfile_id int(15) DEFAULT 0 NOT NULL, problem_id int(15) DEFAULT 0 NOT NULL, PRIMARY KEY (pgfile_id, problem_id) @@ -261,8 +320,19 @@ $dbh->do("DROP TABLE IF EXISTS `NPL-institution`"); $dbh->do("DROP TABLE IF EXISTS `NPL-pgfile-institution`"); for my $tableinfo (@create_tables) { - my $tabname = $tableinfo->[0]; - my $tabinit = $tableinfo->[1]; + my $tabCnm = $tableinfo->[0]; + my $tabname = $tableinfo->[1]; + my $tabinit = $tableinfo->[2]; + +# FIXME some tables should NOT be dropped on special libraries + if ( $clearAll == 0 ) { + # Do not drop and recreate some tables + if ( $AlwayDropTables{$tabCnm} == 0 ) { + print "Not dropping/re-creating $tabname$tabname\n"; + next; + } + } + my $query = "DROP TABLE IF EXISTS `$tabname`"; $dbh->do($query); $query = "CREATE TABLE `$tabname` ( $tabinit ) ENGINE=$db_storage_engine"; @@ -868,29 +938,33 @@ sub pgfiles { print "\n\n"; -# Now prune away DBsection, etc, which do not appear in any files -#%my $query = "SELECT chapter_id FROM `$tables{chapter}` WHERE textbook_id = \"$bookid\" AND number = \"$1\""; -#%my $chapid = $dbh->selectrow_array($query); +# When allowing multiple libraries, only the FIRST library should do pruning. +if ( $clearAll == 1 ) { + + # Now prune away DBsection, etc, which do not appear in any files + #%my $query = "SELECT chapter_id FROM `$tables{chapter}` WHERE textbook_id = \"$bookid\" AND number = \"$1\""; + #%my $chapid = $dbh->selectrow_array($query); -#select dbs.DBsection_id from OPL_DBsection dbs; -#select COUNT(*) from OPL_pgfile where DBsection_id=857; + #select dbs.DBsection_id from OPL_DBsection dbs; + #select COUNT(*) from OPL_pgfile where DBsection_id=857; -my $dbsects = $dbh->selectall_arrayref("SELECT DBsection_id from `$tables{dbsection}`"); -for my $sect (@{$dbsects}) { + my $dbsects = $dbh->selectall_arrayref("SELECT DBsection_id from `$tables{dbsection}`"); + for my $sect (@{$dbsects}) { $sect = $sect->[0]; my $srar = $dbh->selectall_arrayref("SELECT * FROM `$tables{pgfile}` WHERE DBsection_id=$sect"); if(scalar(@{$srar})==0) { $dbh->do("DELETE FROM `$tables{dbsection}` WHERE DBsection_id=$sect"); } -} + } -my $dbchaps = $dbh->selectall_arrayref("SELECT DBchapter_id from `$tables{dbchapter}`"); -for my $chap (@{$dbchaps}) { + my $dbchaps = $dbh->selectall_arrayref("SELECT DBchapter_id from `$tables{dbchapter}`"); + for my $chap (@{$dbchaps}) { $chap = $chap->[0]; my $srar = $dbh->selectall_arrayref("SELECT * FROM `$tables{dbsection}` WHERE DBchapter_id=$chap"); if(scalar(@{$srar})==0) { $dbh->do("DELETE FROM `$tables{dbchapter}` WHERE DBchapter_id=$chap"); } + } } # Now run the script build-library-tree diff --git a/bin/OPLUtils.pm b/bin/OPLUtils.pm index 539e990deb..62ceb25650 100644 --- a/bin/OPLUtils.pm +++ b/bin/OPLUtils.pm @@ -166,6 +166,20 @@ sub build_library_subject_tree { my %tables = ($libraryVersion eq '2.5')? %OPLtables : %NPLtables; + # Modify table names for per-library tables + my @special_tables = qw( pgfile pgfile_keyword pgfile_problem ); + my $tmp1; my $tblName; + foreach $tmp1 ( @special_tables ) { + $tblName = $tables{$tmp1}; + #print "old table name $tblName\n"; + if ( $libraryVersion eq '2.5') { + $tblName =~ s/OPL/$myLib/; + } else { + $tblName =~ s/NPL/$myLib/; + } + #print "new table name $tblName\n"; + $tables{$tmp1} = $tblName; + } my $selectClause = "select subj.name, ch.name, sect.name, path.path,pg.filename from `$tables{dbsection}` AS sect " ."JOIN `$tables{dbchapter}` AS ch ON ch.DBchapter_id = sect.DBchapter_id " @@ -319,6 +333,20 @@ sub build_library_textbook_tree { my %tables = ($libraryVersion eq '2.5')? %OPLtables : %NPLtables; + # Modify table names for per-library tables + my @special_tables = qw( pgfile pgfile_keyword pgfile_problem ); + my $tmp1; my $tblName; + foreach $tmp1 ( @special_tables ) { + $tblName = $tables{$tmp1}; + #print "old table name $tblName\n"; + if ( $libraryVersion eq '2.5') { + $tblName =~ s/OPL/$myLib/; + } else { + $tblName =~ s/NPL/$myLib/; + } + #print "new table name $tblName\n"; + $tables{$tmp1} = $tblName; + } my $selectClause = "SELECT pg.pgfile_id from `$tables{path}` as path " ."LEFT JOIN `$tables{pgfile}` AS pg ON pg.path_id=path.path_id " diff --git a/lib/WeBWorK/Utils/ListingDB.pm b/lib/WeBWorK/Utils/ListingDB.pm index a629297e2e..82b83e054b 100644 --- a/lib/WeBWorK/Utils/ListingDB.pm +++ b/lib/WeBWorK/Utils/ListingDB.pm @@ -99,6 +99,23 @@ sub getTables { } else { %tables = %OPLtables; } + + # Modify table names for per-library tables + my @special_tables = qw( pgfile pgfile_keyword pgfile_problem ); + my $tmp1; my $tblName; + foreach $tmp1 ( @special_tables ) { + next if ( $myLib eq "" ); # Skip this + $tblName = $tables{$tmp1}; + #print "old table name $tblName\n"; + if ( $ce->{problemLibrary}->{$myLib}->{version} eq "2" ) { + $tblName =~ s/NPL/${myLib}/; + } else { + $tblName =~ s/OPL/${myLib}/; + } + #print "new table name $tblName\n"; + $tables{$tmp1} = $tblName; + } + return %tables; } From 71772e907d85a34e592b4cab4410705f331a03e8 Mon Sep 17 00:00:00 2001 From: Nathan Wallach Date: Tue, 14 Aug 2018 10:28:29 +0300 Subject: [PATCH 03/10] Minor fix, move reset of library_name back to to "all" outside of loop over all libraries. --- lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm b/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm index 3c9b229c94..3a08fb069a 100644 --- a/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm +++ b/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm @@ -598,8 +598,8 @@ sub browse_library_panel2 { # FIXME should check here that this library is installed for this course $count_line += WeBWorK::Utils::ListingDB::countDBListings($r); - $r->param( 'library_name' => LIB2_DATA->{dbLibrary}{all} ); } + $r->param( 'library_name' => LIB2_DATA->{dbLibrary}{all} ); } else { $count_line = WeBWorK::Utils::ListingDB::countDBListings($r); } @@ -757,8 +757,8 @@ sub browse_library_panel2adv { # FIXME should check here that this library is installed for this course $count_line += WeBWorK::Utils::ListingDB::countDBListings($r); - $r->param( 'library_name' => LIB2_DATA->{dbLibrary}{all} ); } + $r->param( 'library_name' => LIB2_DATA->{dbLibrary}{all} ); } else { $count_line = WeBWorK::Utils::ListingDB::countDBListings($r); } @@ -1525,7 +1525,7 @@ sub pre_header_initialize { ##### View from the library database } elsif ($r->param('lib_view')) { - + @pg_files=(); my @dbsearch; From 94ac375a3f6b15e89658a77780a7d486fbf1ec90 Mon Sep 17 00:00:00 2001 From: Nathan Wallach Date: Tue, 14 Aug 2018 10:30:12 +0300 Subject: [PATCH 04/10] Added in some commented out debug code about the hash settings for libraries. --- lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm b/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm index 3a08fb069a..8f804b1e5c 100644 --- a/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm +++ b/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm @@ -483,6 +483,16 @@ sub browse_library_panel { my $linkName = $r->{ce}->{problemLibrary}->{OPL}->{linkname}; + # # Debug code below displays the hash data in the HTML output + #my $pl1 = $r->{ce}->{problemLibrary}; + #my $opl1 = $pl1->{OPL}; + #print CGI::Tr(CGI::td(CGI::div({class=>'ResultsWithError', align=>"center"}, + # "libraryRoot setting is $libraryRoot and pl1 keys: " + # . join(" ",keys( %$pl1)) + # . "
opl1 keys: " + # . join(" ",keys( %$opl1)) + # ))); + unless($libraryRoot) { print CGI::Tr(CGI::td(CGI::div({class=>'ResultsWithError', align=>"center"}, "The OPL problem library has not been installed."))); From ff00061c808be10ff4d0907ab2dd035e7731bed7 Mon Sep 17 00:00:00 2001 From: Nathan Wallach Date: Thu, 16 Aug 2018 12:19:51 +0300 Subject: [PATCH 05/10] Store pgfile counts for given subject/chapter/section in database. Use a single shared DB table to each type with a library code field. OPL-update creates the tables. Data is added when searches are made for which the count tables do not have a record yet. --- bin/OPL-update | 37 ++++- lib/WeBWorK/Utils/ListingDB.pm | 264 ++++++++++++++++++++++++++++++++- 2 files changed, 297 insertions(+), 4 deletions(-) diff --git a/bin/OPL-update b/bin/OPL-update index 0bc96d4050..9425a89527 100755 --- a/bin/OPL-update +++ b/bin/OPL-update @@ -82,6 +82,9 @@ my %OPLtables = ( problem => 'OPL_problem', morelt => 'OPL_morelt', pgfile_problem => 'OPL_pgfile_problem', + cnt_dbsubject => 'Cnt_DBsubject', + cnt_dbchapter => 'Cnt_DBchapter', + cnt_dbsection => 'Cnt_DBsection', ); @@ -100,10 +103,13 @@ my %NPLtables = ( problem => 'NPL-problem', morelt => 'NPL-morelt', pgfile_problem => 'NPL-pgfile-problem', + cnt_dbsubject => 'Cnt_DBsubject', + cnt_dbchapter => 'Cnt_DBchapter', + cnt_dbsection => 'Cnt_DBsection', ); # Which tables to ALWAYS drop and recreate and which can remain -# 1 = always drop, 0 = conditional on command-line arguments +# 1 = always drop, 0 = conditional on command-line arguments, 2 = NEVER drop my %AlwayDropTables = ( dbsubject => 0, dbchapter => 0, @@ -119,6 +125,9 @@ my %AlwayDropTables = ( pgfile => 1, pgfile_keyword => 1, pgfile_problem => 1, + cnt_dbsubject => 2, + cnt_dbchapter => 2, + cnt_dbsection => 2, ); @@ -309,6 +318,24 @@ foreach $tmp1 ( @special_tables ) { pgfile_id int(15) DEFAULT 0 NOT NULL, problem_id int(15) DEFAULT 0 NOT NULL, PRIMARY KEY (pgfile_id, problem_id) +'], +["cnt_dbsubject",$tables{cnt_dbsubject}, ' + libcode varchar(60) NOT NULL, + DBsubject_id int(15) NOT NULL, + count int(15) NOT NULL, + PRIMARY KEY (libcode,DBsubject_id) +'], +["cnt_dbchapter",$tables{cnt_dbchapter}, ' + libcode varchar(60) NOT NULL, + DBchapter_id int(15) NOT NULL, + count int(15) NOT NULL, + PRIMARY KEY (libcode,DBchapter_id) +'], +["cnt_dbsection",$tables{cnt_dbsection}, ' + libcode varchar(60) NOT NULL, + DBsection_id int(15) NOT NULL, + count int(15) NOT NULL, + PRIMARY KEY (libcode,DBsection_id) ']); ### End of database data @@ -325,10 +352,16 @@ for my $tableinfo (@create_tables) { my $tabinit = $tableinfo->[2]; # FIXME some tables should NOT be dropped on special libraries + if ( $AlwayDropTables{$tabCnm} == 2 ) { + print "Not dropping/re-creating $tabname\n"; + my $query = "CREATE TABLE IF NOT EXISTS `$tabname` ( $tabinit ) ENGINE=$db_storage_engine"; + $dbh->do($query); + next; + } if ( $clearAll == 0 ) { # Do not drop and recreate some tables if ( $AlwayDropTables{$tabCnm} == 0 ) { - print "Not dropping/re-creating $tabname$tabname\n"; + print "Not dropping/re-creating $tabname\n"; next; } } diff --git a/lib/WeBWorK/Utils/ListingDB.pm b/lib/WeBWorK/Utils/ListingDB.pm index 82b83e054b..eef288fd26 100644 --- a/lib/WeBWorK/Utils/ListingDB.pm +++ b/lib/WeBWorK/Utils/ListingDB.pm @@ -67,6 +67,9 @@ my %OPLtables = ( problem => 'OPL_problem', morelt => 'OPL_morelt', pgfile_problem => 'OPL_pgfile_problem', + cnt_dbsubject => 'Cnt_DBsubject', + cnt_dbchapter => 'Cnt_DBchapter', + cnt_dbsection => 'Cnt_DBsection', ); @@ -85,6 +88,9 @@ my %NPLtables = ( problem => 'NPL-problem', morelt => 'NPL-morelt', pgfile_problem => 'NPL-pgfile-problem', + cnt_dbsubject => 'Cnt_DBsubject', + cnt_dbchapter => 'Cnt_DBchapter', + cnt_dbsection => 'Cnt_DBsection', ); @@ -721,9 +727,263 @@ sub getDBListings { return @results; } +# special return codes: +# -200 = all libraries, do not save data +# -100 = should not save data +# -1 = should save count after it is found + +sub requestSavedCount { + my $r = shift; + + # Find library (directory) name and then lookup the code name + my $reqLib = $r->param('library_name'); + if ( $reqLib eq "" ) { + # When called via instructorXMLHandler from JavaScript + # there can be a difference between the "param" value and the "hash" value. + # and we want the non-empty value + $reqLib = $r->{library_name}; + } + + # Need to handle case of "All Libraries" which should not get passed + # into this function, but would cause an error below. + if ( ( $reqLib eq "All Libraries" ) || ( $reqLib eq "" ) ) { + return( -200 ); + } + + my $libCode = $r->{ce}->{problemLibrary}->{LookupTable}->{$reqLib}; + + # Now depends on the $libCode + my %tables = getTables($r->ce, $libCode); + my $ce = $r->ce; + + my $keywords = $r->param('library_keywords') || ""; + if($keywords) { + return( -100 ); # No saved counts for this case + } + + for my $j (qw( textbook textchapter textsection )) { + my $foo = $r->param(LIBRARY_STRUCTURE->{$j}{name}) || ''; + $foo =~ s/^\s*\d+\.\s*//; + if($foo) { + return( -100 ); # No saved counts for this case + } + } + + # Next could be an array, an array reference, or nothing + my @levels = $r->param('level'); + if(scalar(@levels) == 1 and ref($levels[0]) eq 'ARRAY') { + @levels = @{$levels[0]}; + } + @levels = grep { defined($_) && m/\S/ } @levels; + if(scalar(@levels)) { + return( -100 ); # No saved counts for this case + } + + my $subj = $r->param('library_subjects') || ""; + my $chap = $r->param('library_chapters') || ""; + my $sec = $r->param('library_sections') || ""; + + my $dbh = getDB($ce); + my $cnt_table = $tables{cnt_dbsubject}; + my $query; + + my $typewhere = ''; + my $extrawhere = ''; + my @select_parameters=(); + + if($subj) { + $cnt_table = $tables{cnt_dbsubject}; + $typewhere = " dbsj.DBsubject_id = cnt.DBsubject_id "; + $extrawhere .= " AND dbsj.name= ? "; + push @select_parameters, $subj; + } + if($chap) { + $cnt_table = $tables{cnt_dbchapter}; + $typewhere = " dbc.DBchapter_id = cnt.DBchapter_id "; + $extrawhere .= " AND dbc.name= ? "; + push @select_parameters, $chap; + } + if($sec) { + $cnt_table = $tables{cnt_dbsection}; + $typewhere = " dbsc.DBsection_id = cnt.DBsection_id "; + $extrawhere .= " AND dbsc.name= ? "; + push @select_parameters, $sec; + } + push @select_parameters, $libCode; + + my $query = "SELECT count from `$cnt_table` cnt, + `$tables{dbsection}` dbsc, + `$tables{dbchapter}` dbc, + `$tables{dbsubject}` dbsj + WHERE dbsj.DBsubject_id = dbc.DBsubject_id AND + dbc.DBchapter_id = dbsc.DBchapter_id AND + $typewhere $extrawhere AND cnt.libcode = ?"; + + $query =~ s/\n/ /g; + warn "no text info: ", $query; + warn "params: ", join(" | ",@select_parameters); + + my $sth = $dbh->prepare_cached( $query ); + if ( !defined($sth) ) { + warn "Couldn't prepare statement: " . $dbh->errstr; + return(-300); + } + + my $rv = $sth->execute(@select_parameters); + if ( !defined($rv) ) { + warn "Couldn't execute statement: " . $sth->errstr; + return(-300); + } + + if ($sth->rows == 0) { + warn "No record found"; + return(-1); + } + my @data = $sth->fetchrow_array(); + return( $data[0] ); +} + +# Get the id +sub safe_get_id { + my $dbh = shift; + my $tablename = shift; + my $idname = shift; + my $whereclause = shift; + my $wherevalues = shift; + + my $query = "SELECT $idname FROM `$tablename` ".$whereclause; + my $sth = $dbh->prepare($query); + $sth->execute(@$wherevalues); + my $idvalue; + my @row; + unless(@row = $sth->fetchrow_array()) { + return -1; + } + $idvalue = $row[0]; + return($idvalue); +} + + +sub saveCount { + my $r = shift; + my $countToSave = shift; # count found + + # Find library (directory) name and then lookup the code name + my $reqLib = $r->param('library_name'); + if ( $reqLib eq "" ) { + # When called via instructorXMLHandler from JavaScript + # there can be a difference between the "param" value and the "hash" value. + # and we want the non-empty value + $reqLib = $r->{library_name}; + } + + # Need to handle case of "All Libraries" which should not get passed + # into this function, but would cause an error below. + if ( ( $reqLib eq "All Libraries" ) || ( $reqLib eq "" ) ) { + return; # Do not save counts for this case + } + + my $libCode = $r->{ce}->{problemLibrary}->{LookupTable}->{$reqLib}; + + # Now depends on the $libCode + my %tables = getTables($r->ce, $libCode); + my $ce = $r->ce; + + my $keywords = $r->param('library_keywords') || ""; + if($keywords) { + return; # Do not save counts for this case + } + + for my $j (qw( textbook textchapter textsection )) { + my $foo = $r->param(LIBRARY_STRUCTURE->{$j}{name}) || ''; + $foo =~ s/^\s*\d+\.\s*//; + if($foo) { + return; # Do not save counts for this case + } + } + + # Next could be an array, an array reference, or nothing + my @levels = $r->param('level'); + if(scalar(@levels) == 1 and ref($levels[0]) eq 'ARRAY') { + @levels = @{$levels[0]}; + } + @levels = grep { defined($_) && m/\S/ } @levels; + if(scalar(@levels)) { + return; # Do not save counts for this case + } + + my $subj = $r->param('library_subjects') || ""; + my $chap = $r->param('library_chapters') || ""; + my $sec = $r->param('library_sections') || ""; + + my $dbh = getDB($ce); + my $cnt_table = $tables{cnt_dbsubject}; + my $query; + + my @insert_parameters=( "$libCode" ); # Always the first parameter + + my $id_to_use = -1; + + my $new_dbsubj_id; + my $new_dbchap_id; + my $new_dbsect_id; + + if($subj) { + $new_dbsubj_id = safe_get_id($dbh, $tables{dbsubject}, 'DBsubject_id', + qq(WHERE name = ?), ["$subj"] ); + $id_to_use = $new_dbsubj_id; + $cnt_table = $tables{cnt_dbsubject}; + } + if($chap) { + $new_dbchap_id = safe_get_id($dbh, $tables{dbchapter}, 'DBchapter_id', + qq(WHERE name = ? and DBsubject_id = ?), ["$chap", $new_dbsubj_id] ); + $id_to_use = $new_dbchap_id; + $cnt_table = $tables{cnt_dbchapter}; + } + if($sec) { + $new_dbsect_id = safe_get_id($dbh, $tables{dbsection}, 'DBsection_id', + qq(WHERE name = ? and DBchapter_id = ?), ["$sec", $new_dbchap_id] ); + $id_to_use = $new_dbsect_id; + $cnt_table = $tables{cnt_dbsection}; + } + push( @insert_parameters, $id_to_use, $countToSave ); + +# my $query = "INSERT INTO `$cnt_table` VALUES (?,?,?)"; + my $query = "REPLACE INTO `$cnt_table` VALUES (?,?,?)"; + + $query =~ s/\n/ /g; + #warn "no text info: ", $query; + #warn "params: ", join(" | ",@insert_parameters); + + my $sth = $dbh->prepare_cached( $query ); + if ( !defined($sth) ) { + warn "Couldn't prepare statement: " . $dbh->errstr; + return; + } + + my $rv = $sth->execute(@insert_parameters); + if ( !defined($rv) ) { + warn "Couldn't execute statement: " . $sth->errstr; + return; + } +} + sub countDBListings { - my $r = shift; - return (getDBListings($r,1)); + my $r = shift; + my $fromSaved = -1; + $fromSaved = requestSavedCount($r); + if ( $fromSaved >= 0 ) { + #warn "fromSaved = $fromSaved"; + return( $fromSaved ); + } else { + #warn "fromSaved = $fromSaved"; + my $countedNow = getDBListings($r,1); + #warn "countedNow = $countedNow"; + if ( $fromSaved == -1 ) { + saveCount($r, $countedNow); + } + return( $countedNow ); + } } sub getMLTleader { From 4b5824ee5218c2049a31a1dd3237720bf244a54a Mon Sep 17 00:00:00 2001 From: Nathan Wallach Date: Thu, 16 Aug 2018 13:12:58 +0300 Subject: [PATCH 06/10] Small bugs in prior commit fixed. --- lib/WeBWorK/Utils/ListingDB.pm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/WeBWorK/Utils/ListingDB.pm b/lib/WeBWorK/Utils/ListingDB.pm index eef288fd26..9bd5337d92 100644 --- a/lib/WeBWorK/Utils/ListingDB.pm +++ b/lib/WeBWorK/Utils/ListingDB.pm @@ -793,19 +793,19 @@ sub requestSavedCount { if($subj) { $cnt_table = $tables{cnt_dbsubject}; - $typewhere = " dbsj.DBsubject_id = cnt.DBsubject_id "; + $typewhere = "AND dbsj.DBsubject_id = cnt.DBsubject_id "; $extrawhere .= " AND dbsj.name= ? "; push @select_parameters, $subj; } if($chap) { $cnt_table = $tables{cnt_dbchapter}; - $typewhere = " dbc.DBchapter_id = cnt.DBchapter_id "; + $typewhere = " AND dbc.DBchapter_id = cnt.DBchapter_id "; $extrawhere .= " AND dbc.name= ? "; push @select_parameters, $chap; } if($sec) { $cnt_table = $tables{cnt_dbsection}; - $typewhere = " dbsc.DBsection_id = cnt.DBsection_id "; + $typewhere = " AND dbsc.DBsection_id = cnt.DBsection_id "; $extrawhere .= " AND dbsc.name= ? "; push @select_parameters, $sec; } @@ -816,7 +816,7 @@ sub requestSavedCount { `$tables{dbchapter}` dbc, `$tables{dbsubject}` dbsj WHERE dbsj.DBsubject_id = dbc.DBsubject_id AND - dbc.DBchapter_id = dbsc.DBchapter_id AND + dbc.DBchapter_id = dbsc.DBchapter_id $typewhere $extrawhere AND cnt.libcode = ?"; $query =~ s/\n/ /g; From 820991eaa39cdae7fcb129334868b91ac9276026 Mon Sep 17 00:00:00 2001 From: Nathan Wallach Date: Thu, 16 Aug 2018 17:41:14 +0300 Subject: [PATCH 07/10] Add a setParam() method so a new request parameter can be added to the FakeRequest data used by WebworkXMLRPC object. This is needed in the code which handles multiple libraries, as the lib/WeBWorK/Utils/ListingDB.pm needs to be able to temporarily set such parameters. --- lib/WebworkWebservice.pm | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/WebworkWebservice.pm b/lib/WebworkWebservice.pm index c3e5b71e7c..2cf23add7d 100644 --- a/lib/WebworkWebservice.pm +++ b/lib/WebworkWebservice.pm @@ -936,11 +936,11 @@ sub updateSetting { ce db - params + param authz authen maketext - + setParam - Added to add an extra param like value =cut sub ce { @@ -959,6 +959,12 @@ sub param { # imitate get behavior of the request object params method debug("use param $param => $out") if $UNIT_TESTS_ON; $out; } +sub setParam { # add a new param like value + my $self =shift; + my $param = shift; + my $value = shift; + $self->{fake_r}->{$param} = $value; +} sub authz { my $self = shift; debug("use authz ") if $UNIT_TESTS_ON; From a7dc4125335d14853210ab62de98541f5d20c032 Mon Sep 17 00:00:00 2001 From: Nathan Wallach Date: Thu, 16 Aug 2018 17:44:01 +0300 Subject: [PATCH 08/10] Clear various library settings in mydefaultRequestObject inside the lib_update() routine, as the multi-library code expects that they will be blank when not needed. This is necessary for the subcommands: getAllLibraries getAllDBsubjects getAllDBchapters getSectionListings which need access to the counts database data of the appropriate type and type is determined by the lowest level set. --- htdocs/js/apps/SetMaker/setmaker.js | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/htdocs/js/apps/SetMaker/setmaker.js b/htdocs/js/apps/SetMaker/setmaker.js index ecd7c676b0..0520103bf7 100644 --- a/htdocs/js/apps/SetMaker/setmaker.js +++ b/htdocs/js/apps/SetMaker/setmaker.js @@ -182,11 +182,28 @@ function lib_update(who, what) { // if(who=='myLibrary'){ subcommand = "getAllLibraries";} - if(who=='myLibrary'){ subcommand = "getAllLibraries";} - if(who=='subjects') { subcommand = "getAllDBsubjects";} - if(who=='chapters') { subcommand = "getAllDBchapters";} - - if(who=='sections') { subcommand = "getSectionListings";} + if(who=='myLibrary'){ + subcommand = "getAllLibraries"; + mydefaultRequestObject.library_name=''; + mydefaultRequestObject.library_subjects=''; + mydefaultRequestObject.library_chapters=''; + mydefaultRequestObject.library_sections=''; + } + if(who=='subjects') { + subcommand = "getAllDBsubjects"; + mydefaultRequestObject.library_subjects=''; + mydefaultRequestObject.library_chapters=''; + mydefaultRequestObject.library_sections=''; + } + if(who=='chapters') { + subcommand = "getAllDBchapters"; + mydefaultRequestObject.library_chapters=''; + mydefaultRequestObject.library_sections=''; + } + if(who=='sections') { + subcommand = "getSectionListings"; + mydefaultRequestObject.library_sections=''; + } mydefaultRequestObject.command = subcommand; // console.log(mydefaultRequestObject); From cf648c44438639521a0f7f8bdd10ad8950093260 Mon Sep 17 00:00:00 2001 From: Nathan Wallach Date: Thu, 16 Aug 2018 18:40:29 +0300 Subject: [PATCH 09/10] Add a "Get subjects in this library" button, as we do not want to clear settings when changing libraries. This approach allows doing the same search on another library just by changing the library chosen. --- .../ContentGenerator/Instructor/SetMaker.pm | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm b/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm index 8f804b1e5c..92a4c5cc3e 100644 --- a/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm +++ b/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm @@ -634,10 +634,10 @@ sub browse_library_panel2 { -default=> $libName_selected, -onchange=>"lib_update('count', 'clear');return true" )]), - #CGI::td({-colspan=>2, -align=>"left"}, - # CGI::button(-name=>"change_library", -value=>$r->maketext("Change library"), # Not needed - # -onclick=>"lib_update('subjects', 'get');return true" - # )) + CGI::td({-colspan=>2, -align=>"left"}, + CGI::button(-name=>"change_library", -value=>$r->maketext("Get subjects in this library"), + -onclick=>"lib_update('subjects', 'get');return true" + )) ), CGI::Tr({}, @@ -812,10 +812,10 @@ sub browse_library_panel2adv { -default=> $selected{dbLibrary}, -onchange=>"lib_update('count', 'clear');return true" )]), - #CGI::td({-colspan=>2, -align=>"left"}, - # CGI::button(-name=>"change_library", -value=>$r->maketext("Change library"), # Not needed - # -onclick=>"lib_update('subjects', 'get');return true" - # )) + CGI::td({-colspan=>2, -align=>"left"}, + CGI::button(-name=>"change_library", -value=>$r->maketext("Get subjects in this library"), # Not needed + -onclick=>"lib_update('subjects', 'get');return true" + )) ), CGI::Tr({}, From 5a0e3a7f876fa3c965dcc3924129b7f715763754 Mon Sep 17 00:00:00 2001 From: Nathan Wallach Date: Thu, 16 Aug 2018 18:42:22 +0300 Subject: [PATCH 10/10] Modify ListingDB.pm so that for a specific library it returns subjects, chapters, and sections ONLY if they contain problems. However, for the "All Libraries" setting it will return all options from the entire taxonomy. --- lib/WeBWorK/Utils/ListingDB.pm | 98 +++++++++++++++++++++++++++++++--- 1 file changed, 91 insertions(+), 7 deletions(-) diff --git a/lib/WeBWorK/Utils/ListingDB.pm b/lib/WeBWorK/Utils/ListingDB.pm index 9bd5337d92..9e0edcf473 100644 --- a/lib/WeBWorK/Utils/ListingDB.pm +++ b/lib/WeBWorK/Utils/ListingDB.pm @@ -453,8 +453,37 @@ sub getAllDBsubjects { while (@row = $sth->fetchrow_array()) { push @results, $row[0]; } - # @results = sortByName(undef, @results); - return @results; + + # When no current library is set, or "All Libraries" was set and + # changed to an empty string - list ALL subjects from ALL libraries. + if ( !defined($reqLib) || ( $reqLib eq "" ) ) { + #@results = sortByName(undef, @results); + return @results; + } + + # Get count of problems to determine if the subject should be listed. + my $tmp1; + my $savedCount; + my @nonEmptyResults; # Results which are not empty + + foreach $tmp1 ( @results ) { + + # Set the "param"eter - needs special code when this is a WebworkXMLRPC object + my $toSet = 'library_subjects'; + if ( ref($r) eq "WebworkXMLRPC" ) { + $r->setParam($toSet, $tmp1); + } else { + $r->param($toSet => "$tmp1"); + } + + $savedCount = countDBListings( $r ); + #warn "In getAllDBsubjects $tmp1 savedCount = $savedCount"; + push( @nonEmptyResults, $tmp1) if ( $savedCount > 0 ); + } + + #@results = sortByName(undef, @results); + #return @results; + return @nonEmptyResults; } @@ -492,8 +521,36 @@ sub getAllDBchapters { my $all_chaps_ref = $dbh->selectall_arrayref($query, {},$subject); my @results = map { $_->[0] } @{$all_chaps_ref}; + + # When no current library is set, or "All Libraries" was set and + # changed to an empty string - list ALL chapters from ALL libraries. + if ( !defined($reqLib) || ( $reqLib eq "" ) ) { + #@results = sortByName(undef, @results); + return @results; + } + + # Get count of problems to determine if the chapter should be listed. + my $tmp1; + my $savedCount; + my @nonEmptyResults; # Results which are not empty + foreach $tmp1 ( @results ) { + + # Set the "param"eter - needs special code when this is a WebworkXMLRPC object + my $toSet = 'library_chapters'; + if ( ref($r) eq "WebworkXMLRPC" ) { + $r->setParam($toSet, $tmp1); + } else { + $r->param($toSet => "$tmp1"); + } + + $savedCount = countDBListings( $r ); + #warn "In getAllDBchapters $tmp1 savedCount = $savedCount"; + push( @nonEmptyResults, $tmp1) if ( $savedCount > 0 ); + } + #@results = sortByName(undef, @results); - return @results; + #return @results; + return @nonEmptyResults; } =item getAllDBsections($r) @@ -534,8 +591,35 @@ sub getAllDBsections { my $all_sections_ref = $dbh->selectall_arrayref($query, {},$subject, $chapter); my @results = map { $_->[0] } @{$all_sections_ref}; + + # When no current library is set, or "All Libraries" was set and + # changed to an empty string - list ALL subjects from ALL libraries. + if ( !defined($reqLib) || ( $reqLib eq "" ) ) { + #@results = sortByName(undef, @results); + return @results; + } + + # Get count of problems to determine if the section should be listed. + my $tmp1; + my $savedCount; + my @nonEmptyResults; # Results which are not empty + foreach $tmp1 ( @results ) { + # Set the "param"eter - needs special code when this is a WebworkXMLRPC object + my $toSet = 'library_sections'; + if ( ref($r) eq "WebworkXMLRPC" ) { + $r->setParam($toSet, $tmp1); + } else { + $r->param($toSet => "$tmp1"); + } + + $savedCount = countDBListings( $r ); + #warn "In getAllDBsections $tmp1 savedCount = $savedCount"; + push( @nonEmptyResults, $tmp1) if ( $savedCount > 0 ); + } + #@results = sortByName(undef, @results); - return @results; + #return @results; + return @nonEmptyResults; } =item getDBSectionListings($r) @@ -820,8 +904,8 @@ sub requestSavedCount { $typewhere $extrawhere AND cnt.libcode = ?"; $query =~ s/\n/ /g; - warn "no text info: ", $query; - warn "params: ", join(" | ",@select_parameters); + #warn "no text info: ", $query; + #warn "params: ", join(" | ",@select_parameters); my $sth = $dbh->prepare_cached( $query ); if ( !defined($sth) ) { @@ -836,7 +920,7 @@ sub requestSavedCount { } if ($sth->rows == 0) { - warn "No record found"; + #warn "No record found"; return(-1); } my @data = $sth->fetchrow_array();