You can subscribe to this list here.
2004 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(58) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2005 |
Jan
(53) |
Feb
(56) |
Mar
|
Apr
|
May
(30) |
Jun
(78) |
Jul
(121) |
Aug
(155) |
Sep
(77) |
Oct
(61) |
Nov
(45) |
Dec
(94) |
2006 |
Jan
(116) |
Feb
(33) |
Mar
(11) |
Apr
(23) |
May
(60) |
Jun
(89) |
Jul
(130) |
Aug
(109) |
Sep
(124) |
Oct
(63) |
Nov
(82) |
Dec
(45) |
2007 |
Jan
(31) |
Feb
(35) |
Mar
(123) |
Apr
(36) |
May
(18) |
Jun
(134) |
Jul
(133) |
Aug
(241) |
Sep
(126) |
Oct
(31) |
Nov
(15) |
Dec
(5) |
2008 |
Jan
(11) |
Feb
(6) |
Mar
(16) |
Apr
(29) |
May
(43) |
Jun
(149) |
Jul
(27) |
Aug
(29) |
Sep
(37) |
Oct
(20) |
Nov
(4) |
Dec
(6) |
2009 |
Jan
(34) |
Feb
(30) |
Mar
(16) |
Apr
(6) |
May
(1) |
Jun
(32) |
Jul
(22) |
Aug
(7) |
Sep
(18) |
Oct
(50) |
Nov
(22) |
Dec
(8) |
2010 |
Jan
(17) |
Feb
(15) |
Mar
(10) |
Apr
(9) |
May
(67) |
Jun
(30) |
Jul
|
Aug
|
Sep
(2) |
Oct
|
Nov
(1) |
Dec
|
From: Mike G. v. a. <we...@ma...> - 2009-02-07 23:03:16
|
Log Message: ----------- print warning if the javaScript file is not available. Modified Files: -------------- pg/macros: AppletObjects.pl Revision Data ------------- Index: AppletObjects.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/AppletObjects.pl,v retrieving revision 1.13 retrieving revision 1.14 diff -Lmacros/AppletObjects.pl -Lmacros/AppletObjects.pl -u -r1.13 -r1.14 --- macros/AppletObjects.pl +++ macros/AppletObjects.pl @@ -51,7 +51,9 @@ <script src="/webwork2_files/js/ww_applet_support.js"> //upload functions stored in /opt/webwork/webwork2/htdocs/js ... </script> - + if (!( typeof(set_debug) == "function") ) { + alert("Can't find the function set_debug. Is the file ww_applet_support.js in /webwork2/htdocs/js"); + } END_HEADER_TEXT }; |
From: Mike G. v. a. <we...@ma...> - 2009-02-07 22:38:37
|
Log Message: ----------- Reworked support code for applets to use a more object oriented approach. Modified Files: -------------- pg/lib: Applet.pm Revision Data ------------- Index: Applet.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Applet.pm,v retrieving revision 1.14 retrieving revision 1.15 diff -Llib/Applet.pm -Llib/Applet.pm -u -r1.14 -r1.15 --- lib/Applet.pm +++ lib/Applet.pm @@ -110,7 +110,7 @@ submitAction() -- calls the submit action of the applets - initializeAction() -- calls the initialize action of the applets + initializeWWquestion() -- calls the initialize action of the applets getQE(name) -- gets an HTML element of the question by name or by id. Be sure to keep all names and ids @@ -179,8 +179,9 @@ initializeActionAlias -- (default: initializeAction) the name of the javaScript subroutine called to initialize the applet (some overlap with config/ and setState submitActionAlias -- (default: submitAction)the name of the javaScript subroutine called when the submit button of the .pg question is pressed. - - returnFieldName + answerBox -- name of answer box to return answer to: default defaultAnswerBox + getAnswer -- (formerly sendData) get student answer from applet and place in answerBox + returnFieldName -- (deprecated) synonmym for answerBox =cut @@ -206,7 +207,7 @@ initializeActionAlias => 'setXML', submitActionAlias => 'getXML', submitActionScript => '', # script executed on submitting the WW question - returnFieldName => 'receivedField', + answerBox => 'answerBox', headerText => DEFAULT_HEADER_TEXT(), objectText => '', debug => 0, @@ -282,8 +283,13 @@ } sub returnFieldName { my $self = shift; - $self->{returnFieldName} = shift ||$self->{returnFieldName}; # replace the current contents if non-empty - $self->{returnFieldName}; + $self->{answerBox} = shift ||$self->{answerBox}; # replace the current contents if non-empty + $self->{answerBox}; +} +sub answerBox { + my $self = shift; + $self->{answerBox} = shift ||$self->{answerBox}; # replace the current contents if non-empty + $self->{answerBox}; } sub codebase { my $self = shift; @@ -368,8 +374,7 @@ my $base64_config = $self->base64_config; my $debugMode = ($self->debug) ? "1": "0"; my $returnFieldName = $self->{returnFieldName}; -# my $encodeStateQ = ($self->debug)?'' : "state = Base64.encode(state);"; # in debug mode base64 encoding is not used. -# my $decodeStateQ = "if (!state.match(/<XML>*/i) ) {state = Base64.decode(state)}"; # decode if <XML> is not present + my $answerBox = $self->{answerBox}; my $headerText = $self->header(); $headerText =~ s/(\$\w+)/$1/gee; # interpolate variables p17 of Cookbook @@ -402,26 +407,54 @@ $objectText =~ s/(\$\w+)/$1/gee; return $objectText; } -sub initialize { - my $self = shift; - return q{ - <script> - initializeAction(); - // this should really be done in the <body> tag - </script> - }; - -} +# sub initialize { +# my $self = shift; +# return q{ +# <script> +# initializeAllApplets(); +# // this should really be done in the <body> tag +# </script> +# }; +# +# } ######################################################## # HEADER material for one flash or java applet ######################################################## use constant DEFAULT_HEADER_TEXT =><<'END_HEADER_SCRIPT'; - + <script src="/webwork2_files/js/Base64.js" language="javascript"> + </script> + <script src="/webwork2_files/js/ww_applet_support.js"> + //upload functions stored in /opt/webwork/webwork2/htdocs/js ... + </script> <script language="JavaScript"> // set debug mode for this applet set_debug($debugMode); + + ////////////////////////////////////////////////////////// + //TEST code + // + // + ////////////////////////////////////////////////////////// + + ww_applet_list["$appletName"] = new ww_applet("$appletName"); + + + ww_applet_list["$appletName"].code = "$code"; + ww_applet_list["$appletName"].codebase = "$codebase"; + ww_applet_list["$appletName"].appletID = "$appletID"; + ww_applet_list["$appletName"].base64_state = "$base64_initializationState"; + ww_applet_list["$appletName"].base64_config = "$base64_config"; + ww_applet_list["$appletName"].getStateAlias = "$getState"; + ww_applet_list["$appletName"].setStateAlias = "$setState"; + ww_applet_list["$appletName"].configAlias = "$config"; + ww_applet_list["$appletName"].initializeActionAlias = "$initializeAction"; + ww_applet_list["$appletName"].submitActionAlias = "$submitAction"; + ww_applet_list["$appletName"].submitActionScript = "$submitActionScript"; + ww_applet_list["$appletName"].answerBox = "$answerBox"; + ww_applet_list["$appletName"].debug = "$debugMode"; + ////////////////////////////////////////////////////////// //CONFIGURATIONS @@ -429,119 +462,119 @@ // configurations are "permanent" ////////////////////////////////////////////////////////// - applet_config_list["$appletName"] = function() { - debug_add("applet_config_list:\n attempt to configure $appletName . $config ( $base64_config ) if config function is defined: " - ); - try { - if (( typeof(getApplet("$appletName").$config) == "function" ) ) { - debug_add("CONFIGURE $appletName"); - getApplet("$appletName").$config(Base64.decode("$base64_config")); - } - } catch(e) { - alert("Error executing configuration command $config for $appletName: " + e ); - } - } - //////////////////////////////////////////////////////////// - // - //STATE: - // state can vary as the applet is manipulated -- it is reset from the questions _state values - // - ////////////////////////////////////////////////////////// - - applet_setState_list["$appletName"] = function(state) { - debug_add("Begin setState for applet $appletName"); - debug_add("Obtain state from $appletName"+"_state"); - state = state || getQE("$appletName"+"_state").value; - if ( base64Q(state) ) { - state=Base64.decode(state); - } - if (state.match(/<xml/i) || state.match(/<?xml/i) ) { // if state starts with <?xml - - debug_add("applet_setState_list: \n set (decoded) state for $appletName to " + - state +"\nfunction type is " +typeof(getApplet("$appletName").$setState) - ); - try { - if (( typeof(getApplet("$appletName").$setState) =="function" ) ) { - debug_add("setState for $appletName"); - getApplet("$appletName").$setState( state ); - } - } catch(e) { - alert("Error in setting state of $appletName using command $setState : " + e ); - } - } else if (debug) { - alert("new state was empty string or did not begin with <xml-- state was not reset"); - } - }; - applet_getState_list["$appletName"] = function () { - debug_add("get current state for applet $appletName and store it in $appletName"+"_state"); - var applet = getApplet("$appletName"); - try { - if (( typeof(applet.$getState) == "function" ) ) { // there may be no state function - state = applet.$getState(); // get state in xml format - debug_add("state has type " + typeof(state)); - state = String(state); // geogebra returned an object type instead of a string type - debug_add("state converted to type " + typeof(state)); - } - - if (!debug) { - state = Base64.encode(state); - }; // replace state by encoded version unless in debug mode - - debug_add("state is "+state); // this should still be in plain text - getQE("$appletName"+"_state").value = state; //place state in input item (debug: textarea, otherwise: hidden) - } catch (e) { - alert("Error in getting state for $appletName " + e ); - } - }; - - //////////////////////////////////////////////////////////// - // - //INITIALIZE - // - //////////////////////////////////////////////////////////// - - - applet_checkLoaded_list["$appletName"] = function() { // this function returns 0 unless: - // applet has already been flagged as ready in applet_isReady_list - // applet.config is defined (or alias for .config) - // applet.setState is defined - // applet.isActive is defined - // applet reported that it is loaded by calling loadQ() - var ready = 0; - var applet = getApplet("$appletName"); - if (!debug && applet_isReady_list["$appletName"]) {return(1)}; // memorize readiness in non-debug mode - if ( typeof(applet.$config) == "function") { - debug_add( "applet.config is " + typeof(applet.$config) ); - ready = 1; - } - if( typeof(applet.$getState) == "function") { - debug_add( "applet.getState is " + typeof(applet.$getState) ); - ready =1; - } - if (typeof(applet.isActive) == "function" && applet.isActive ) { - debug_add( "applet.isActive is " + typeof(applet.isActive) ); - ready =1; - } - if (typeof(applet_reportsLoaded_list["$appletName"]) !="undefined" && applet_reportsLoaded_list["$appletName"] != 0 ) { - debug_add( "applet reports that it is loaded " + applet_reportsLoaded_list["$appletName"] ); - ready =1; - } - applet_isReady_list["$appletName"]= ready; - return(ready); - } - - applet_initializeAction_list["$appletName"] = function (state) { - applet_setState_list["$appletName"](state); - }; - - applet_submitAction_list["$appletName"] = function () { - if (! applet_isReady_list["$appletName"] ) { - alert("$appletName is not ready"); - } - applet_getState_list["$appletName"](); - $submitActionScript - //getQE("$returnFieldName").value = getApplet("$appletName").sendData(); //FIXME -- not needed in general? - }; +// applet_config_list["$appletName"] = function() { +// debug_add("applet_config_list:\n attempt to configure $appletName . $config ( $base64_config ) if config function is defined: " +// ); +// try { +// if (( typeof(getApplet("$appletName").$config) == "function" ) ) { +// debug_add("CONFIGURE $appletName"); +// getApplet("$appletName").$config(Base64.decode("$base64_config")); +// } +// } catch(e) { +// alert("Error executing configuration command $config for $appletName: " + e ); +// } +// } +// //////////////////////////////////////////////////////////// +// // +// //STATE: +// // state can vary as the applet is manipulated -- it is reset from the questions _state values +// // +// ////////////////////////////////////////////////////////// +// +// applet_setState_list["$appletName"] = function(state) { +// debug_add("Begin setState for applet $appletName"); +// debug_add("Obtain state from $appletName"+"_state"); +// state = state || getQE("$appletName"+"_state").value; +// if ( base64Q(state) ) { +// state=Base64.decode(state); +// } +// if (state.match(/<xml/i) || state.match(/<?xml/i) ) { // if state starts with <?xml +// +// debug_add("applet_setState_list: \n set (decoded) state for $appletName to " + +// state +"\nfunction type is " +typeof(getApplet("$appletName").$setState) +// ); +// try { +// if (( typeof(getApplet("$appletName").$setState) =="function" ) ) { +// debug_add("setState for $appletName"); +// getApplet("$appletName").$setState( state ); +// } +// } catch(e) { +// alert("Error in setting state of $appletName using command $setState : " + e ); +// } +// } else if (debug) { +// alert("new state was empty string or did not begin with <xml-- state was not reset"); +// } +// }; +// applet_getState_list["$appletName"] = function () { +// debug_add("get current state for applet $appletName and store it in $appletName"+"_state"); +// var applet = getApplet("$appletName"); +// try { +// if (( typeof(applet.$getState) == "function" ) ) { // there may be no state function +// state = applet.$getState(); // get state in xml format +// debug_add("state has type " + typeof(state)); +// state = String(state); // geogebra returned an object type instead of a string type +// debug_add("state converted to type " + typeof(state)); +// } +// +// if (!debug) { +// state = Base64.encode(state); +// }; // replace state by encoded version unless in debug mode +// +// debug_add("state is "+state); // this should still be in plain text +// getQE("$appletName"+"_state").value = state; //place state in input item (debug: textarea, otherwise: hidden) +// } catch (e) { +// alert("Error in getting state for $appletName " + e ); +// } +// }; +// +// //////////////////////////////////////////////////////////// +// // +// //INITIALIZE +// // +// //////////////////////////////////////////////////////////// +// +// +// applet_checkLoaded_list["$appletName"] = function() { // this function returns 0 unless: +// // applet has already been flagged as ready in applet_isReady_list +// // applet.config is defined (or alias for .config) +// // applet.setState is defined +// // applet.isActive is defined +// // applet reported that it is loaded by calling loadQ() +// var ready = 0; +// var applet = getApplet("$appletName"); +// if (!debug && applet_isReady_list["$appletName"]) {return(1)}; // memorize readiness in non-debug mode +// if ( typeof(applet.$config) == "function") { +// debug_add( "applet.config is " + typeof(applet.$config) ); +// ready = 1; +// } +// if( typeof(applet.$getState) == "function") { +// debug_add( "applet.getState is " + typeof(applet.$getState) ); +// ready =1; +// } +// if (typeof(applet.isActive) == "function" && applet.isActive ) { +// debug_add( "applet.isActive is " + typeof(applet.isActive) ); +// ready =1; +// } +// if (typeof(applet_reportsLoaded_list["$appletName"]) !="undefined" && applet_reportsLoaded_list["$appletName"] != 0 ) { +// debug_add( "applet reports that it is loaded " + applet_reportsLoaded_list["$appletName"] ); +// ready =1; +// } +// applet_isReady_list["$appletName"]= ready; +// return(ready); +// } +// +// applet_initializeAction_list["$appletName"] = function (state) { +// applet_setState_list["$appletName"](state); +// }; +// +// applet_submitAction_list["$appletName"] = function () { +// if (! applet_isReady_list["$appletName"] ) { +// alert("$appletName is not ready"); +// } +// applet_getState_list["$appletName"](); +// $submitActionScript +// //getQE("$answerBox").value = getApplet("$appletName").getAnswer(); //FIXME -- not needed in general? +// }; </script> END_HEADER_SCRIPT |
From: Mike G. v. a. <we...@ma...> - 2009-02-07 22:37:33
|
Log Message: ----------- Reworked applet support code to use more object oriented approach. Modified Files: -------------- pg/macros: AppletObjects.pl Revision Data ------------- Index: AppletObjects.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/AppletObjects.pl,v retrieving revision 1.12 retrieving revision 1.13 diff -Lmacros/AppletObjects.pl -Lmacros/AppletObjects.pl -u -r1.12 -r1.13 --- macros/AppletObjects.pl +++ macros/AppletObjects.pl @@ -47,175 +47,10 @@ <script src="/webwork2_files/applets/AC_RunActiveContent.js" language="javascript"> </script> <script src="/webwork2_files/js/Base64.js" language="javascript"> - </script> - -<script language="JavaScript"> - -////////////////////////////////////////////////////////// -// applet lists -////////////////////////////////////////////////////////// - - var applet_initializeAction_list = new Object; // functions for initializing question with an applet - var applet_submitAction_list = new Object; // functions for submitting question with applet - var applet_setState_list = new Object; // functions for setting state (XML) from applets - var applet_getState_list = new Object; // functions for getting state (XML) from applets - var applet_config_list = new Object; // functions for configuring on applets - var applet_checkLoaded_list = new Object; // functions for probing the applet to see if it is loaded - var applet_reportsLoaded_list = new Object; // flag set by applet - var applet_isReady_list = new Object; // flag set by javaScript in checkLoaded - -////////////////////////////////////////////////////////// -// DEBUGGING tools -////////////////////////////////////////////////////////// - var debug; - var debugText = ""; - function set_debug(num) { // setting debug for any applet sets it for all of them - if (num) { - debug =1; - } - } - function debug_add(str) { - if (debug) { - debugText = debugText + "\n\n" +str; - } - } - -////////////////////////////////////////////////////////// -// INITIALIZE and SUBMIT actions -////////////////////////////////////////////////////////// - - function submitAction() { - - if (debug) { - debugText = " Begin looping through applet_submitAction_list\n"; - } - for (var applet in applet_submitAction_list) { - - applet_submitAction_list[applet](); - } - if (debug) { - alert(debugText); debugText=""; - }; - } - function initializeAction() { - var iMax = 10; - debugText="start intializeAction() with up to " +iMax + " attempts\n"; - for (var appletName in applet_initializeAction_list) { - safe_applet_initialize(appletName, iMax); - } - - } - - // applet can set isReady flag by calling applet_loaded(appletName, loaded); - function applet_loaded(appletName,loaded) { - applet_reportsLoaded_list[appletName] = loaded; // 0 means not loaded - debug_add("applet reporting that it has been loaded = " + loaded ); - } - - // insures that applet is loaded before initializing it - function safe_applet_initialize(appletName, i) { - debug_add("Iteration " + i + " of safe_applet_initialize with applet " + appletName ); - - i--; - var applet_loaded = applet_checkLoaded_list[appletName](); - debug_add("applet is ready = " + applet_loaded ); - - if ( 0 < i && !applet_loaded ) { // wait until applet is loaded - debug_add("applet " + appletName + "not ready try again"); - window.setTimeout( "safe_applet_initialize(\"" + appletName + "\"," + i + ")",1); - } else if( 0 < i ){ // now that applet is loaded configure it and initialize it with saved data. - debug_add(" Ready to initialize applet " + appletName + " with " + i + " iterations left. "); - - // in-line handler -- configure and initialize - try{ - if (debug && typeof(getApplet(appletName).debug) == "function" ) { - getApplet(appletName).debug(1); // turn the applet's debug functions on. - } - } catch(e) { - alert("Unable to set debug mode for applet " + appletName); - } - try{ - applet_config_list[appletName](); - } catch(e) { - alert("Unable to configure " + appletName + " \n " +e ); - } - try{ - applet_initializeAction_list[appletName](); - } catch(e) { - alert("unable to initialize " + appletName + " \n " +e ); - } - - } else { - if (debug) {alert("Error: timed out waiting for applet " +appletName + " to load");} - } - if (debug) {alert(debugText); debugText="";}; - } - -/////////////////////////////////////////////////////// -// Utility functions -/////////////////////////////////////////////////////// - - - function getApplet(appletName) { - var isIE = navigator.appName.indexOf("Microsoft") != -1; - var obj = (isIE) ? window[appletName] : window.document[appletName]; - //return window.document[appletName]; - if (obj && (obj.name = appletName)) { - return( obj ); - } else { - // alert ("can't find applet " + appletName); - } - } - - function listQuestionElements() { // list all HTML input and textarea elements in main problem form - var isIE = navigator.appName.indexOf("Microsoft") != -1; - var elementList = (isIE) ? document.getElementsByTagName("input") : document.problemMainForm.getElementsByTagName("input"); - var str=elementList.length +" Question Elements\n type | name = value < id > \n"; - for( var i=0; i< elementList.length; i++) { - str = str + " "+i+" " + elementList[i].type - + " | " + elementList[i].name - + "= " + elementList[i].value + - " <" + elementList[i].id + ">\n"; - } - elementList = (isIE) ? document.getElementsByTagName("textarea") : document.problemMainForm.getElementsByTagName("textarea"); - for( var i=0; i< elementList.length; i++) { - str = str + " "+i+" " + elementList[i].type - + " | " + elementList[i].name - + "= " + elementList[i].value + - " <" + elementList[i].id + ">\n"; - } - alert(str +"\n Place listQuestionElements() at end of document in order to get all form elements!"); - } - - function base64Q(str) { - return ( !str.match(/<XML/i) && !str.match(/<?xml/i)); - } - function setEmptyState(appletName){ - var newState = "<xml></xml>"; - applet_setState_list[appletName](newState); - var applet = getApplet(appletName); - getQE(appletName+"_state").value = newState; - getQE("previous_" + appletName + "_state").value = newState - } - - function getQE(name1) { // get Question Element in problemMainForm by name - var isIE = navigator.appName.indexOf("Microsoft") != -1; - var obj = (isIE) ? document.getElementById(name1) - :document.problemMainForm[name1]; - // needed for IE -- searches id and name space so it can be unreliable if names are not unique - if (!obj || obj.name != name1) { - alert("Can't find element " + name1); - listQuestionElements(); - } else { - return( obj ); - } - - } - function getQuestionElement(name1) { - return getQE(name1); - } - - </script> + </script> + <script src="/webwork2_files/js/ww_applet_support.js"> + //upload functions stored in /opt/webwork/webwork2/htdocs/js ... + </script> END_HEADER_TEXT @@ -285,29 +120,46 @@ main::RECORD_FORM_LABEL($appletStateName); #this insures that they'll be saved from one invocation to the next #main::RECORD_FORM_LABEL("previous_$appletStateName"); my $answer_value = ''; - $answer_value = ${$main::inputs_ref}{$appletStateName} if defined(${$main::inputs_ref}{$appletStateName}); - - if ( defined( $main::rh_sticky_answers->{$appletStateName} ) ) { + + if ( defined( ${$main::inputs_ref}{$appletStateName} ) and ${$main::inputs_ref}{$appletStateName} =~ /\S/ ) { + $answer_value = ${$main::inputs_ref}{$appletStateName}; + } elsif ( defined( $main::rh_sticky_answers->{$appletStateName} ) ) { + warn "type of sticky answers is ", ref( $main::rh_sticky_answers->{$appletStateName} ); $answer_value = shift( @{ $main::rh_sticky_answers->{$appletStateName} }); - $answer_value = '' unless defined($answer_value); } $answer_value =~ tr/\\$@`//d; #`## make sure student answers can not be interpolated by e.g. EV3 $answer_value =~ s/\s+/ /g; ## remove excessive whitespace from student answer - ####### # insert a hidden variable to hold the applet's state (debug =>1 makes it visible for debugging and provides debugging buttons) ####### - my $base_64_encoded_answer_value = ($answer_value =~/<XML|<?xml/i)? encode_base64($answer_value) : $answer_value; + my $base_64_encoded_answer_value; + my $decoded_answer_value; + if ( $answer_value =~/<XML|<?xml/i) { + $base_64_encoded_answer_value = encode_base64($answer_value); + $decoded_answer_value = $answer_value; + } else { + $decoded_answer_value = decode_base64($answer_value); + if ( $decoded_answer_value =~/<XML|<?xml/i) { # great, we've decoded the answer to obtain an xml string + $base_64_encoded_answer_value = $answer_value; + } else { #WTF?? apparently we don't have XML tags + $answer_value = "<xml>$answer_value</xml>"; + $base_64_encoded_answer_value = encode_base64($answer_value); + $decoded_answer_value = $answer_value; + } + } $base_64_encoded_answer_value =~ s/\r|\n//g; # get rid of line returns - my $decoded_answer_value = ($answer_value =~/<XML|<?xml/i) ? $answer_value : decode_base64($answer_value); + # debug version of the applet state answerBox and controls my $debug_input_element = qq!\n<textarea rows="4" cols="80" name = "$appletStateName">$decoded_answer_value</textarea><br/> <input type="button" value="$getState" - onClick="applet_getState_list['$appletName']()" + onClick="debugText=''; + ww_applet_list['$appletName'].getState(); + alert(debugText);" > <input type="button" value="$setState" - onClick="var tmp = getQE('$appletStateName').value; - applet_setState_list['$appletName'](tmp);" + onClick="debugText=''; + ww_applet_list['$appletName'].setState(); + alert(debugText);" > !; my $state_input_element = ($self->debug == 1) ? $debug_input_element : @@ -323,6 +175,10 @@ $reset_button_str. $state_input_element. qq!<input type="hidden" name="previous_$appletStateName" value = "$base_64_encoded_answer_value">!; + $state_storage_html_code = qq!<input type="hidden" name="previous_$appletStateName" value = "$base_64_encoded_answer_value">! + . $reset_button_str + . $state_input_element + ; ####### # insert header material ####### |
From: dpvc v. a. <we...@ma...> - 2009-02-05 15:11:37
|
Log Message: ----------- Removed unused variable Modified Files: -------------- pg/lib/Parser: Value.pm Revision Data ------------- Index: Value.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Value.pm,v retrieving revision 1.22 retrieving revision 1.23 diff -Llib/Parser/Value.pm -Llib/Parser/Value.pm -u -r1.22 -r1.23 --- lib/Parser/Value.pm +++ lib/Parser/Value.pm @@ -46,8 +46,7 @@ # Set flags for the object # sub check { - my $self = shift; - my $type = $self->{type}; my $value = $self->{value}; + my $self = shift; my $value = $self->{value}; $self->{isZero} = $value->isZero; $self->{isOne} = $value->isOne; } |
From: dpvc v. a. <we...@ma...> - 2009-02-05 15:08:53
|
Log Message: ----------- Changed wording of a warning message Modified Files: -------------- pg/macros: contextLimitedPolynomial.pl Revision Data ------------- Index: contextLimitedPolynomial.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextLimitedPolynomial.pl,v retrieving revision 1.21 retrieving revision 1.22 diff -Lmacros/contextLimitedPolynomial.pl -Lmacros/contextLimitedPolynomial.pl -u -r1.21 -r1.22 --- macros/contextLimitedPolynomial.pl +++ macros/contextLimitedPolynomial.pl @@ -234,7 +234,7 @@ sub checkStrict { my $self = shift; - $self->Error("You can only use '%s' between a coefficent and a variable in a polynomial",$self->{bop}); + $self->Error("You can only use '%s' between coefficents and variables in a polynomial",$self->{bop}); } ############################################## |
From: dpvc v. a. <we...@ma...> - 2009-02-05 15:07:43
|
Log Message: ----------- Fix inheritance to copy the tree rather than just the pointer. This makes sure that the nodes point to the proper equation. In the routine for adapting to parameters, check that the coefficient matrix isn't singular, and try new random points if it is. Modified Files: -------------- pg/lib/Value: Formula.pm Revision Data ------------- Index: Formula.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Formula.pm,v retrieving revision 1.63 retrieving revision 1.64 diff -Llib/Value/Formula.pm -Llib/Value/Formula.pm -u -r1.63 -r1.64 --- lib/Value/Formula.pm +++ lib/Value/Formula.pm @@ -188,7 +188,7 @@ # situations where this is a problem. # if ($l->AdaptParameters($r,$self->{context}->variables->parameters)) { - my $avalues = $l->{test_adapt}; + my $avalues = $l->{test_adapt}; my $tolerance = $self->getFlag('tolerance',1E-4); my $isRelative = $self->getFlag('tolType','relative') eq 'relative'; my $zeroLevel = $self->getFlag('zeroLevel',1E-14); @@ -220,6 +220,17 @@ } # +# Inherit should make sure the tree is copied +# (so it's nodes point to the correct equation, for one thing) +# +sub inherit { + my $self = shift; + $self = $self->SUPER::inherit(@_); + $self->{tree} = $self->{tree}->copy($self); + return $self; +} + +# # Don't inherit test values or adapted values, or other temporary items # sub noinherit { @@ -463,27 +474,29 @@ my $B = MatrixReal1->new($d,1); $B->[0] = \@b; ($M,$B) = $M->normalize($B); $M = $M->decompose_LR; - if (($D,$B,$M) = $M->solve_LR($B)) { - if ($D == 0) { - # - # Get parameter values and recompute the points using them - # - my @a; my $i = 0; my $max = $l->getFlag('max_adapt',1E8); - foreach my $row (@{$B->[0]}) { - if (abs($row->[0]) > $max) { - $max = Value::makeValue($max); $row->[0] = Value::makeValue($row->[0]); - $l->Error(["Constant of integration is too large: %s\n(maximum allowed is %s)", - $row->[0]->string,$max->string]) if $params[$i] eq 'C0' or $params[$i] eq 'n00'; - $l->Error(["Adaptive constant is too large: %s = %s\n(maximum allowed is %s)", - $params[$i],$row->[0]->string,$max->string]); + if (abs($M->det_LR) > 1E-6) { + if (($D,$B,$M) = $M->solve_LR($B)) { + if ($D == 0) { + # + # Get parameter values and recompute the points using them + # + my @a; my $i = 0; my $max = $l->getFlag('max_adapt',1E8); + foreach my $row (@{$B->[0]}) { + if (abs($row->[0]) > $max) { + $max = Value::makeValue($max); $row->[0] = Value::makeValue($row->[0]); + $l->Error(["Constant of integration is too large: %s\n(maximum allowed is %s)", + $row->[0]->string,$max->string]) if $params[$i] eq 'C0' or $params[$i] eq 'n00'; + $l->Error(["Adaptive constant is too large: %s = %s\n(maximum allowed is %s)", + $params[$i],$row->[0]->string,$max->string]); + } + push @a, $row->[0]; $i++; } - push @a, $row->[0]; $i++; - } - my $context = $l->context; - foreach my $i (0..$#a) {$context->{variables}{$params[$i]}{value} = $a[$i]} - $l->{parameters} = [@a]; - $l->createAdaptedValues; - return 1; + my $context = $l->context; + foreach my $i (0..$#a) {$context->{variables}{$params[$i]}{value} = $a[$i]} + $l->{parameters} = [@a]; + $l->createAdaptedValues; + return 1; + } } } } |
From: dpvc v. a. <we...@ma...> - 2009-02-05 15:05:21
|
Log Message: ----------- Minor changes Modified Files: -------------- pg/lib/Value: AnswerChecker.pm Revision Data ------------- Index: AnswerChecker.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/AnswerChecker.pm,v retrieving revision 1.123 retrieving revision 1.124 diff -Llib/Value/AnswerChecker.pm -Llib/Value/AnswerChecker.pm -u -r1.123 -r1.124 --- lib/Value/AnswerChecker.pm +++ lib/Value/AnswerChecker.pm @@ -30,9 +30,9 @@ # Internal use. # Set default flags for the answer checker in this object -# showTypeWarnings => 1 -# showEqualErrors => 1 -# ignoreStrings => 1 +# showTypeWarnings => 1 +# showEqualErrors => 1 +# ignoreStrings => 1 # studentsMustReduceUnions => 1 # showUnionReduceWarnings => 1 # @@ -241,6 +241,7 @@ return eval {$self == $other} unless ref($ans->{checker}) eq 'CODE'; my @equal = eval {&{$ans->{checker}}($self,$other,$ans,$nth,@_)}; if (!defined($equal) && $@ ne '' && (!$context->{error}{flag} || $ans->{showAllErrors})) { + $nth = "" if ref($nth) eq 'AnswerHash'; $context->setError(["<I>An error occurred while checking your$nth answer:</I>\n". '<DIV STYLE="margin-left:1em">%s</DIV>',$@],'',undef,undef,$CMP_ERROR); warn "Please inform your instructor that an error occurred while checking your answer"; @@ -1785,7 +1786,7 @@ my @P = (map {(scalar(@{$_}) == 1)? $_->[0]: $self->Package("Point")->make(@{$_})} @{$self->{test_points}}); my @i = sort {$P[$a] <=> $P[$b]} (0..$#P); foreach $p (@P) {if (Value::isValue($p) && $p->length > 2) {$p = $p->string; $p =~ s|,|,<br />|g}} - my $zeroLevelTol = $self->getFlag('zeroLevelTol'); + my $zeroLevelTol = $self->{context}{flags}{zeroLevelTol}; $self->{context}{flags}{zeroLevelTol} = 0; # always show full resolution in the tables below my $names = join(',',@names); $names = '('.$names.')' if scalar(@names) > 1; |
From: dpvc v. a. <we...@ma...> - 2009-02-05 15:03:55
|
Log Message: ----------- Fix inheritance so that text_values and test_adapt are preserved when transferring to the stored copy of the adapted function. (These are needed for the diagnostics output.) Use new Parser::Eval to avoid ugliness in trying to trap errors in comparisons. Modified Files: -------------- pg/macros: parserFormulaUpToConstant.pl Revision Data ------------- Index: parserFormulaUpToConstant.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserFormulaUpToConstant.pl,v retrieving revision 1.19 retrieving revision 1.20 diff -Lmacros/parserFormulaUpToConstant.pl -Lmacros/parserFormulaUpToConstant.pl -u -r1.19 -r1.20 --- macros/parserFormulaUpToConstant.pl +++ macros/parserFormulaUpToConstant.pl @@ -173,12 +173,16 @@ # If constants aren't the same, substitute the professor's in the student answer. # $r = $r->substitute($r->{constant}=>$l->{constant}) unless $r->{constant} eq $l->{constant}; + # # Compare with adaptive parameters to see if $l + n0 C = $r for some n0. # my $adapt = $l->adapt; - my $equal = $adapt->cmp_compare($r,{}); + my $equal = Parser::Eval(sub {$adapt == $r}); $self->{adapt} = $self->{adapt}->inherit($adapt); # save the adapted value's flags + $self->{adapt}{test_values} = $adapt->{test_values}; # (these two are removed by inherit) + $self->{adapt}{test_adapt} = $adapt->{test_adapt}; + $_[1]->{test_values} = $r->{test_values}; # save these in student answer for diagnostics return -1 unless $equal; # # Check that n0 is non-zero (i.e., there is a multiple of C in the student answer) @@ -192,7 +196,6 @@ # sub adapt { my $self = shift; - my $adapt = $self->{adapt}->inherit($self); delete $adapt->{adapt}; return $self->adjustInherit($self->{adapt}); } @@ -262,7 +265,10 @@ # sub cmp_diagnostics { my $self = shift; - $self->inherit($self->{adapt})->SUPER::cmp_diagnostics(@_); + my $adapt = $self->inherit($self->{adapt}); + $adapt->{test_values} = $self->{adapt}{test_values}; # these aren't copied by inherit + $adapt->{test_adapt} = $self->{adapt}{test_adapt}; + $adapt->SUPER::cmp_diagnostics(@_); } # @@ -289,9 +295,7 @@ return unless $ans->{score} == 0 && !$ans->{isPreview}; return if $ans->{ans_message} || !$self->getFlag("showHints"); my $student = $ans->{student_value}; - $main::{_cmp_} = sub {return $ans->{correct_value} <=> $student}; # compare encodes the reason in the result - my $result = main::PG_restricted_eval('&{$main::{_cmp_}}'); - delete $main::{_cmp_}; + my $result = Parser::Eval(sub {return $ans->{correct_value} <=> $student}); # compare encodes the reason in the result $self->cmp_Error($ans,"Note: there is always more than one posibility") if $result == 2 || $result == 3; if ($result == 3) { my $context = $self->context; |
From: jj v. a. <we...@ma...> - 2009-02-04 15:31:07
|
Log Message: ----------- Fix the sorting of days and weeks for output. Modified Files: -------------- webwork2/bin: remove_stale_images Revision Data ------------- Index: remove_stale_images =================================================================== RCS file: /webwork/cvs/system/webwork2/bin/remove_stale_images,v retrieving revision 1.7 retrieving revision 1.8 diff -Lbin/remove_stale_images -Lbin/remove_stale_images -u -r1.7 -r1.8 --- bin/remove_stale_images +++ bin/remove_stale_images @@ -197,12 +197,12 @@ sub count_report { my $j; print "Days\n"; - for $j (sort keys(%days)) { + for $j (sort {$a <=> $b} keys(%days)) { print "$j $days{$j}\n"; } print "\nWeeks\n"; - for $j (sort keys(%week)) { + for $j (sort {$a <=> $b} keys(%week)) { print " $j $week{$j}\n"; } @@ -284,4 +284,4 @@ -1; \ No newline at end of file +1; |
From: Mike G. v. a. <we...@ma...> - 2009-02-04 03:25:44
|
Log Message: ----------- Added code to check the directory structure of the courses (make sure all standard directories are present and have rwx privileges available to the webserver. No automatic repair of the directory structure is performed. (We'll see how annoying that is.) Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: CourseAdmin.pm webwork2/lib/WeBWorK/Utils: CourseIntegrityCheck.pm Revision Data ------------- Index: CourseAdmin.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/CourseAdmin.pm,v retrieving revision 1.81 retrieving revision 1.82 diff -Llib/WeBWorK/ContentGenerator/CourseAdmin.pm -Llib/WeBWorK/ContentGenerator/CourseAdmin.pm -u -r1.81 -r1.82 --- lib/WeBWorK/ContentGenerator/CourseAdmin.pm +++ lib/WeBWorK/ContentGenerator/CourseAdmin.pm @@ -383,13 +383,14 @@ }); my $CIchecker = new WeBWorK::Utils::CourseIntegrityCheck(ce=>$tempCE); - my ($tables_ok,$dbStatus) = $CIchecker->checkCourseTables($courseID); + my ($tables_ok,$dbStatus) = $CIchecker->checkCourseTables($courseID); + my ($directories_ok, $str2) = $CIchecker->checkCourseDirectories(); print CGI::li(CGI::a({href=>$self->systemLink($urlpath, authen => 0)}, $courseID), CGI::code( $tempCE->{dbLayoutName}, ), - (-r $tempCE->{courseFiles}->{environment}) ? "" : CGI::i(", missing course.conf"), - ($courseID eq "modelCourse" or $tables_ok ) ? CGI::span({style=>"color:green"},"Database tables ok") : CGI::span({style=>"color:red"},"Database tables need updating"), + ($courseID eq "modelCourse" or $directories_ok) ? "" : CGI::span({style=>"color:red"},"Directory structure or permissions need to be repaired. "), + ($courseID eq "modelCourse" or $tables_ok ) ? CGI::span({style=>"color:green"},"Database tables ok") : CGI::span({style=>"color:red"},"Database tables need updating."), ); @@ -908,9 +909,11 @@ %WeBWorK::SeedCE, courseName => $rename_oldCourseID, }); +############################################################################# +# Check database +############################################################################# my ($tables_ok,$dbStatus); - my %missing_fields; if ($ce2->{dbLayoutName} ) { my $CIchecker = new WeBWorK::Utils::CourseIntegrityCheck(ce=>$ce2); ($tables_ok,$dbStatus) = $CIchecker->checkCourseTables($rename_oldCourseID); @@ -981,6 +984,10 @@ $str.=CGI::br(); } +############################################################################# +# Report on databases +############################################################################# + print CGI::p($str); if ($extra_database_tables) { print CGI::p({-style=>'color:red; font-weight:bold'},"There are extra database tables which are not defined in the schema. @@ -989,12 +996,31 @@ if ($extra_database_fields) { print CGI::p({-style=>'color:red; font-weight:bold'},"There are extra database fields which are not defined in the schema for at least one table. They can only be removed manually from the database."); - } + } if ($all_tables_ok) { print CGI::p({-style=>'color:green; font-weight:bold'},"Course $rename_oldCourseID database is in order"); } else { print CGI::p({-style=>'color:red; font-weight:bold'}, "Course $rename_oldCourseID databases must be updated before renaming this course."); - } + } + +############################################################################# +# Check directories +############################################################################# + + + my ($directories_ok, $str2) = $CIchecker->checkCourseDirectories($ce2); + my $style = ($directories_ok)?"color:green" : "color:red"; + print CGI::h2("Directory structure"), CGI::p($str2), + ($directories_ok)? CGI::p({style=>$style},"Directory structure is ok") : + CGI::p({style=>$style},"Directory structure is missing directories + or the webserver lacks sufficient privileges."); + +############################################################################# +# Print form for choosing next action. +############################################################################# + + + print CGI::start_form(-method=>"POST", -action=>$r->uri); print $self->hidden_authen_fields; print $self->hidden_fields("subDisplay"); @@ -1004,18 +1030,23 @@ - if ($all_tables_ok ) { # no missing tables or missing fields + if ($all_tables_ok && $directories_ok ) { # no missing tables or missing fields or directories print CGI::p({style=>"text-align: center"}, CGI::submit(-name=>"decline_rename_course", -value=>"Don't rename"), " ", CGI::submit(-name=>"confirm_rename_course", -value=>"Rename") , ); - } else { + } elsif( $directories_ok ) { print CGI::p({style=>"text-align: center"}, CGI::submit(-name => "decline_rename_course", -value => "Don't rename"), " ", CGI::submit(-name=>"upgrade_course_tables", -value=>"upgrade course tables"), ); + } else { + print CGI::p({style=>"text-align: center"}, + CGI::submit(-name => "decline_rename_course", -value => "Don't rename"), + CGI::br(),"Directory structure needs to be repaired manually before renaming." + ); } } } @@ -1798,7 +1829,9 @@ my ($tables_ok,$dbStatus); - +############################################################################# +# Check database +############################################################################# my %missing_fields; if ($ce2->{dbLayoutName} ) { my $CIchecker = new WeBWorK::Utils::CourseIntegrityCheck(ce=>$ce2); @@ -1870,6 +1903,10 @@ $str.=CGI::br(); } +############################################################################# +# Report on databases +############################################################################# + print CGI::p($str); if ($extra_database_tables) { print CGI::p({-style=>'color:red; font-weight:bold'},"There are extra database tables which are not defined in the schema. @@ -1889,6 +1926,25 @@ must be upgraded before archiving this course." ); } +############################################################################# +# Check directories +############################################################################# + + + my ($directories_ok, $str2) = $CIchecker->checkCourseDirectories(); + my $style = ($directories_ok)?"color:green" : "color:red"; + print CGI::h2("Directory structure"), CGI::p($str2), + ($directories_ok)? CGI::p({style=>$style},"Directory structure is ok") : + CGI::p({style=>$style},"Directory structure is missing directories + or the webserver lacks sufficient privileges."); + + + + +############################################################################# +# Print form for choosing next action. +############################################################################# + print CGI::start_form(-method=>"POST", -action=>$r->uri); print $self->hidden_authen_fields; print $self->hidden_fields("subDisplay"); @@ -1901,7 +1957,7 @@ print CGI::p( "$archive_courseID: The directory for the course not found."); } - if ($all_tables_ok ) { # no missing fields + if ($all_tables_ok && $directories_ok ) { # no missing fields # Warn about overwriting an existing archive if (-e $archive_path and -w $archive_path) { print CGI::p({-style=>'color:red; font-weight:bold'},"The course '$archive_courseID' has already been archived at '$archive_path'. @@ -1913,13 +1969,19 @@ " ", CGI::submit(-name=>"confirm_archive_course", -value=>"archive") , ); - } else { + } elsif( $directories_ok) { print CGI::p({style=>"text-align: center"}, CGI::submit(-name => "decline_archive_course", -value => "Don't archive"), " ", CGI::submit(-name=>"upgrade_course_tables", -value=>"upgrade course tables"), ); - } + } else { + print CGI::p({style=>"text-align: center"}, + CGI::submit(-name => "decline_archive_course", -value => "Don't archive"),CGI::br(), + "Directory structure needs to be repaired manually before archiving." + ); + + } print CGI::end_form(); } else { print CGI::p({-style=>'color:red; font-weight:bold'},"Unable to find database layout for $archive_courseID"); Index: CourseIntegrityCheck.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/Utils/CourseIntegrityCheck.pm,v retrieving revision 1.2 retrieving revision 1.3 diff -Llib/WeBWorK/Utils/CourseIntegrityCheck.pm -Llib/WeBWorK/Utils/CourseIntegrityCheck.pm -u -r1.2 -r1.3 --- lib/WeBWorK/Utils/CourseIntegrityCheck.pm +++ lib/WeBWorK/Utils/CourseIntegrityCheck.pm @@ -264,6 +264,48 @@ } + + + + +=item $CIchecker->checkCourseDirectories($courseName); + +Checks the course directories to make sure they exist and have the correct +permissions. + + +=cut + +sub checkCourseDirectories { + my ($self) = @_; + my $ce = $self->{ce}; + my @webworkDirectories = keys %{$ce->{webworkDirs}}; + my @courseDirectories = keys %{$ce->{courseDirs}}; + my $str = ''; + my @results; + my $directories_ok =1; + foreach my $dir (sort @courseDirectories) { + my $path = $ce->{courseDirs}->{$dir}; + my $status = (-e $path) ? + ((-r $path)?'r':'-') . + ((-w _ )?'w':'-' ) . + ((-x _ )?'x':'-' ) : "missing"; + + #all directories should be readable, writable and executable + my $style; + if ($status eq 'rwx') { + $style = "color:green"; + } else { + $directories_ok = 0; + $style = "color:red"; + } + + push @results, CGI::span({style=>$style},"$dir => $path $status <br/>\n"); + } + $str = join(" ",@results); + return ( $directories_ok, $str); +} + ############################################################################## # Database utilities -- borrowed from DBUpgrade.pm ??use or modify??? --MEG ############################################################################## |
From: Mike G. v. a. <we...@ma...> - 2009-02-02 03:27:56
|
Log Message: ----------- Streamlined the course integrity checking Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: CourseAdmin.pm webwork2/lib/WeBWorK/DB/Schema/NewSQL: Std.pm webwork2/lib/WeBWorK/Utils: CourseIntegrityCheck.pm CourseManagement.pm Revision Data ------------- Index: CourseAdmin.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/CourseAdmin.pm,v retrieving revision 1.80 retrieving revision 1.81 diff -Llib/WeBWorK/ContentGenerator/CourseAdmin.pm -Llib/WeBWorK/ContentGenerator/CourseAdmin.pm -u -r1.80 -r1.81 --- lib/WeBWorK/ContentGenerator/CourseAdmin.pm +++ lib/WeBWorK/ContentGenerator/CourseAdmin.pm @@ -381,11 +381,15 @@ %WeBWorK::SeedCE, courseName => $courseID, }); + + my $CIchecker = new WeBWorK::Utils::CourseIntegrityCheck(ce=>$tempCE); + my ($tables_ok,$dbStatus) = $CIchecker->checkCourseTables($courseID); print CGI::li(CGI::a({href=>$self->systemLink($urlpath, authen => 0)}, $courseID), CGI::code( $tempCE->{dbLayoutName}, ), (-r $tempCE->{courseFiles}->{environment}) ? "" : CGI::i(", missing course.conf"), + ($courseID eq "modelCourse" or $tables_ok ) ? CGI::span({style=>"color:green"},"Database tables ok") : CGI::span({style=>"color:red"},"Database tables need updating"), ); @@ -905,68 +909,89 @@ courseName => $rename_oldCourseID, }); - my ($tables_ok,$ok_tables,$schema_only,$database_only,$update_fields); + my ($tables_ok,$dbStatus); my %missing_fields; if ($ce2->{dbLayoutName} ) { - my $CIchecker = new WeBWorK::Utils::CourseIntegrityCheck(ce=>$ce2); - if ($r->param("missing_database_tables")) { - my @table_names = split(/\s+/, $r->param("missing_database_tables") ); - my $msg = $CIchecker->updateCourseTables($rename_oldCourseID, [@table_names]); - print CGI::p({-style=>'color:green; font-weight:bold'}, $msg); - } - ($tables_ok,$ok_tables,$schema_only,$database_only,$update_fields) = $CIchecker->checkCourseTables($rename_oldCourseID); - print CGI::p("Are you sure you want to rename the course " . CGI::b($rename_oldCourseID). " to ".CGI::b($rename_newCourseID) - . "? "); - - print CGI::p({-style=>'color:black; font-weight:bold'},"These schema tables agree with those found in the database:"); - my $str = ''; - foreach my $table (sort keys %$ok_tables) { - $str .= CGI::b($table).CGI::br(); - #$str .= CGI::span( {-style=>'color:gray; font-weight:lighter'},$both->{$table} ); - } - print CGI::p($str); - - # print tables with mismatched fields - my $all_fields_ok = 1; - if (%$update_fields) { - print CGI::p({-style=>'color:black; font-weight:bold'},"The field names for these tables don't - agree with those found in the database. <br/>These fields will need to be repaired by hand by - accessing the database directly."); - $str=''; - foreach my $table (sort keys %$update_fields) { - my ($field_ok, $fields_both, $fields_schema_only, $fields_database_only) = @{$update_fields->{$table}}; - $str .= " missing fields from database table <b>$table</b>: " - . join(", ", map { "<br/> $_ => $$fields_schema_only{$_}" } keys %$fields_schema_only ) - . CGI::br(); - $all_fields_ok = 0 unless $field_ok; + my $CIchecker = new WeBWorK::Utils::CourseIntegrityCheck(ce=>$ce2); + ($tables_ok,$dbStatus) = $CIchecker->checkCourseTables($rename_oldCourseID); + if ($r->param("upgrade_course_tables")) { + my @schema_table_names = keys %$dbStatus; # update tables missing from database; + my @tables_to_create = grep {$dbStatus->{$_}->[0] == WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_A} @schema_table_names; + my @tables_to_alter = grep {$dbStatus->{$_}->[0] == WeBWorK::Utils::CourseIntegrityCheck::DIFFER_IN_A_AND_B} @schema_table_names; + my $msg = $CIchecker->updateCourseTables($rename_oldCourseID, [@tables_to_create]); + foreach my $table_name (@tables_to_alter) { + $msg .= $CIchecker->updateTableFields($rename_oldCourseID, $table_name); } - print CGI::p($str); - - } - - # print tables missing from database - if (%$schema_only) { - print CGI::p({-style=>'color:red; font-weight:bold'}, "These schema tables are missing from the database. - Upgrading the database will create these tables." ); - $str = ''; - foreach my $table (sort keys %$schema_only) { - $str .= CGI::b($table)." missing from database".CGI::br(); - } - print CGI::p($str); + print CGI::p({-style=>'color:green; font-weight:bold'}, $msg); } - - # print tables missing from schema - if (%$database_only) { - print CGI::p({-style=>'color:red; font-weight:bold'}, "These database tables are missing from the schema. - These tables will be created in the database before archiving this course." ); - $str = ''; - foreach my $table (sort keys %$database_only) { - $str .= CGI::b($table)." exists in database but is missing from schema".CGI::br(); + ($tables_ok,$dbStatus) = $CIchecker->checkCourseTables($rename_oldCourseID); + + + # print db status + + my %msg =( WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_A => CGI::span({style=>"color:red"}," Table defined in schema but missing in database"), + WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_B => CGI::span({style=>"color:red"}," Table defined in database but missing in schema"), + WeBWorK::Utils::CourseIntegrityCheck::SAME_IN_A_AND_B => CGI::span({style=>"color:green"}," Table is ok "), + WeBWorK::Utils::CourseIntegrityCheck::DIFFER_IN_A_AND_B => CGI::span({style=>"color:red"}," Schema and database table definitions do not agree "), + ); + my %msg2 =( WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_A => CGI::span({style=>"color:red"}," missing in database"), + WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_B => CGI::span({style=>"color:red"}," missing in schema"), + WeBWorK::Utils::CourseIntegrityCheck::SAME_IN_A_AND_B => CGI::span({style=>"color:green"}," is ok "), + WeBWorK::Utils::CourseIntegrityCheck::DIFFER_IN_A_AND_B => CGI::span({style=>"color:red"}," Schema and database field definitions do not agree "), + ); + my $all_tables_ok=1; + my $extra_database_tables=0; + my $extra_database_fields=0; + my $str=CGI::h4("Report on database structure for course $rename_oldCourseID:").CGI::br(); + foreach my $table (sort keys %$dbStatus) { + my $table_status = $dbStatus->{$table}->[0]; + $str .= CGI::b($table) . $msg{ $table_status } . CGI::br(); + + CASE: { + $table_status == WeBWorK::Utils::CourseIntegrityCheck::SAME_IN_A_AND_B + && do{ last CASE; + }; + $table_status == WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_A + && do{ + $all_tables_ok = 0; last CASE; + }; + $table_status == WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_B + && do{ + $extra_database_tables = 1; last CASE; + }; + $table_status == WeBWorK::Utils::CourseIntegrityCheck::DIFFER_IN_A_AND_B + && do{ + my %fieldInfo = %{ $dbStatus->{$table}->[1] }; + foreach my $key (keys %fieldInfo) { + my $field_status = $fieldInfo{$key}->[0]; + CASE2: { + $field_status == WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_B + && do{ + $extra_database_fields = 1; last CASE2; + }; + $field_status == WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_A + && do{ + $all_tables_ok=0; last CASE2; + }; + } + $str .= CGI::br()."\n Field $key => ". $msg2{$field_status }; + } + }; } - print CGI::p($str); + $str.=CGI::br(); + } - if ($tables_ok) { - print CGI::p({-style=>'color:black; font-weight:bold'},"Course $rename_oldCourseID database is in order"); + print CGI::p($str); + if ($extra_database_tables) { + print CGI::p({-style=>'color:red; font-weight:bold'},"There are extra database tables which are not defined in the schema. + They can only be removed manually from the database. They will not be renamed."); + } + if ($extra_database_fields) { + print CGI::p({-style=>'color:red; font-weight:bold'},"There are extra database fields which are not defined in the schema for at least one table. + They can only be removed manually from the database."); + } + if ($all_tables_ok) { + print CGI::p({-style=>'color:green; font-weight:bold'},"Course $rename_oldCourseID database is in order"); } else { print CGI::p({-style=>'color:red; font-weight:bold'}, "Course $rename_oldCourseID databases must be updated before renaming this course."); } @@ -979,31 +1004,19 @@ - if ($tables_ok and $all_fields_ok ) { # no missing fields + if ($all_tables_ok ) { # no missing tables or missing fields print CGI::p({style=>"text-align: center"}, CGI::submit(-name=>"decline_rename_course", -value=>"Don't rename"), " ", CGI::submit(-name=>"confirm_rename_course", -value=>"Rename") , ); - } elsif ($all_fields_ok) { + } else { print CGI::p({style=>"text-align: center"}, - CGI::hidden(-name => 'missing_database_tables',-value => join(" ",keys %$schema_only)), - CGI::hidden(-name => 'extra_database_tables', -value => join(" ",keys %$database_only) ), - CGI::hidden(-name => 'missing_fields', -value => join(" ", %missing_fields) ), CGI::submit(-name => "decline_rename_course", -value => "Don't rename"), " ", CGI::submit(-name=>"upgrade_course_tables", -value=>"upgrade course tables"), ); - } else { - print CGI::p({style=>"text-align: center"}, - CGI::hidden(-name => 'missing_database_tables',-value => join(" ",keys %$schema_only)), - CGI::hidden(-name => 'extra_database_tables', -value => join(" ",keys %$database_only) ), - CGI::hidden(-name => 'missing_fields', -value => join(" ", %missing_fields) ), - CGI::submit(-name => "decline_rename_course", -value => "Don't rename"), - " ", - # CGI::submit(-name=>"upgrade_course_tables", -value=>"upgrade course tables"), - ); - } + } } } sub rename_course_validate { @@ -1784,73 +1797,97 @@ }); - my ($tables_ok,$ok_tables,$schema_only,$database_only,$update_fields); + my ($tables_ok,$dbStatus); + my %missing_fields; if ($ce2->{dbLayoutName} ) { my $CIchecker = new WeBWorK::Utils::CourseIntegrityCheck(ce=>$ce2); - if ($r->param("missing_database_tables")) { - my @table_names = split(/\s+/, $r->param("missing_database_tables") ); - my $msg = $CIchecker->updateCourseTables($archive_courseID, [@table_names]); - print CGI::p({-style=>'color:green; font-weight:bold'}, $msg); - } - ($tables_ok,$ok_tables,$schema_only,$database_only,$update_fields) = $CIchecker->checkCourseTables($archive_courseID); - print CGI::p("Are you sure you want to archive the course " . CGI::b($archive_courseID) - . "? "); - - print CGI::p({-style=>'color:black; font-weight:bold'},"These schema tables agree with those found in the database:"); - my $str = ''; - foreach my $table (sort keys %$ok_tables) { - $str .= CGI::b($table).CGI::br(); - #$str .= CGI::span( {-style=>'color:gray; font-weight:lighter'},$both->{$table} ); - } - print CGI::p($str); - - # print tables with mismatched fields - my $all_fields_ok = 1; - if (%$update_fields) { - print CGI::p({-style=>'color:black; font-weight:bold'},"The field names for these tables don't - agree with those found in the database. <br/>These fields will need to be repaired by hand by - accessing the database directly."); - $str=''; - foreach my $table (sort keys %$update_fields) { - my ($field_ok, $fields_both, $fields_schema_only, $fields_database_only) = @{$update_fields->{$table}}; - $str .= " missing fields from database table <b>$table</b>: " - . join(", ", map { "<br/> $_ => $$fields_schema_only{$_}" } keys %$fields_schema_only ) - . CGI::br(); - $all_fields_ok = 0 unless $field_ok; + ($tables_ok,$dbStatus) = $CIchecker->checkCourseTables($archive_courseID); + if ($r->param("upgrade_course_tables")) { + my @schema_table_names = keys %$dbStatus; # update tables missing from database; + my @tables_to_create = grep {$dbStatus->{$_}->[0] == WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_A} @schema_table_names; + my @tables_to_alter = grep {$dbStatus->{$_}->[0] == WeBWorK::Utils::CourseIntegrityCheck::DIFFER_IN_A_AND_B} @schema_table_names; + my $msg = $CIchecker->updateCourseTables($archive_courseID, [@tables_to_create]); + foreach my $table_name (@tables_to_alter) { + $msg .= $CIchecker->updateTableFields($archive_courseID, $table_name); } - print CGI::p($str); - - } - - # print tables missing from database - if (%$schema_only) { - print CGI::p({-style=>'color:red; font-weight:bold'}, "These schema tables are missing from the database. - Upgrading the database will create these tables." ); - $str = ''; - foreach my $table (sort keys %$schema_only) { - $str .= CGI::b($table)." missing from database".CGI::br(); - } - print CGI::p($str); + print CGI::p({-style=>'color:green; font-weight:bold'}, $msg); } - - # print tables missing from schema - if (%$database_only) { - print CGI::p({-style=>'color:red; font-weight:bold'}, "These database tables are missing from the schema. - These tables will be created in the database before archiving this course." ); - $str = ''; - foreach my $table (sort keys %$database_only) { - $str .= CGI::b($table)." exists in database but is missing from schema".CGI::br(); + ($tables_ok,$dbStatus) = $CIchecker->checkCourseTables($archive_courseID); + + + # print db status + + my %msg =( WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_A => CGI::span({style=>"color:red"}," Table defined in schema but missing in database"), + WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_B => CGI::span({style=>"color:red"}," Table defined in database but missing in schema"), + WeBWorK::Utils::CourseIntegrityCheck::SAME_IN_A_AND_B => CGI::span({style=>"color:green"}," Table is ok "), + WeBWorK::Utils::CourseIntegrityCheck::DIFFER_IN_A_AND_B => CGI::span({style=>"color:red"}," Schema and database table definitions do not agree "), + ); + my %msg2 =( WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_A => CGI::span({style=>"color:red"}," missing in database"), + WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_B => CGI::span({style=>"color:red"}," missing in schema"), + WeBWorK::Utils::CourseIntegrityCheck::SAME_IN_A_AND_B => CGI::span({style=>"color:green"}," is ok "), + WeBWorK::Utils::CourseIntegrityCheck::DIFFER_IN_A_AND_B => CGI::span({style=>"color:red"}," Schema and database field definitions do not agree "), + ); + my $all_tables_ok=1; + my $extra_database_tables=0; + my $extra_database_fields=0; + my $str=CGI::h4("Report on database structure for course $archive_courseID:").CGI::br(); + foreach my $table (sort keys %$dbStatus) { + my $table_status = $dbStatus->{$table}->[0]; + $str .= CGI::b($table) . $msg{ $table_status } . CGI::br(); + + CASE: { + $table_status == WeBWorK::Utils::CourseIntegrityCheck::SAME_IN_A_AND_B + && do{ last CASE; + }; + $table_status == WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_A + && do{ + $all_tables_ok = 0; last CASE; + }; + $table_status == WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_B + && do{ + $extra_database_tables = 1; last CASE; + }; + $table_status == WeBWorK::Utils::CourseIntegrityCheck::DIFFER_IN_A_AND_B + && do{ + my %fieldInfo = %{ $dbStatus->{$table}->[1] }; + foreach my $key (keys %fieldInfo) { + my $field_status = $fieldInfo{$key}->[0]; + CASE2: { + $field_status == WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_B + && do{ + $extra_database_fields = 1; last CASE2; + }; + $field_status == WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_A + && do{ + $all_tables_ok=0; last CASE2; + }; + } + $str .= CGI::br()."\n Field $key => ". $msg2{$field_status }; + } + }; } - print CGI::p($str); + $str.=CGI::br(); + } - - if ($tables_ok) { - print CGI::p({-style=>'color:black; font-weight:bold'},"Course $archive_courseID database is in order"); + print CGI::p($str); + if ($extra_database_tables) { + print CGI::p({-style=>'color:red; font-weight:bold'},"There are extra database tables which are not defined in the schema. + They can only be removed manually from the database."); + } + if ($extra_database_fields) { + print CGI::p({-style=>'color:red; font-weight:bold'},"There are extra database fields which are not defined in the schema for at least one table. + They can only be removed manually from the database."); + } + if ($all_tables_ok) { + print CGI::p({-style=>'color:green; font-weight:bold'},"Course $archive_courseID database is in order"); print(CGI::p({-style=>'color:red; font-weight:bold'}, "Are you sure that you want to delete the course ". - CGI::b($archive_courseID). " after archiving? This cannot be undone!")) if $delete_course_flag; + CGI::b($archive_courseID). " after archiving? This cannot be undone!")) if $delete_course_flag; } else { - print CGI::p({-style=>'color:red; font-weight:bold'}, "Course $archive_courseID databases must be updated before archiving this course."); + print CGI::p({-style=>'color:red; font-weight:bold'}, "There are tables or fields missing from the + database. The database + must be upgraded before archiving this course." + ); } print CGI::start_form(-method=>"POST", -action=>$r->uri); print $self->hidden_authen_fields; @@ -1864,39 +1901,25 @@ print CGI::p( "$archive_courseID: The directory for the course not found."); } - # fail if a course archive already exists - # FIXME there could be an option to overwrite an existing archive - if (-e $archive_path and -w $archive_path) { - print CGI::p({-style=>'color:red; font-weight:bold'},"The course '$archive_courseID' has already been archived at '$archive_path'. - This earlier archive will be erased. This cannot be undone."); - } - - - if ($tables_ok and $all_fields_ok ) { # no missing fields + if ($all_tables_ok ) { # no missing fields + # Warn about overwriting an existing archive + if (-e $archive_path and -w $archive_path) { + print CGI::p({-style=>'color:red; font-weight:bold'},"The course '$archive_courseID' has already been archived at '$archive_path'. + This earlier archive will be erased. This cannot be undone."); + } + # archive execute button print CGI::p({style=>"text-align: center"}, CGI::submit(-name=>"decline_archive_course", -value=>"Don't archive"), " ", CGI::submit(-name=>"confirm_archive_course", -value=>"archive") , ); - } elsif ($all_fields_ok) { + } else { print CGI::p({style=>"text-align: center"}, - CGI::hidden(-name => 'missing_database_tables',-value => join(" ",keys %$schema_only)), - CGI::hidden(-name => 'extra_database_tables', -value => join(" ",keys %$database_only) ), - CGI::hidden(-name => 'missing_fields', -value => join(" ", %missing_fields) ), - CGI::submit(-name => "decline_archive_course", -value => "Don't archive"), + CGI::submit(-name => "decline_archive_course", -value => "Don't archive"), " ", CGI::submit(-name=>"upgrade_course_tables", -value=>"upgrade course tables"), ); - } else { - print CGI::p({style=>"text-align: center"}, - CGI::hidden(-name => 'missing_database_tables',-value => join(" ",keys %$schema_only)), - CGI::hidden(-name => 'extra_database_tables', -value => join(" ",keys %$database_only) ), - CGI::hidden(-name => 'missing_fields', -value => join(" ", %missing_fields) ), - CGI::submit(-name => "decline_archive_course", -value => "Don't archive"), - " ", - # CGI::submit(-name=>"upgrade_course_tables", -value=>"upgrade course tables"), - ); - } + } print CGI::end_form(); } else { print CGI::p({-style=>'color:red; font-weight:bold'},"Unable to find database layout for $archive_courseID"); Index: Std.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/DB/Schema/NewSQL/Std.pm,v retrieving revision 1.21 retrieving revision 1.22 diff -Llib/WeBWorK/DB/Schema/NewSQL/Std.pm -Llib/WeBWorK/DB/Schema/NewSQL/Std.pm -u -r1.21 -r1.22 --- lib/WeBWorK/DB/Schema/NewSQL/Std.pm +++ lib/WeBWorK/DB/Schema/NewSQL/Std.pm @@ -302,6 +302,29 @@ return "Describe `$sql_table_name` `$field_name`"; } #################################################### +# adding Field column +#################################################### + +sub add_column_field { + my $self = shift; + my $field_name = shift; + my $stmt = $self->_add_column_field_stmt($field_name); + #warn "database command $stmt"; + my $result = $self->dbh->do($stmt); + #warn "result of add column is $result"; + #return ($result eq "0E0") ? 0 : 1; # failed result is 0E0 + return 1; #FIXME how to determine if database update was successful??? +} + +sub _add_column_field_stmt { + my $self = shift; + my $field_name=shift; + my $sql_table_name = $self->sql_table_name; + my $sql_field_name = $self->sql_field_name($field_name); + my $sql_field_type = $self->field_data->{$field_name}{type}; + return "Alter table `$sql_table_name` add column `$sql_field_name` $sql_field_type"; +} +#################################################### # checking Tables #################################################### sub tableExists { Index: CourseManagement.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/Utils/CourseManagement.pm,v retrieving revision 1.45 retrieving revision 1.46 diff -Llib/WeBWorK/Utils/CourseManagement.pm -Llib/WeBWorK/Utils/CourseManagement.pm -u -r1.45 -r1.46 --- lib/WeBWorK/Utils/CourseManagement.pm +++ lib/WeBWorK/Utils/CourseManagement.pm @@ -1169,154 +1169,5 @@ -# -# -# =item checkCourseDirectories($courseName) -# -# Checks the course files and directories to make sure they exist and have the correct permissions. -# -# =cut -# -# -# -# =item checkCourseTables($courseName, $dbLayoutName, $ce); -# -# Checks the course tables in the mysql database and ensures that they are the -# same as the ones specified by the databaseLayout -# -# -# =cut -# -# sub checkCourseTables { -# my ($courseName, $dbLayoutName, $ce) = @_; -# my $str=''; -# my %both = (); -# my %schema_only = (); -# my %database_only = (); -# ########################################################## -# # fetch schema from course environment and search database -# # for corresponding tables. -# ########################################################## -# my $db = new WeBWorK::DB($ce->{dbLayouts}->{$dbLayoutName}); -# foreach my $table (sort keys %$db) { -# next if $db->{$table}{params}{non_native}; # skip non-native tables -# my $table_name = (exists $db->{$table}->{params}->{tableOverride})? $db->{$table}->{params}->{tableOverride}:$table; -# my $database_table_exists = ($db->{$table}->can("delete_table")) ? 1:0; -# if ($database_table_exists ) { # exists means the table could be deleted. -# $both{$table_name} = checkTableFields($courseName, $dbLayoutName, $ce, $table); -# } else { -# $schema_only{$table_name} = 1; -# } -# } -# ########################################################## -# # fetch fetch corresponding tables in the database and -# # search for corresponding schema entries. -# ########################################################## -# -# my $dbh =$db->{key}->dbh; # grab any database handle -# my $stmt = "show tables like '$courseName%'"; # mysql request -# my $result = $dbh->selectall_arrayref($stmt) ; -# my @tableNames = map {@$_} @$result; # drill down in the result to the table name level -# foreach my $table (sort @tableNames) { -# $table =~/${courseName}_(.*)/; -# my $schema_name = $1; -# my $exists = exists($db->{$schema_name}); -# $database_only{$table}=1 unless $exists; -# } -# print CGI::p($str); -# my $tables_ok = not ( %schema_only || %database_only ); # count number of extraneous tables; 0 means ok -# return ($tables_ok,\%both, \%schema_only, \%database_only); # table in both schema & database; found in schema only; found in database only -# } -# -# =item updateCourseTables($courseName, $dbLayoutName, $ce, $table_names); -# -# Adds schema tables to the database that had been missing from the database. -# -# =cut -# -# sub updateCourseTables { -# my ($courseName, $dbLayoutName, $ce, $table_names) = @_; -# my $db = new WeBWorK::DB($ce->{dbLayouts}->{$dbLayoutName}); -# warn "Programmers: Pass reference to the array of table names to be updated." unless ref($table_names)=~/ARRAY/; -# #warn "table names are ".join(" ", @$table_names); -# my $str=''; -# foreach my $table (sort @$table_names) { # remainder copied from db->create_table -# #warn "processing $table"; -# next if $table =~ /^_/; # skip non-table self fields (none yet) -# #warn "not a non-table self field"; -# next if $db->{$table}{params}{non_native}; # skip non-native tables -# #warn "not a non_native table"; -# my $schema_obj = $db->{$table}; -# -# if ($schema_obj->can("create_table")) { -# # warn "creating table $schema_obj"; -# $schema_obj->create_table; -# $str .= "Table $table created".CGI::br(); -# } else { -# warn "Skipping creation of '$table' table: no create_table method\n"; -# } -# } -# $str; -# -# } -# -# =cut -# -# -# -# =item checkTableFields($courseName, $dbLayoutName, $ce, $table); -# -# Checks the course tables in the mysql database and insures that they are the same as the ones specified by the databaseLayout -# -# -# =cut -# -# -# sub checkTableFields { -# my ($courseName, $dbLayoutName, $ce,$table) = @_; -# my $str=' '; -# my %both = (); -# my %schema_only = (); -# my %database_only = (); -# ########################################################## -# # fetch schema from course environment and search database -# # for corresponding tables. -# ########################################################## -# my $db = new WeBWorK::DB($ce->{dbLayouts}->{$dbLayoutName}); -# my $table_name = (exists $db->{$table}->{params}->{tableOverride})? $db->{$table}->{params}->{tableOverride}:$table; -# warn "$table_name is a non native table" if $db->{$table}{params}{non_native}; # skip non-native tables -# my @fields = $db->{$table}->{record}->FIELDS; -# foreach my $field (sort @fields) { -# #my $database_table_exists = ($db->{$table}->can("delete_table")) ? 1:0; -# my $field_name = $db->{$table}->{params}->{fieldOverride}->{$field} ||$field; -# my $database_field_exists = $db->{$table}->tableFieldExists($field_name); -# if ($database_field_exists) { -# $str.="$field =>$field_name, "; -# $both{$field}=1; -# } else { -# $str.="$field =>MISSING, "; -# $schema_only{$field}=1; -# } -# -# } -# ########################################################## -# # fetch fetch corresponding tables in the database and -# # search for corresponding schema entries. -# ########################################################## -# -# # my $dbh =$db->{key}->dbh; # grab any database handle -# # my $stmt = "show tables like '$courseName%'"; # mysql request -# # my $result = $dbh->selectall_arrayref($stmt) ; -# # my @tableNames = map {@$_} @$result; # drill down in the result to the table name level -# # foreach my $table (sort @tableNames) { -# # $table =~/${courseName}_(.*)/; -# # my $schema_name = $1; -# # my $exists = exists($db->{$schema_name}); -# # $database_only{$table}=1 unless $exists; -# # } -# # return (\%both, \%schema_only, \%database_only); # table in both schema & database; found in schema only; found in database only -# return $str."<br/>"; -# } - 1; Index: CourseIntegrityCheck.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/Utils/CourseIntegrityCheck.pm,v retrieving revision 1.1 retrieving revision 1.2 diff -Llib/WeBWorK/Utils/CourseIntegrityCheck.pm -Llib/WeBWorK/Utils/CourseIntegrityCheck.pm -u -r1.1 -r1.2 --- lib/WeBWorK/Utils/CourseIntegrityCheck.pm +++ lib/WeBWorK/Utils/CourseIntegrityCheck.pm @@ -28,6 +28,12 @@ use WeBWorK::Debug; use WeBWorK::Utils::CourseManagement qw/listCourses/; +use constant { # constants describing the comparison of two hashes. + ONLY_IN_A=>0, + ONLY_IN_B=>1, + DIFFER_IN_A_AND_B=>2, + SAME_IN_A_AND_B=>3 +}; ################################################################################ sub new { @@ -71,7 +77,7 @@ } ################################################################################ -=item checkCourseDirectories($courseName) +=item $CIchecker->checkCourseDirectories($courseName) Checks the course files and directories to make sure they exist and have the correct permissions. @@ -79,7 +85,7 @@ -=item checkCourseTables($courseName, $dbLayoutName, $ce); +=item $CIchecker->checkCourseTables($courseName); Checks the course tables in the mysql database and ensures that they are the same as the ones specified by the databaseLayout @@ -90,11 +96,9 @@ sub checkCourseTables { my ($self, $courseName) = @_; my $str=''; - my %ok_tables = (); - my %schema_only = (); - my %database_only = (); - my %update_fields = (); - ########################################################## + my $tables_ok = 1; + my %dbStatus = (); + ################################# # fetch schema from course environment and search database # for corresponding tables. ########################################################## @@ -105,14 +109,16 @@ my $table_name = (exists $db->{$table}->{params}->{tableOverride})? $db->{$table}->{params}->{tableOverride}:$table; my $database_table_exists = ($db->{$table}->tableExists) ? 1:0; if ($database_table_exists ) { # exists means the table can be described; - my( $fields_ok, $field_str,$fields_both, $fields_schema_only, $fields_database_only) = $self->checkTableFields($courseName, $table); + my( $fields_ok, $fieldStatus) = $self->checkTableFields($courseName, $table); if ($fields_ok) { - $ok_tables{$table_name} = 1; + $dbStatus{$table} = [SAME_IN_A_AND_B()]; } else { - $update_fields{$table_name}=[$fields_ok,$fields_both,$fields_schema_only,$fields_database_only]; + $dbStatus{$table} = [DIFFER_IN_A_AND_B(),$fieldStatus]; + $tables_ok=0; } } else { - $schema_only{$table_name} = 1; + $tables_ok=0; + $dbStatus{$table}=[ONLY_IN_A(),]; } } ########################################################## @@ -128,40 +134,40 @@ next unless $table =~/^${courseName}\_(.*)/; #double check that we only have our course tables my $schema_name = $1; my $exists = exists($db->{$schema_name}); - $database_only{$table}=1 unless $exists; + $tables_ok = 0 unless exists($db->{$schema_name}); + $dbStatus{$schema_name} =[ONLY_IN_B] unless $exists; } - my $tables_ok = ( scalar(%schema_only) || scalar(%database_only) ||scalar(%update_fields) ) ?0 :1; # count number of extraneous tables; no such tables makes $tables_ok true $self->unlock_database; - return ($tables_ok,\%ok_tables, \%schema_only, \%database_only, \%update_fields); # table in both schema & database; found in schema only; found in database only + return ($tables_ok,\%dbStatus); # table in both schema & database; found in schema only; found in database only } -=item updateCourseTables($courseName, $dbLayoutName, $ce, $table_names); +=item $CIchecker-> updateCourseTables($courseName, $table_names); Adds schema tables to the database that had been missing from the database. =cut sub updateCourseTables { - my ($self, $courseName, $table_names) = @_; + my ($self, $courseName, $schema_table_names) = @_; my $db = $self->db; $self->lock_database; - warn "Programmers: Pass reference to the array of table names to be updated." unless ref($table_names)=~/ARRAY/; - #warn "table names are ".join(" ", @$table_names); + warn "Programmers: Pass reference to the array of table names to be updated." unless ref($schema_table_names)=~/ARRAY/; + # warn "table names are ".join(" ", @$schema_table_names); my $str=''; - foreach my $table (sort @$table_names) { # remainder copied from db->create_table - next if $table =~ /^_/; # skip non-table self fields (none yet) - #warn "not a non-table self field"; - $table =~ /${courseName}_(.*)/; - my $schema_table_name = $1; + foreach my $schema_table_name (sort @$schema_table_names) { # remainder copied from db->create_table + # next if $table =~ /^_/; # skip non-table self fields (none yet) + # warn "not a non-table self field"; next if $db->{$schema_table_name}{params}{non_native}; # skip non-native tables #warn "not a non_native table"; my $schema_obj = $db->{$schema_table_name}; + my $database_table_name = (exists $db->{$schema_table_name}->{params}->{tableOverride})? + $db->{$schema_table_name}->{params}->{tableOverride}:$schema_table_name; if ($schema_obj->can("create_table")) { # warn "creating table $schema_obj"; $schema_obj->create_table; - $str .= "Table $table created".CGI::br(); + $str .= "Table $schema_table_name created as $database_table_name in database.".CGI::br(); } else { - warn "Skipping creation of '$table' table: no create_table method\n"; + warn "Skipping creation of '$schema_table_name' table: no create_table method\n"; } } $self->unlock_database; @@ -173,7 +179,7 @@ -=item checkTableFields($courseName, $dbLayoutName, $ce, $table); +=item $CIchecker->checkTableFields($courseName, $table); Checks the course tables in the mysql database and insures that they are the same as the ones specified by the databaseLayout @@ -183,10 +189,8 @@ sub checkTableFields { my ($self,$courseName, $table) = @_; - my $str=' '; - my %both = (); - my %schema_only = (); - my %database_only = (); + my $fields_ok = 1; + my %fieldStatus = (); ########################################################## # fetch schema from course environment and search database # for corresponding tables. @@ -200,12 +204,11 @@ my $field_name = $db->{$table}->{params}->{fieldOverride}->{$field} ||$field; $schema_override_field_names{$field_name}=$field; my $database_field_exists = $db->{$table}->tableFieldExists($field_name); - if ($database_field_exists) { - $str.="$field =>$field_name, "; - $both{$field}=1; + if ($database_field_exists) { + $fieldStatus{$field} =[SAME_IN_A_AND_B] } else { - $str.="$field =>MISSING, "; - $schema_only{$field}=1; + $fields_ok = 0; + $fieldStatus{$field} =[ONLY_IN_A]; } } @@ -215,16 +218,50 @@ ########################################################## my $dbh =$self->dbh; # grab any database handle - my $stmt = "SHOW COLUMNS FROM $table_name"; # mysql request + my $stmt = "SHOW COLUMNS FROM `$table_name`"; # mysql request my $result = $dbh->selectall_arrayref($stmt) ; my %database_field_names = map {${$_}[0]=>[$_]} @$result; # drill down in the result to the field name level # result is array: Field | Type | Null | Key | Default | Extra foreach my $field_name (sort keys %database_field_names) { my $exists = exists($schema_override_field_names{$field_name} ); - $database_only{$table}=1 unless $exists; + $fields_ok=0 unless $exists; + $fieldStatus{$field_name} = [ONLY_IN_B] unless $exists; } - my $fields_ok = not ( %schema_only || %database_only ); # count number of extraneous tables; no such tables makes $fields_ok true - return ($fields_ok, $str."<br/>",\%both, \%schema_only, \%database_only); # table in both schema & database; found in schema only; found in database only + + + return ($fields_ok, \%fieldStatus); # table in both schema & database; found in schema only; found in database only +} + + +=item $CIchecker->updateTableFields($courseName, $table); + +Checks the fields in the table in the mysql database and insures that they are the same as the ones specified by the databaseLayout + + +=cut + + +sub updateTableFields { + my ($self,$courseName, $table) = @_; + my $msg=''; + ########################################################## + # fetch schema from course environment and search database + # for corresponding tables. + ########################################################## + my $db = $self->db; + my $table_name = (exists $db->{$table}->{params}->{tableOverride})? $db->{$table}->{params}->{tableOverride}:$table; + warn "$table_name is a non native table" if $db->{$table}{params}{non_native}; # skip non-native tables + my ($fields_ok, $fieldStatus) = $self->checkTableFields($courseName,$table); + # add fields + foreach my $field_name (keys %$fieldStatus) { + next unless $fieldStatus->{$field_name}->[0] == ONLY_IN_A; + my $schema_obj = $db->{$table}; + if ( $schema_obj->can("add_column_field") ) { + $msg.= "Added column '$field_name' to table '$table'".CGI::br() if $schema_obj->add_column_field($field_name); + } + } + return $msg; + } ############################################################################## @@ -308,4 +345,17 @@ } +# +# +# =item checkCourseDirectories($courseName) +# +# Checks the course files and directories to make sure they exist and have the correct permissions. +# +# =cut +# +# +# + + + 1; \ No newline at end of file |
From: Mike G. v. a. <we...@ma...> - 2009-01-28 17:16:48
|
Log Message: ----------- Added submitActionScript option which allows you to add javaScript commands that are executed when you submit the webwork question. It's possible that this should replace submitActionAlias which hasn't really been implemented yet. Modified Files: -------------- pg/lib: Applet.pm Revision Data ------------- Index: Applet.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Applet.pm,v retrieving revision 1.13 retrieving revision 1.14 diff -Llib/Applet.pm -Llib/Applet.pm -u -r1.13 -r1.14 --- lib/Applet.pm +++ lib/Applet.pm @@ -205,6 +205,7 @@ configAlias => 'config', initializeActionAlias => 'setXML', submitActionAlias => 'getXML', + submitActionScript => '', # script executed on submitting the WW question returnFieldName => 'receivedField', headerText => DEFAULT_HEADER_TEXT(), objectText => '', @@ -258,6 +259,11 @@ $self->{submitActionAlias} = shift ||$self->{submitActionAlias}; # replace the current contents if non-empty $self->{submitActionAlias}; } +sub submitActionScript { + my $self = shift; + $self->{submitActionScript} = shift ||$self->{submitActionScript}; # replace the current contents if non-empty + $self->{submitActionScript}; +} sub getStateAlias { my $self = shift; $self->{getStateAlias} = shift ||$self->{getStateAlias}; # replace the current contents if non-empty @@ -355,6 +361,7 @@ my $base64_initialState = $self->base64_state; my $initializeAction = $self->initializeActionAlias; my $submitAction = $self->submitActionAlias; + my $submitActionScript = $self->submitActionScript; my $setState = $self->setStateAlias; my $getState = $self->getStateAlias; my $config = $self->configAlias; @@ -532,6 +539,7 @@ alert("$appletName is not ready"); } applet_getState_list["$appletName"](); + $submitActionScript //getQE("$returnFieldName").value = getApplet("$appletName").sendData(); //FIXME -- not needed in general? }; </script> |
From: Mike G. v. a. <we...@ma...> - 2009-01-25 22:24:29
|
Log Message: ----------- Changes which allow the rename function to also add new tables when renaming. Updating fields is not yet implemented. Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: CourseAdmin.pm Revision Data ------------- Index: CourseAdmin.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/CourseAdmin.pm,v retrieving revision 1.79 retrieving revision 1.80 diff -Llib/WeBWorK/ContentGenerator/CourseAdmin.pm -Llib/WeBWorK/ContentGenerator/CourseAdmin.pm -u -r1.79 -r1.80 --- lib/WeBWorK/ContentGenerator/CourseAdmin.pm +++ lib/WeBWorK/ContentGenerator/CourseAdmin.pm @@ -109,8 +109,25 @@ if (@errors) { $method_to_call = "rename_course_form"; } else { + $method_to_call = "rename_course_confirm"; + } + } elsif (defined $r->param("confirm_rename_course")) { + # validate and delete + @errors = $self->rename_course_validate; + if (@errors) { + $method_to_call = "rename_course_form"; + } else { $method_to_call = "do_rename_course"; } + } elsif (defined $r->param("upgrade_course_tables") ){ + # upgrade and revalidate + @errors = $self->rename_course_validate; + if (@errors) { + $method_to_call = "rename_course_form"; + } else { + $method_to_call = "rename_course_confirm"; + } + } else { $method_to_call = "rename_course_form"; } @@ -871,7 +888,124 @@ print CGI::end_form(); } +sub rename_course_confirm { + my ($self) = @_; + my $r = $self->r; + my $ce = $r->ce; + #my $db = $r->db; + #my $authz = $r->authz; + #my $urlpath = $r->urlpath; + + my $rename_oldCourseID = $r->param("rename_oldCourseID") || ""; + my $rename_newCourseID = $r->param("rename_newCourseID") || ""; + + my $ce2 = new WeBWorK::CourseEnvironment({ + %WeBWorK::SeedCE, + courseName => $rename_oldCourseID, + }); + + my ($tables_ok,$ok_tables,$schema_only,$database_only,$update_fields); + my %missing_fields; + if ($ce2->{dbLayoutName} ) { + my $CIchecker = new WeBWorK::Utils::CourseIntegrityCheck(ce=>$ce2); + if ($r->param("missing_database_tables")) { + my @table_names = split(/\s+/, $r->param("missing_database_tables") ); + my $msg = $CIchecker->updateCourseTables($rename_oldCourseID, [@table_names]); + print CGI::p({-style=>'color:green; font-weight:bold'}, $msg); + } + ($tables_ok,$ok_tables,$schema_only,$database_only,$update_fields) = $CIchecker->checkCourseTables($rename_oldCourseID); + print CGI::p("Are you sure you want to rename the course " . CGI::b($rename_oldCourseID). " to ".CGI::b($rename_newCourseID) + . "? "); + + print CGI::p({-style=>'color:black; font-weight:bold'},"These schema tables agree with those found in the database:"); + my $str = ''; + foreach my $table (sort keys %$ok_tables) { + $str .= CGI::b($table).CGI::br(); + #$str .= CGI::span( {-style=>'color:gray; font-weight:lighter'},$both->{$table} ); + } + print CGI::p($str); + + # print tables with mismatched fields + my $all_fields_ok = 1; + if (%$update_fields) { + print CGI::p({-style=>'color:black; font-weight:bold'},"The field names for these tables don't + agree with those found in the database. <br/>These fields will need to be repaired by hand by + accessing the database directly."); + $str=''; + foreach my $table (sort keys %$update_fields) { + my ($field_ok, $fields_both, $fields_schema_only, $fields_database_only) = @{$update_fields->{$table}}; + $str .= " missing fields from database table <b>$table</b>: " + . join(", ", map { "<br/> $_ => $$fields_schema_only{$_}" } keys %$fields_schema_only ) + . CGI::br(); + $all_fields_ok = 0 unless $field_ok; + } + print CGI::p($str); + + } + + # print tables missing from database + if (%$schema_only) { + print CGI::p({-style=>'color:red; font-weight:bold'}, "These schema tables are missing from the database. + Upgrading the database will create these tables." ); + $str = ''; + foreach my $table (sort keys %$schema_only) { + $str .= CGI::b($table)." missing from database".CGI::br(); + } + print CGI::p($str); + } + + # print tables missing from schema + if (%$database_only) { + print CGI::p({-style=>'color:red; font-weight:bold'}, "These database tables are missing from the schema. + These tables will be created in the database before archiving this course." ); + $str = ''; + foreach my $table (sort keys %$database_only) { + $str .= CGI::b($table)." exists in database but is missing from schema".CGI::br(); + } + print CGI::p($str); + } + if ($tables_ok) { + print CGI::p({-style=>'color:black; font-weight:bold'},"Course $rename_oldCourseID database is in order"); + } else { + print CGI::p({-style=>'color:red; font-weight:bold'}, "Course $rename_oldCourseID databases must be updated before renaming this course."); + } + print CGI::start_form(-method=>"POST", -action=>$r->uri); + print $self->hidden_authen_fields; + print $self->hidden_fields("subDisplay"); + print $self->hidden_fields(qw/rename_oldCourseID rename_newCourseID/); + # grab some values we'll need + # fail if the source course does not exist + + + + if ($tables_ok and $all_fields_ok ) { # no missing fields + print CGI::p({style=>"text-align: center"}, + CGI::submit(-name=>"decline_rename_course", -value=>"Don't rename"), + " ", + CGI::submit(-name=>"confirm_rename_course", -value=>"Rename") , + ); + } elsif ($all_fields_ok) { + print CGI::p({style=>"text-align: center"}, + CGI::hidden(-name => 'missing_database_tables',-value => join(" ",keys %$schema_only)), + CGI::hidden(-name => 'extra_database_tables', -value => join(" ",keys %$database_only) ), + CGI::hidden(-name => 'missing_fields', -value => join(" ", %missing_fields) ), + CGI::submit(-name => "decline_rename_course", -value => "Don't rename"), + " ", + CGI::submit(-name=>"upgrade_course_tables", -value=>"upgrade course tables"), + ); + } else { + print CGI::p({style=>"text-align: center"}, + CGI::hidden(-name => 'missing_database_tables',-value => join(" ",keys %$schema_only)), + CGI::hidden(-name => 'extra_database_tables', -value => join(" ",keys %$database_only) ), + CGI::hidden(-name => 'missing_fields', -value => join(" ", %missing_fields) ), + CGI::submit(-name => "decline_rename_course", -value => "Don't rename"), + " ", + # CGI::submit(-name=>"upgrade_course_tables", -value=>"upgrade course tables"), + ); + } + } +} sub rename_course_validate { my ($self) = @_; my $r = $self->r; |
From: Mike G. v. a. <we...@ma...> - 2009-01-25 22:22:15
|
Log Message: ----------- Moving Exception handling to DB.pm from DB::Schema Modified Files: -------------- webwork2/lib/WeBWorK/DB/Schema/NewSQL: Std.pm Revision Data ------------- Index: Std.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/DB/Schema/NewSQL/Std.pm,v retrieving revision 1.20 retrieving revision 1.21 diff -Llib/WeBWorK/DB/Schema/NewSQL/Std.pm -Llib/WeBWorK/DB/Schema/NewSQL/Std.pm -u -r1.20 -r1.21 --- lib/WeBWorK/DB/Schema/NewSQL/Std.pm +++ lib/WeBWorK/DB/Schema/NewSQL/Std.pm @@ -308,7 +308,7 @@ my $self = shift; my $stmt = $self->_exists_table_stmt; my $result = eval { $self->dbh->do($stmt); }; - ( caught WeBWorK::DB::Schema::Ex::TableMissing ) ? 0:1; + ( caught WeBWorK::DB::Ex::TableMissing ) ? 0:1; } sub _exists_table_stmt { @@ -772,8 +772,8 @@ # maps error numbers to exception classes for MySQL our %MYSQL_ERROR_CODES = ( - 1062 => 'WeBWorK::DB::Schema::Ex::RecordExists', - 1146 => 'WeBWorK::DB::Schema::Ex::TableMissing', + 1062 => 'WeBWorK::DB::Ex::RecordExists', + 1146 => 'WeBWorK::DB::Ex::TableMissing', ); # turns MySQL error codes into exceptions -- WeBWorK::DB::Schema::Ex objects |
From: Mike G. v. a. <we...@ma...> - 2009-01-25 22:21:26
|
Log Message: ----------- Moving exception catching to DB.pm from Schema.pm Modified Files: -------------- webwork2/lib/WeBWorK/DB: Schema.pm Revision Data ------------- Index: Schema.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/DB/Schema.pm,v retrieving revision 1.13 retrieving revision 1.14 diff -Llib/WeBWorK/DB/Schema.pm -Llib/WeBWorK/DB/Schema.pm -u -r1.13 -r1.14 --- lib/WeBWorK/DB/Schema.pm +++ lib/WeBWorK/DB/Schema.pm @@ -50,17 +50,17 @@ use strict; use warnings; -use Exception::Class ( - 'WeBWorK::DB::Schema::Ex' => {}, - 'WeBWorK::DB::Schema::Ex::RecordExists' => { - isa => 'WeBWorK::DB::Schema::Ex', - description => "Record exists", - }, - 'WeBWorK::DB::Schema::Ex::TableMissing' => { - isa => 'WeBWorK::DB::Schema::Ex', - description =>"missing table", - }, -); +# use Exception::Class ( +# 'WeBWorK::DB::Schema::Ex' => {}, +# 'WeBWorK::DB::Schema::Ex::RecordExists' => { +# isa => 'WeBWorK::DB::Schema::Ex', +# description => "Record exists", +# }, +# 'WeBWorK::DB::Schema::Ex::TableMissing' => { +# isa => 'WeBWorK::DB::Schema::Ex', +# description =>"missing table", +# }, +# ); ################################################################################ # constructor |
From: Mike G. v. a. <we...@ma...> - 2009-01-25 22:20:46
|
Log Message: ----------- moving exception catching to DB instead of DB::Schema Modified Files: -------------- webwork2/lib/WeBWorK: DB.pm Revision Data ------------- Index: DB.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/DB.pm,v retrieving revision 1.109 retrieving revision 1.110 diff -Llib/WeBWorK/DB.pm -Llib/WeBWorK/DB.pm -u -r1.109 -r1.110 --- lib/WeBWorK/DB.pm +++ lib/WeBWorK/DB.pm @@ -151,6 +151,10 @@ 'WeBWorK::DB::Ex::DependencyNotFound' => { isa => 'WeBWorK::DB::Ex::RecordNotFound', }, + 'WeBWorK::DB::Ex::TableMissing' => { + isa => 'WeBWorK::DB::Ex', + description =>"missing table", + }, ); ################################################################################ @@ -496,11 +500,20 @@ eval { return $self->{user}->add($User); }; - if (my $ex = caught WeBWorK::DB::Schema::Ex::RecordExists) { + if (my $ex = caught WeBWorK::DB::Ex::RecordExists) { croak "addUser: user exists (perhaps you meant to use putUser?)"; } elsif ($@) { die $@; } + # FIXME about these exceptions: eventually the exceptions should be part of + # WeBWorK::DB rather than WeBWorK::DB::Schema, and we should just let them + # through to the calling code. however, right now we have code that checks + # for the string "... exists" in the error message, so we need to convert + # here. + # + # WeBWorK::DB::Ex::RecordExists + # WeBWorK::DB::Ex::DependencyNotFound - i.e. inserting a password for a nonexistent user + # ? } sub putUser { @@ -592,7 +605,7 @@ eval { return $self->{password}->add($Password); }; - if (my $ex = caught WeBWorK::DB::Schema::Ex::RecordExists) { + if (my $ex = caught WeBWorK::DB::Ex::RecordExists) { croak "addPassword: password exists (perhaps you meant to use putPassword?)"; } elsif ($@) { die $@; @@ -685,7 +698,7 @@ eval { return $self->{permission}->add($PermissionLevel); }; - if (my $ex = caught WeBWorK::DB::Schema::Ex::RecordExists) { + if (my $ex = caught WeBWorK::DB::Ex::RecordExists) { croak "addPermissionLevel: permission level exists (perhaps you meant to use putPermissionLevel?)"; } elsif ($@) { die $@; @@ -771,7 +784,7 @@ eval { return $self->{key}->add($Key); }; - if (my $ex = caught WeBWorK::DB::Schema::Ex::RecordExists) { + if (my $ex = caught WeBWorK::DB::Ex::RecordExists) { croak "addKey: key exists (perhaps you meant to use putKey?)"; } elsif ($@) { die $@; @@ -904,7 +917,7 @@ eval { return $self->{locations}->add($Location); }; - if ( my $ex = caught WeBWorK::DB::Schema::Ex::RecordExists ) { + if ( my $ex = caught WeBWorK::DB::Ex::RecordExists ) { croak "addLocation: location exists (perhaps you meant to use putLocation?)"; } elsif ($@) { die $@; @@ -1005,7 +1018,7 @@ eval { return $self->{location_addresses}->add($LocationAddress); }; - if (my $ex = caught WeBWorK::DB::Schema::Ex::RecordExists) { + if (my $ex = caught WeBWorK::DB::Ex::RecordExists) { croak "addLocationAddress: location address exists (perhaps you meant to use putLocationAddress?)"; } elsif ($@) { die $@; @@ -1076,7 +1089,7 @@ return $self->{set}->add($GlobalSet); }; - if (my $ex = caught WeBWorK::DB::Schema::Ex::RecordExists) { + if (my $ex = caught WeBWorK::DB::Ex::RecordExists) { croak "addGlobalSet: global set exists (perhaps you meant to use putGlobalSet?)"; } elsif ($@) { die $@; @@ -1168,7 +1181,7 @@ eval { return $self->{set_user}->add($UserSet); }; - if (my $ex = caught WeBWorK::DB::Schema::Ex::RecordExists) { + if (my $ex = caught WeBWorK::DB::Ex::RecordExists) { croak "addUserSet: user set exists (perhaps you meant to use putUserSet?)"; } elsif ($@) { die $@; @@ -1280,7 +1293,7 @@ eval { return $self->{set_version}->add($SetVersion); }; - if (my $ex = caught WeBWorK::DB::Schema::Ex::RecordExists) { + if (my $ex = caught WeBWorK::DB::Ex::RecordExists) { croak "addSetVersion: set version exists (perhaps you meant to use putSetVersion?)"; } elsif ($@) { die $@; @@ -1394,7 +1407,7 @@ eval { return $self->{set_locations}->add($GlobalSetLocation); }; - if (my $ex = caught WeBWorK::DB::Schema::Ex::RecordExists) { + if (my $ex = caught WeBWorK::DB::Ex::RecordExists) { croak "addGlobalSetLocation: global set_location exists (perhaps you meant to use putGlobalSetLocation?)"; } elsif ($@) { die $@; @@ -1493,7 +1506,7 @@ eval { return $self->{set_locations_user}->add($UserSetLocation); }; - if (my $ex = caught WeBWorK::DB::Schema::Ex::RecordExists) { + if (my $ex = caught WeBWorK::DB::Ex::RecordExists) { croak "addUserSetLocation: user set_location exists (perhaps you meant to use putUserSetLocation?)"; } elsif ($@) { die $@; @@ -1595,7 +1608,7 @@ eval { return $self->{problem}->add($GlobalProblem); }; - if (my $ex = caught WeBWorK::DB::Schema::Ex::RecordExists) { + if (my $ex = caught WeBWorK::DB::Ex::RecordExists) { croak "addGlobalProblem: global problem exists (perhaps you meant to use putGlobalProblem?)"; } elsif ($@) { die $@; @@ -1693,7 +1706,7 @@ eval { return $self->{problem_user}->add($UserProblem); }; - if (my $ex = caught WeBWorK::DB::Schema::Ex::RecordExists) { + if (my $ex = caught WeBWorK::DB::Ex::RecordExists) { croak "addUserProblem: user problem exists (perhaps you meant to use putUserProblem?)"; } elsif ($@) { die $@; @@ -1837,7 +1850,7 @@ eval { return $self->{problem_version}->add($ProblemVersion); }; - if (my $ex = caught WeBWorK::DB::Schema::Ex::RecordExists) { + if (my $ex = caught WeBWorK::DB::Ex::RecordExists) { croak "addProblemVersion: problem version exists (perhaps you meant to use putProblemVersion?)"; } elsif ($@) { die $@; |
From: Mike G. v. a. <we...@ma...> - 2009-01-25 15:41:48
|
Log Message: ----------- Changes to support archiving courses. Modified Files: -------------- webwork2/lib/WeBWorK/Utils: CourseManagement.pm DBUpgrade.pm Added Files: ----------- webwork2/lib/WeBWorK/Utils: CourseIntegrityCheck.pm Revision Data ------------- Index: DBUpgrade.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/Utils/DBUpgrade.pm,v retrieving revision 1.4 retrieving revision 1.5 diff -Llib/WeBWorK/Utils/DBUpgrade.pm -Llib/WeBWorK/Utils/DBUpgrade.pm -u -r1.4 -r1.5 --- lib/WeBWorK/Utils/DBUpgrade.pm +++ lib/WeBWorK/Utils/DBUpgrade.pm @@ -25,7 +25,7 @@ use strict; use warnings; use WeBWorK::Debug; -use WeBWorK::Utils::CourseManagement qw/listCourses/; +#use WeBWorK::Utils::CourseManagement qw/listCourses/; ################################################################################ Index: CourseManagement.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/Utils/CourseManagement.pm,v retrieving revision 1.44 retrieving revision 1.45 diff -Llib/WeBWorK/Utils/CourseManagement.pm -Llib/WeBWorK/Utils/CourseManagement.pm -u -r1.44 -r1.45 --- lib/WeBWorK/Utils/CourseManagement.pm +++ lib/WeBWorK/Utils/CourseManagement.pm @@ -33,6 +33,7 @@ use WeBWorK::CourseEnvironment; use WeBWorK::Debug; use WeBWorK::Utils qw(runtime_use readDirectory pretty_print_rh); +use WeBWorK::Utils::DBUpgrade; our @EXPORT = (); our @EXPORT_OK = qw( @@ -44,11 +45,13 @@ archiveCourse unarchiveCourse dbLayoutSQLSources - checkCourseTables - updateCourseTables - checkCourseDirectories + ); +# checkCourseTables +# updateCourseTables +# checkCourseDirectories + =head1 FUNCTIONS =over @@ -1163,138 +1166,53 @@ print $fh "\n\n\n"; } } -=item checkCourseDirectories($courseName) - -Checks the course files and directories to make sure they exist and have the correct permissions. - -=cut - - - -=item checkCourseTables($courseName, $dbLayoutName, $ce); - -Checks the course tables in the mysql database and insures that they are the same as the ones specified by the databaseLayout - - -=cut - -sub checkCourseTables { - my ($courseName, $dbLayoutName, $ce) = @_; - my $str=''; - my %both = (); - my %schema_only = (); - my %database_only = (); - ########################################################## - # fetch schema from course environment and search database - # for corresponding tables. - ########################################################## - my $db = new WeBWorK::DB($ce->{dbLayouts}->{$dbLayoutName}); - foreach my $table (sort keys %$db) { - next if $db->{$table}{params}{non_native}; # skip non-native tables - my $table_name = (exists $db->{$table}->{params}->{tableOverride})? $db->{$table}->{params}->{tableOverride}:$table; - my $database_table_exists = ($db->{$table}->can("delete_table")) ? 1:0; - if ($database_table_exists ) { # exists means the table could be deleted. - $both{$table_name} = checkTableFields($courseName, $dbLayoutName, $ce, $table); - } else { - $schema_only{$table_name} = 1; - } - } - ########################################################## - # fetch fetch corresponding tables in the database and - # search for corresponding schema entries. - ########################################################## - - my $dbh =$db->{key}->dbh; # grab any database handle - my $stmt = "show tables like '$courseName%'"; # mysql request - my $result = $dbh->selectall_arrayref($stmt) ; - my @tableNames = map {@$_} @$result; # drill down in the result to the table name level - foreach my $table (sort @tableNames) { - $table =~/${courseName}_(.*)/; - my $schema_name = $1; - my $exists = exists($db->{$schema_name}); - $database_only{$table}=1 unless $exists; - } - print CGI::p($str); - my $tables_ok = not ( %schema_only || %database_only ); # count number of extraneous tables; 0 means ok - return ($tables_ok,\%both, \%schema_only, \%database_only); # table in both schema & database; found in schema only; found in database only -} - -=item updateCourseTables($courseName, $dbLayoutName, $ce, $table_names); - -Adds schema tables to the database that had been missing from the database. - -=cut - -sub updateCourseTables { - my ($courseName, $dbLayoutName, $ce, $table_names) = @_; - my $db = new WeBWorK::DB($ce->{dbLayouts}->{$dbLayoutName}); - #die "Programmers: Pass reference to the array of table names to be updated." unless ref($table_names)=~/ARRAY/; - #warn "table names are ".join(" ", @$table_names); - my $str=''; - foreach my $table (sort @$table_names) { # remainder copied from db->create_table - #warn "processing $table"; - next if $table =~ /^_/; # skip non-table self fields (none yet) - #warn "not a non-table self field"; - next if $db->{$table}{params}{non_native}; # skip non-native tables - #warn "not a non_native table"; - my $schema_obj = $db->{$table}; - - if ($schema_obj->can("create_table")) { - # warn "creating table $schema_obj"; - $schema_obj->create_table; - $str .= "Table $table created".CGI::br(); - } else { - warn "Skipping creation of '$table' table: no create_table method\n"; - } - } - $str; - -} -=cut - - - -=item checkTableFields($courseName, $dbLayoutName, $ce, $table); - -Checks the course tables in the mysql database and insures that they are the same as the ones specified by the databaseLayout - - -=cut -sub checkTableFields { - my ($courseName, $dbLayoutName, $ce,$table) = @_; - my $str=' '; - my %both = (); - my %schema_only = (); - my %database_only = (); - ########################################################## - # fetch schema from course environment and search database - # for corresponding tables. - ########################################################## - my $db = new WeBWorK::DB($ce->{dbLayouts}->{$dbLayoutName}); - my $table_name = (exists $db->{$table}->{params}->{tableOverride})? $db->{$table}->{params}->{tableOverride}:$table; - warn "$table_name is a non native table" if $db->{$table}{params}{non_native}; # skip non-native tables - my @fields = $db->{$table}->{record}->FIELDS; - foreach my $field (sort @fields) { - #my $database_table_exists = ($db->{$table}->can("delete_table")) ? 1:0; - my $field_name = $db->{$table}->{params}->{fieldOverride}->{$field} ||$field; - my $database_field_exists = $db->{$table}->tableFieldExists($field_name); - if ($database_field_exists) { - $str.="$field =>$field_name, "; - $both{$field}=1; - } else { - $str.="$field =>MISSING, "; - $schema_only{$field}=1; - } - - } - ########################################################## - # fetch fetch corresponding tables in the database and - # search for corresponding schema entries. - ########################################################## - +# +# +# =item checkCourseDirectories($courseName) +# +# Checks the course files and directories to make sure they exist and have the correct permissions. +# +# =cut +# +# +# +# =item checkCourseTables($courseName, $dbLayoutName, $ce); +# +# Checks the course tables in the mysql database and ensures that they are the +# same as the ones specified by the databaseLayout +# +# +# =cut +# +# sub checkCourseTables { +# my ($courseName, $dbLayoutName, $ce) = @_; +# my $str=''; +# my %both = (); +# my %schema_only = (); +# my %database_only = (); +# ########################################################## +# # fetch schema from course environment and search database +# # for corresponding tables. +# ########################################################## +# my $db = new WeBWorK::DB($ce->{dbLayouts}->{$dbLayoutName}); +# foreach my $table (sort keys %$db) { +# next if $db->{$table}{params}{non_native}; # skip non-native tables +# my $table_name = (exists $db->{$table}->{params}->{tableOverride})? $db->{$table}->{params}->{tableOverride}:$table; +# my $database_table_exists = ($db->{$table}->can("delete_table")) ? 1:0; +# if ($database_table_exists ) { # exists means the table could be deleted. +# $both{$table_name} = checkTableFields($courseName, $dbLayoutName, $ce, $table); +# } else { +# $schema_only{$table_name} = 1; +# } +# } +# ########################################################## +# # fetch fetch corresponding tables in the database and +# # search for corresponding schema entries. +# ########################################################## +# # my $dbh =$db->{key}->dbh; # grab any database handle # my $stmt = "show tables like '$courseName%'"; # mysql request # my $result = $dbh->selectall_arrayref($stmt) ; @@ -1305,9 +1223,100 @@ # my $exists = exists($db->{$schema_name}); # $database_only{$table}=1 unless $exists; # } -# return (\%both, \%schema_only, \%database_only); # table in both schema & database; found in schema only; found in database only - return $str."<br/>"; -} +# print CGI::p($str); +# my $tables_ok = not ( %schema_only || %database_only ); # count number of extraneous tables; 0 means ok +# return ($tables_ok,\%both, \%schema_only, \%database_only); # table in both schema & database; found in schema only; found in database only +# } +# +# =item updateCourseTables($courseName, $dbLayoutName, $ce, $table_names); +# +# Adds schema tables to the database that had been missing from the database. +# +# =cut +# +# sub updateCourseTables { +# my ($courseName, $dbLayoutName, $ce, $table_names) = @_; +# my $db = new WeBWorK::DB($ce->{dbLayouts}->{$dbLayoutName}); +# warn "Programmers: Pass reference to the array of table names to be updated." unless ref($table_names)=~/ARRAY/; +# #warn "table names are ".join(" ", @$table_names); +# my $str=''; +# foreach my $table (sort @$table_names) { # remainder copied from db->create_table +# #warn "processing $table"; +# next if $table =~ /^_/; # skip non-table self fields (none yet) +# #warn "not a non-table self field"; +# next if $db->{$table}{params}{non_native}; # skip non-native tables +# #warn "not a non_native table"; +# my $schema_obj = $db->{$table}; +# +# if ($schema_obj->can("create_table")) { +# # warn "creating table $schema_obj"; +# $schema_obj->create_table; +# $str .= "Table $table created".CGI::br(); +# } else { +# warn "Skipping creation of '$table' table: no create_table method\n"; +# } +# } +# $str; +# +# } +# +# =cut +# +# +# +# =item checkTableFields($courseName, $dbLayoutName, $ce, $table); +# +# Checks the course tables in the mysql database and insures that they are the same as the ones specified by the databaseLayout +# +# +# =cut +# +# +# sub checkTableFields { +# my ($courseName, $dbLayoutName, $ce,$table) = @_; +# my $str=' '; +# my %both = (); +# my %schema_only = (); +# my %database_only = (); +# ########################################################## +# # fetch schema from course environment and search database +# # for corresponding tables. +# ########################################################## +# my $db = new WeBWorK::DB($ce->{dbLayouts}->{$dbLayoutName}); +# my $table_name = (exists $db->{$table}->{params}->{tableOverride})? $db->{$table}->{params}->{tableOverride}:$table; +# warn "$table_name is a non native table" if $db->{$table}{params}{non_native}; # skip non-native tables +# my @fields = $db->{$table}->{record}->FIELDS; +# foreach my $field (sort @fields) { +# #my $database_table_exists = ($db->{$table}->can("delete_table")) ? 1:0; +# my $field_name = $db->{$table}->{params}->{fieldOverride}->{$field} ||$field; +# my $database_field_exists = $db->{$table}->tableFieldExists($field_name); +# if ($database_field_exists) { +# $str.="$field =>$field_name, "; +# $both{$field}=1; +# } else { +# $str.="$field =>MISSING, "; +# $schema_only{$field}=1; +# } +# +# } +# ########################################################## +# # fetch fetch corresponding tables in the database and +# # search for corresponding schema entries. +# ########################################################## +# +# # my $dbh =$db->{key}->dbh; # grab any database handle +# # my $stmt = "show tables like '$courseName%'"; # mysql request +# # my $result = $dbh->selectall_arrayref($stmt) ; +# # my @tableNames = map {@$_} @$result; # drill down in the result to the table name level +# # foreach my $table (sort @tableNames) { +# # $table =~/${courseName}_(.*)/; +# # my $schema_name = $1; +# # my $exists = exists($db->{$schema_name}); +# # $database_only{$table}=1 unless $exists; +# # } +# # return (\%both, \%schema_only, \%database_only); # table in both schema & database; found in schema only; found in database only +# return $str."<br/>"; +# } 1; --- /dev/null +++ lib/WeBWorK/Utils/CourseIntegrityCheck.pm @@ -0,0 +1,311 @@ +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/lib/WeBWorK/Utils/CourseIntegrityCheck.pm,v 1.1 2009/01/25 15:32:13 gage Exp $ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the +# Artistic License for more details. +################################################################################ + +package WeBWorK::Utils::CourseIntegrityCheck; + +=head1 NAME + +WeBWorK::Utils::CourseIntegrityCheck - check that course database tables agree with database schema and +that course directory structure is correct. + +=cut + +use strict; +use warnings; +use WeBWorK::Debug; +use WeBWorK::Utils::CourseManagement qw/listCourses/; + +################################################################################ + +sub new { + my $invocant = shift; + my $class = ref $invocant || $invocant; + my $self = bless {}, $class; + $self->init(@_); + return $self; +} + +sub init { + my ($self, %options) = @_; + + $self->{dbh} = DBI->connect( + $options{ce}{database_dsn}, + $options{ce}{database_username}, + $options{ce}{database_password}, + { + PrintError => 0, + RaiseError => 1, + }, + ); + + $self->{verbose_sub} = $options{verbose_sub} || \&debug; + $self->{confirm_sub} = $options{confirm_sub} || \&ask_permission_stdio; + $self->{ce} = $options{ce}; + my $dbLayoutName = $self->{ce}->{dbLayoutName}; + $self->{db} =new WeBWorK::DB($self->{ce}->{dbLayouts}->{$dbLayoutName}); +} + +sub ce { return shift->{ce} } +sub db { return shift->{db} } +sub dbh { return shift->{dbh} } +sub verbose { my $sub = shift->{verbose_sub}; return &$sub(@_) } +sub confirm { my $sub = shift->{confirm_sub}; return &$sub(@_) } + +sub DESTROY { + my ($self) = @_; + $self->unlock_database; + $self->SUPER::DESTROY if $self->can("SUPER::DESTROY"); +} + +################################################################################ +=item checkCourseDirectories($courseName) + +Checks the course files and directories to make sure they exist and have the correct permissions. + +=cut + + + +=item checkCourseTables($courseName, $dbLayoutName, $ce); + +Checks the course tables in the mysql database and ensures that they are the +same as the ones specified by the databaseLayout + + +=cut + +sub checkCourseTables { + my ($self, $courseName) = @_; + my $str=''; + my %ok_tables = (); + my %schema_only = (); + my %database_only = (); + my %update_fields = (); + ########################################################## + # fetch schema from course environment and search database + # for corresponding tables. + ########################################################## + my $db = $self->db; + $self->lock_database; + foreach my $table (sort keys %$db) { + next if $db->{$table}{params}{non_native}; # skip non-native tables + my $table_name = (exists $db->{$table}->{params}->{tableOverride})? $db->{$table}->{params}->{tableOverride}:$table; + my $database_table_exists = ($db->{$table}->tableExists) ? 1:0; + if ($database_table_exists ) { # exists means the table can be described; + my( $fields_ok, $field_str,$fields_both, $fields_schema_only, $fields_database_only) = $self->checkTableFields($courseName, $table); + if ($fields_ok) { + $ok_tables{$table_name} = 1; + } else { + $update_fields{$table_name}=[$fields_ok,$fields_both,$fields_schema_only,$fields_database_only]; + } + } else { + $schema_only{$table_name} = 1; + } + } + ########################################################## + # fetch fetch corresponding tables in the database and + # search for corresponding schema entries. + ########################################################## + + my $dbh = $self->dbh; + my $stmt = "show tables like '${courseName}%'"; # mysql request + my $result = $dbh->selectall_arrayref($stmt) ; + my @tableNames = map {@$_} @$result; # drill down in the result to the table name level + foreach my $table (sort @tableNames) { + next unless $table =~/^${courseName}\_(.*)/; #double check that we only have our course tables + my $schema_name = $1; + my $exists = exists($db->{$schema_name}); + $database_only{$table}=1 unless $exists; + } + my $tables_ok = ( scalar(%schema_only) || scalar(%database_only) ||scalar(%update_fields) ) ?0 :1; # count number of extraneous tables; no such tables makes $tables_ok true + $self->unlock_database; + return ($tables_ok,\%ok_tables, \%schema_only, \%database_only, \%update_fields); # table in both schema & database; found in schema only; found in database only +} + +=item updateCourseTables($courseName, $dbLayoutName, $ce, $table_names); + +Adds schema tables to the database that had been missing from the database. + +=cut + +sub updateCourseTables { + my ($self, $courseName, $table_names) = @_; + my $db = $self->db; + $self->lock_database; + warn "Programmers: Pass reference to the array of table names to be updated." unless ref($table_names)=~/ARRAY/; + #warn "table names are ".join(" ", @$table_names); + my $str=''; + foreach my $table (sort @$table_names) { # remainder copied from db->create_table + next if $table =~ /^_/; # skip non-table self fields (none yet) + #warn "not a non-table self field"; + $table =~ /${courseName}_(.*)/; + my $schema_table_name = $1; + next if $db->{$schema_table_name}{params}{non_native}; # skip non-native tables + #warn "not a non_native table"; + my $schema_obj = $db->{$schema_table_name}; + if ($schema_obj->can("create_table")) { + # warn "creating table $schema_obj"; + $schema_obj->create_table; + $str .= "Table $table created".CGI::br(); + } else { + warn "Skipping creation of '$table' table: no create_table method\n"; + } + } + $self->unlock_database; + $str; + +} + +=cut + + + +=item checkTableFields($courseName, $dbLayoutName, $ce, $table); + +Checks the course tables in the mysql database and insures that they are the same as the ones specified by the databaseLayout + + +=cut + + +sub checkTableFields { + my ($self,$courseName, $table) = @_; + my $str=' '; + my %both = (); + my %schema_only = (); + my %database_only = (); + ########################################################## + # fetch schema from course environment and search database + # for corresponding tables. + ########################################################## + my $db = $self->db; + my $table_name = (exists $db->{$table}->{params}->{tableOverride})? $db->{$table}->{params}->{tableOverride}:$table; + warn "$table_name is a non native table" if $db->{$table}{params}{non_native}; # skip non-native tables + my @schema_field_names = $db->{$table}->{record}->FIELDS; + my %schema_override_field_names=(); + foreach my $field (sort @schema_field_names) { + my $field_name = $db->{$table}->{params}->{fieldOverride}->{$field} ||$field; + $schema_override_field_names{$field_name}=$field; + my $database_field_exists = $db->{$table}->tableFieldExists($field_name); + if ($database_field_exists) { + $str.="$field =>$field_name, "; + $both{$field}=1; + } else { + $str.="$field =>MISSING, "; + $schema_only{$field}=1; + } + + } + ########################################################## + # fetch fetch corresponding tables in the database and + # search for corresponding schema entries. + ########################################################## + + my $dbh =$self->dbh; # grab any database handle + my $stmt = "SHOW COLUMNS FROM $table_name"; # mysql request + my $result = $dbh->selectall_arrayref($stmt) ; + my %database_field_names = map {${$_}[0]=>[$_]} @$result; # drill down in the result to the field name level + # result is array: Field | Type | Null | Key | Default | Extra + foreach my $field_name (sort keys %database_field_names) { + my $exists = exists($schema_override_field_names{$field_name} ); + $database_only{$table}=1 unless $exists; + } + my $fields_ok = not ( %schema_only || %database_only ); # count number of extraneous tables; no such tables makes $fields_ok true + return ($fields_ok, $str."<br/>",\%both, \%schema_only, \%database_only); # table in both schema & database; found in schema only; found in database only +} + +############################################################################## +# Database utilities -- borrowed from DBUpgrade.pm ??use or modify??? --MEG +############################################################################## + +sub lock_database { + my $self =shift; + my $dbh = $self->dbh; + my ($lock_status) = $dbh->selectrow_array("SELECT GET_LOCK('dbupgrade', 10)"); + if (not defined $lock_status) { + die "Couldn't obtain lock because an error occurred.\n"; + } + if ($lock_status) { + } else { + die "Timed out while waiting for lock.\n"; + } +} + +sub unlock_database { + my $self =shift; + my $dbh = $self->dbh; + my ($lock_status) = $dbh->selectrow_array("SELECT RELEASE_LOCK('dbupgrade')"); + if (not defined $lock_status) { + # die "Couldn't release lock because the lock does not exist.\n"; + }elsif ($lock_status) { + return; + } else { + die "Couldn't release lock because the lock is not held by this thread.\n"; + } +} + +############################################################################## + +sub load_sql_table_list { + my $self =shift; + my $dbh = $self->dbh; + my $sql_tables_ref = $dbh->selectcol_arrayref("SHOW TABLES"); + $self->{sql_tables} = {}; @{$self->{sql_tables}}{@$sql_tables_ref} = (); +} + +sub register_sql_table { + my $self =shift; + my $table = shift; + my $dbh = $self->dbh; + $self->{sql_tables}{$table} = (); +} + +sub unregister_sql_table { + my $self =shift; + my $table = shift; + my $dbh = $self->dbh; + delete $self->{sql_tables}{$table}; +} + +sub sql_table_exists { + my $self =shift; + my $table=shift; + my $dbh = $self->dbh; + return exists $self->{sql_tables}{$table}; +} + + +################################################################################ + +sub ask_permission_stdio { + my ($prompt, $default) = @_; + + $default = 1 if not defined $default; + my $options = $default ? "[Y/n]" : "[y/N]"; + + while (1) { + print "$prompt $options "; + my $resp = <STDIN>; + chomp $resp; + return $default if $resp eq ""; + return 1 if lc $resp eq "y"; + return 0 if lc $resp eq "n"; + $prompt = 'Please enter "y" or "n".'; + } +} + + +1; \ No newline at end of file |
From: Mike G. v. a. <we...@ma...> - 2009-01-25 15:40:08
|
Log Message: ----------- Changes to support archiving courses. Modified Files: -------------- webwork2/lib/WeBWorK/DB: Schema.pm webwork2/lib/WeBWorK/DB/Schema/NewSQL: Std.pm Revision Data ------------- Index: Schema.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/DB/Schema.pm,v retrieving revision 1.12 retrieving revision 1.13 diff -Llib/WeBWorK/DB/Schema.pm -Llib/WeBWorK/DB/Schema.pm -u -r1.12 -r1.13 --- lib/WeBWorK/DB/Schema.pm +++ lib/WeBWorK/DB/Schema.pm @@ -54,6 +54,11 @@ 'WeBWorK::DB::Schema::Ex' => {}, 'WeBWorK::DB::Schema::Ex::RecordExists' => { isa => 'WeBWorK::DB::Schema::Ex', + description => "Record exists", + }, + 'WeBWorK::DB::Schema::Ex::TableMissing' => { + isa => 'WeBWorK::DB::Schema::Ex', + description =>"missing table", }, ); Index: Std.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/DB/Schema/NewSQL/Std.pm,v retrieving revision 1.19 retrieving revision 1.20 diff -Llib/WeBWorK/DB/Schema/NewSQL/Std.pm -Llib/WeBWorK/DB/Schema/NewSQL/Std.pm -u -r1.19 -r1.20 --- lib/WeBWorK/DB/Schema/NewSQL/Std.pm +++ lib/WeBWorK/DB/Schema/NewSQL/Std.pm @@ -291,7 +291,8 @@ my $self = shift; my $field_name = shift; my $stmt = $self->_exists_field_stmt($field_name); - return $self->dbh->do($stmt); + my $result = $self->dbh->do($stmt); + return ($result eq "1") ? 1 : 0; # failed result is 0E0 } sub _exists_field_stmt { @@ -300,7 +301,21 @@ my $sql_table_name = $self->sql_table_name; return "Describe `$sql_table_name` `$field_name`"; } +#################################################### +# checking Tables +#################################################### +sub tableExists { + my $self = shift; + my $stmt = $self->_exists_table_stmt; + my $result = eval { $self->dbh->do($stmt); }; + ( caught WeBWorK::DB::Schema::Ex::TableMissing ) ? 0:1; +} +sub _exists_table_stmt { + my $self = shift; + my $sql_table_name = $self->sql_table_name; + return "Describe `$sql_table_name` "; +} ################################################################################ @@ -758,9 +773,10 @@ # maps error numbers to exception classes for MySQL our %MYSQL_ERROR_CODES = ( 1062 => 'WeBWorK::DB::Schema::Ex::RecordExists', + 1146 => 'WeBWorK::DB::Schema::Ex::TableMissing', ); -# turns MySQL error codes into excpetions -- WeBWorK::DB::Schema::Ex objects +# turns MySQL error codes into exceptions -- WeBWorK::DB::Schema::Ex objects # for known error types, and normal die STRING exceptions for unknown errors. # This is one method you'd want to override if you were writing a subclass for # another RDBMS. |
From: Mike G. v. a. <we...@ma...> - 2009-01-25 15:39:13
|
Log Message: ----------- Changes to support archiving and unarchiving courses Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: CourseAdmin.pm Revision Data ------------- Index: CourseAdmin.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/CourseAdmin.pm,v retrieving revision 1.78 retrieving revision 1.79 diff -Llib/WeBWorK/ContentGenerator/CourseAdmin.pm -Llib/WeBWorK/ContentGenerator/CourseAdmin.pm -u -r1.78 -r1.79 --- lib/WeBWorK/ContentGenerator/CourseAdmin.pm +++ lib/WeBWorK/ContentGenerator/CourseAdmin.pm @@ -35,7 +35,8 @@ use WeBWorK::Debug; use WeBWorK::Utils qw(cryptPassword writeLog listFilesRecursive trim_spaces); use WeBWorK::Utils::CourseManagement qw(addCourse renameCourse deleteCourse listCourses archiveCourse - listArchivedCourses unarchiveCourse checkCourseTables updateCourseTables); + listArchivedCourses unarchiveCourse); +use WeBWorK::Utils::CourseIntegrityCheck; use WeBWorK::Utils::DBImportExport qw(dbExport dbImport); # needed for location management use Net::IP; @@ -1647,34 +1648,48 @@ %WeBWorK::SeedCE, courseName => $archive_courseID, }); - if ($r->param("missing_database_tables")) { - my @table_names = split(/\s+/, $r->param("missing_database_tables") ); - warn "tables to be updated @table_names"; - my $msg = updateCourseTables($archive_courseID, $ce2->{dbLayoutName},$ce2,[@table_names]); - print CGI::p({-style=>'color:black; font-weight:bold'}, $msg); - } + - my ($tables_ok,$both,$schema_only,$database_only); + my ($tables_ok,$ok_tables,$schema_only,$database_only,$update_fields); my %missing_fields; if ($ce2->{dbLayoutName} ) { - ($tables_ok,$both,$schema_only,$database_only) = checkCourseTables($archive_courseID, $ce2->{dbLayoutName},$ce2); + my $CIchecker = new WeBWorK::Utils::CourseIntegrityCheck(ce=>$ce2); + if ($r->param("missing_database_tables")) { + my @table_names = split(/\s+/, $r->param("missing_database_tables") ); + my $msg = $CIchecker->updateCourseTables($archive_courseID, [@table_names]); + print CGI::p({-style=>'color:green; font-weight:bold'}, $msg); + } + ($tables_ok,$ok_tables,$schema_only,$database_only,$update_fields) = $CIchecker->checkCourseTables($archive_courseID); print CGI::p("Are you sure you want to archive the course " . CGI::b($archive_courseID) . "? "); - print CGI::p({-style=>'color:black; font-weight:bold'},"These schema tables are also found in the database:"); + print CGI::p({-style=>'color:black; font-weight:bold'},"These schema tables agree with those found in the database:"); my $str = ''; - foreach my $table (sort keys %$both) { - my $ok = " ok "; - if ( $both->{$table} =~ /MISSING/) { - $missing_fields{$table} = $both->{$table}; # string containing schema fields and their corresponding database table names - $ok = " missing fields "; - } - - $str .= CGI::b($table).$ok.CGI::br(); - $str .= CGI::span( {-style=>'color:gray; font-weight:lighter'},$both->{$table} ); + foreach my $table (sort keys %$ok_tables) { + $str .= CGI::b($table).CGI::br(); + #$str .= CGI::span( {-style=>'color:gray; font-weight:lighter'},$both->{$table} ); } print CGI::p($str); - # print missing from database + + # print tables with mismatched fields + my $all_fields_ok = 1; + if (%$update_fields) { + print CGI::p({-style=>'color:black; font-weight:bold'},"The field names for these tables don't + agree with those found in the database. <br/>These fields will need to be repaired by hand by + accessing the database directly."); + $str=''; + foreach my $table (sort keys %$update_fields) { + my ($field_ok, $fields_both, $fields_schema_only, $fields_database_only) = @{$update_fields->{$table}}; + $str .= " missing fields from database table <b>$table</b>: " + . join(", ", map { "<br/> $_ => $$fields_schema_only{$_}" } keys %$fields_schema_only ) + . CGI::br(); + $all_fields_ok = 0 unless $field_ok; + } + print CGI::p($str); + + } + + # print tables missing from database if (%$schema_only) { print CGI::p({-style=>'color:red; font-weight:bold'}, "These schema tables are missing from the database. Upgrading the database will create these tables." ); @@ -1684,16 +1699,18 @@ } print CGI::p($str); } - # print missing from schema + + # print tables missing from schema if (%$database_only) { print CGI::p({-style=>'color:red; font-weight:bold'}, "These database tables are missing from the schema. - These tables wilthe database before archiving this course." ); + These tables will be created in the database before archiving this course." ); $str = ''; foreach my $table (sort keys %$database_only) { $str .= CGI::b($table)." exists in database but is missing from schema".CGI::br(); } print CGI::p($str); } + if ($tables_ok) { print CGI::p({-style=>'color:black; font-weight:bold'},"Course $archive_courseID database is in order"); print(CGI::p({-style=>'color:red; font-weight:bold'}, "Are you sure that you want to delete the course ". @@ -1721,14 +1738,14 @@ } - if ($tables_ok and not scalar(%missing_fields) ) { # no missing fields + if ($tables_ok and $all_fields_ok ) { # no missing fields print CGI::p({style=>"text-align: center"}, CGI::submit(-name=>"decline_archive_course", -value=>"Don't archive"), " ", CGI::submit(-name=>"confirm_archive_course", -value=>"archive") , ); - } else { - print CGI::p({style=>"text-align: center"}, + } elsif ($all_fields_ok) { + print CGI::p({style=>"text-align: center"}, CGI::hidden(-name => 'missing_database_tables',-value => join(" ",keys %$schema_only)), CGI::hidden(-name => 'extra_database_tables', -value => join(" ",keys %$database_only) ), CGI::hidden(-name => 'missing_fields', -value => join(" ", %missing_fields) ), @@ -1736,6 +1753,15 @@ " ", CGI::submit(-name=>"upgrade_course_tables", -value=>"upgrade course tables"), ); + } else { + print CGI::p({style=>"text-align: center"}, + CGI::hidden(-name => 'missing_database_tables',-value => join(" ",keys %$schema_only)), + CGI::hidden(-name => 'extra_database_tables', -value => join(" ",keys %$database_only) ), + CGI::hidden(-name => 'missing_fields', -value => join(" ", %missing_fields) ), + CGI::submit(-name => "decline_archive_course", -value => "Don't archive"), + " ", + # CGI::submit(-name=>"upgrade_course_tables", -value=>"upgrade course tables"), + ); } print CGI::end_form(); } else { |
From: dpvc v. a. <we...@ma...> - 2009-01-20 19:04:16
|
Log Message: ----------- Fixed incorrect parentheses being added when $showparens eq "same" Modified Files: -------------- pg/lib/Parser: Function.pm Revision Data ------------- Index: Function.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Function.pm,v retrieving revision 1.26 retrieving revision 1.27 diff -Llib/Parser/Function.pm -Llib/Parser/Function.pm -u -r1.26 -r1.27 --- lib/Parser/Function.pm +++ lib/Parser/Function.pm @@ -256,7 +256,9 @@ foreach my $x (@{$self->{params}}) {push(@pstr,$x->string)} $string = ($self->{def}{string} || $self->{name})."$power".'('.join(',',@pstr).')'; $string = $self->addParens($string) - if (defined($precedence) and $precedence > $fn_precedence) || $showparens; + if $showparens eq 'all' or $showparens eq 'extra' or + (defined($precedence) and $precedence > $fn_precedence) or + (defined($precedence) and $precedence == $fn_precedence and $showparens eq 'same'); return $string; } @@ -275,7 +277,9 @@ if ($fn->{braceTeX}) {$TeX = $name.'{'.join(',',@pstr).'}'} else {$TeX = $name."$power".'\!\left('.join(',',@pstr).'\right)'} $TeX = '\left('.$TeX.'\right)' - if (defined($precedence) and $precedence > $fn_precedence) or $showparens; + if $showparens eq 'all' or $showparens eq 'extra' or + (defined($precedence) and $precedence > $fn_precedence) or + (defined($precedence) and $precedence == $fn_precedence and $showparens eq 'same'); return $TeX; } |
From: dpvc v. a. <we...@ma...> - 2009-01-19 19:00:07
|
Log Message: ----------- Fixed some typos in the comments. Modified Files: -------------- pg/macros: contextFraction.pl Revision Data ------------- Index: contextFraction.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextFraction.pl,v retrieving revision 1.2 retrieving revision 1.3 diff -Lmacros/contextFraction.pl -Lmacros/contextFraction.pl -u -r1.2 -r1.3 --- macros/contextFraction.pl +++ macros/contextFraction.pl @@ -20,7 +20,7 @@ and then select the appropriate context -- one of the following three: Context("Fraction"); - Context("Fraction-NoDecimal"); + Context("Fraction-NoDecimals"); Context("LimitedFraction"); The first is the most general, and allows fractions to be intermixed @@ -88,7 +88,7 @@ There are several options to the cmp() method that control how the answer checker will work. The first is controls whether unreduced fractions are accepted as correct. Unreduced fractions are allowed in -the Fraction and Fraction->NoDecimals contexts, but not in the +the Fraction and Fraction-NoDecimals contexts, but not in the LimitedFraction context. You can control this using the studentsMustReduceFractions option: @@ -139,7 +139,7 @@ not. If you want to prevent division from accepting non-integers, then set strictFractions=>1 (and also strictMinus=>1 and strictMultiplication=>1). These are all three 0 by default in the -Fraction and Fraction->NoDecimals contexts, but 1 in LimitedFraction. +Fraction and Fraction-NoDecimals contexts, but 1 in LimitedFraction. =item S<C<< allowProperFractions >>> @@ -147,7 +147,7 @@ is interpretted as implicit multiplication (as it usually would be in WeBWorK), or as addition, allowing "4 1/2" to mean "4 and 1/2". By default, it acts as multiplication in the Fraction and -Fraction->NoDecimals contexts, and as addition in LimitedFraction. If +Fraction-NoDecimals contexts, and as addition in LimitedFraction. If you set allowProperFractions=>1 you should also set reduceConstants=>0. =item S<C<< requireProperFractions >>> |
From: dpvc v. a. <we...@ma...> - 2009-01-19 14:05:28
|
Log Message: ----------- Fixed incorrect use of => when -> was intended. Modified Files: -------------- pg/macros: contextPiecewiseFunction.pl Revision Data ------------- Index: contextPiecewiseFunction.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextPiecewiseFunction.pl,v retrieving revision 1.10 retrieving revision 1.11 diff -Lmacros/contextPiecewiseFunction.pl -Lmacros/contextPiecewiseFunction.pl -u -r1.10 -r1.11 --- macros/contextPiecewiseFunction.pl +++ macros/contextPiecewiseFunction.pl @@ -712,7 +712,7 @@ # sub compareInterval { my $self = shift; my ($D,$f0,$f1) = @_; - my ($a,$b) = $D->value; $a = $a->value; $b = $b=>value; + my ($a,$b) = $D->value; $a = $a->value; $b = $b->value; return $f0 == $f1 if $D->{leftInfinite} && $D->{rightInfinite}; $a = $b - 2 if $D->{leftInfinite}; $b = $a + 2 if $D->{rightInfinite}; |
From: Mike G. v. a. <we...@ma...> - 2009-01-18 03:39:40
|
Log Message: ----------- changes to support error checking when archiving Modified Files: -------------- webwork2/lib/WeBWorK/DB/Schema/NewSQL: Std.pm Revision Data ------------- Index: Std.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/DB/Schema/NewSQL/Std.pm,v retrieving revision 1.18 retrieving revision 1.19 diff -Llib/WeBWorK/DB/Schema/NewSQL/Std.pm -Llib/WeBWorK/DB/Schema/NewSQL/Std.pm -u -r1.18 -r1.19 --- lib/WeBWorK/DB/Schema/NewSQL/Std.pm +++ lib/WeBWorK/DB/Schema/NewSQL/Std.pm @@ -283,6 +283,25 @@ return ($my_cnf, $dsn{database}); } +#################################################### +# checking Fields +#################################################### + +sub tableFieldExists { + my $self = shift; + my $field_name = shift; + my $stmt = $self->_exists_field_stmt($field_name); + return $self->dbh->do($stmt); +} + +sub _exists_field_stmt { + my $self = shift; + my $field_name=shift; + my $sql_table_name = $self->sql_table_name; + return "Describe `$sql_table_name` `$field_name`"; +} + + ################################################################################ # counting/existence |
From: Mike G. v. a. <we...@ma...> - 2009-01-18 03:34:56
|
Log Message: ----------- changes to support increased error checking when archiving courses Modified Files: -------------- webwork2/lib/WeBWorK/Utils: CourseManagement.pm Revision Data ------------- Index: CourseManagement.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/Utils/CourseManagement.pm,v retrieving revision 1.43 retrieving revision 1.44 diff -Llib/WeBWorK/Utils/CourseManagement.pm -Llib/WeBWorK/Utils/CourseManagement.pm -u -r1.43 -r1.44 --- lib/WeBWorK/Utils/CourseManagement.pm +++ lib/WeBWorK/Utils/CourseManagement.pm @@ -32,7 +32,7 @@ use String::ShellQuote; use WeBWorK::CourseEnvironment; use WeBWorK::Debug; -use WeBWorK::Utils qw(runtime_use readDirectory); +use WeBWorK::Utils qw(runtime_use readDirectory pretty_print_rh); our @EXPORT = (); our @EXPORT_OK = qw( @@ -44,6 +44,9 @@ archiveCourse unarchiveCourse dbLayoutSQLSources + checkCourseTables + updateCourseTables + checkCourseDirectories ); =head1 FUNCTIONS @@ -585,7 +588,12 @@ # fail if a course archive already exists # FIXME there could be an option to overwrite an existing archive if (-e $archive_path) { - croak "The course '$courseID' has already been archived at '$archive_path'.\n"; + unlink($archive_path) if (-w $archive_path); + unless (-e $archive_path) { + print CGI::p({-style=>'color:red; font-weight:bold'}, "The archival version of '$courseID' has been replaced'.\n"); + } else { + croak "Unable to replace the archival version of '$courseID'"; + } } #### step 1: dump tables ##### @@ -957,6 +965,14 @@ } } +=over + +=item getHelperRef($helperName, $dbLayoutName) + +Call a database-specific helper function, if a database-layout specific helper +class exists and contains a function named "${helperName}Helper". + +=cut sub getHelperRef { my ($helperName, $dbLayoutName) = @_; @@ -1147,5 +1163,151 @@ print $fh "\n\n\n"; } } +=item checkCourseDirectories($courseName) + +Checks the course files and directories to make sure they exist and have the correct permissions. + +=cut + + + +=item checkCourseTables($courseName, $dbLayoutName, $ce); + +Checks the course tables in the mysql database and insures that they are the same as the ones specified by the databaseLayout + + +=cut + +sub checkCourseTables { + my ($courseName, $dbLayoutName, $ce) = @_; + my $str=''; + my %both = (); + my %schema_only = (); + my %database_only = (); + ########################################################## + # fetch schema from course environment and search database + # for corresponding tables. + ########################################################## + my $db = new WeBWorK::DB($ce->{dbLayouts}->{$dbLayoutName}); + foreach my $table (sort keys %$db) { + next if $db->{$table}{params}{non_native}; # skip non-native tables + my $table_name = (exists $db->{$table}->{params}->{tableOverride})? $db->{$table}->{params}->{tableOverride}:$table; + my $database_table_exists = ($db->{$table}->can("delete_table")) ? 1:0; + if ($database_table_exists ) { # exists means the table could be deleted. + $both{$table_name} = checkTableFields($courseName, $dbLayoutName, $ce, $table); + } else { + $schema_only{$table_name} = 1; + } + } + ########################################################## + # fetch fetch corresponding tables in the database and + # search for corresponding schema entries. + ########################################################## + + my $dbh =$db->{key}->dbh; # grab any database handle + my $stmt = "show tables like '$courseName%'"; # mysql request + my $result = $dbh->selectall_arrayref($stmt) ; + my @tableNames = map {@$_} @$result; # drill down in the result to the table name level + foreach my $table (sort @tableNames) { + $table =~/${courseName}_(.*)/; + my $schema_name = $1; + my $exists = exists($db->{$schema_name}); + $database_only{$table}=1 unless $exists; + } + print CGI::p($str); + my $tables_ok = not ( %schema_only || %database_only ); # count number of extraneous tables; 0 means ok + return ($tables_ok,\%both, \%schema_only, \%database_only); # table in both schema & database; found in schema only; found in database only +} + +=item updateCourseTables($courseName, $dbLayoutName, $ce, $table_names); + +Adds schema tables to the database that had been missing from the database. + +=cut + +sub updateCourseTables { + my ($courseName, $dbLayoutName, $ce, $table_names) = @_; + my $db = new WeBWorK::DB($ce->{dbLayouts}->{$dbLayoutName}); + #die "Programmers: Pass reference to the array of table names to be updated." unless ref($table_names)=~/ARRAY/; + #warn "table names are ".join(" ", @$table_names); + my $str=''; + foreach my $table (sort @$table_names) { # remainder copied from db->create_table + #warn "processing $table"; + next if $table =~ /^_/; # skip non-table self fields (none yet) + #warn "not a non-table self field"; + next if $db->{$table}{params}{non_native}; # skip non-native tables + #warn "not a non_native table"; + my $schema_obj = $db->{$table}; + + if ($schema_obj->can("create_table")) { + # warn "creating table $schema_obj"; + $schema_obj->create_table; + $str .= "Table $table created".CGI::br(); + } else { + warn "Skipping creation of '$table' table: no create_table method\n"; + } + } + $str; + +} + +=cut + + + +=item checkTableFields($courseName, $dbLayoutName, $ce, $table); + +Checks the course tables in the mysql database and insures that they are the same as the ones specified by the databaseLayout + + +=cut + + +sub checkTableFields { + my ($courseName, $dbLayoutName, $ce,$table) = @_; + my $str=' '; + my %both = (); + my %schema_only = (); + my %database_only = (); + ########################################################## + # fetch schema from course environment and search database + # for corresponding tables. + ########################################################## + my $db = new WeBWorK::DB($ce->{dbLayouts}->{$dbLayoutName}); + my $table_name = (exists $db->{$table}->{params}->{tableOverride})? $db->{$table}->{params}->{tableOverride}:$table; + warn "$table_name is a non native table" if $db->{$table}{params}{non_native}; # skip non-native tables + my @fields = $db->{$table}->{record}->FIELDS; + foreach my $field (sort @fields) { + #my $database_table_exists = ($db->{$table}->can("delete_table")) ? 1:0; + my $field_name = $db->{$table}->{params}->{fieldOverride}->{$field} ||$field; + my $database_field_exists = $db->{$table}->tableFieldExists($field_name); + if ($database_field_exists) { + $str.="$field =>$field_name, "; + $both{$field}=1; + } else { + $str.="$field =>MISSING, "; + $schema_only{$field}=1; + } + + } + ########################################################## + # fetch fetch corresponding tables in the database and + # search for corresponding schema entries. + ########################################################## + +# my $dbh =$db->{key}->dbh; # grab any database handle +# my $stmt = "show tables like '$courseName%'"; # mysql request +# my $result = $dbh->selectall_arrayref($stmt) ; +# my @tableNames = map {@$_} @$result; # drill down in the result to the table name level +# foreach my $table (sort @tableNames) { +# $table =~/${courseName}_(.*)/; +# my $schema_name = $1; +# my $exists = exists($db->{$schema_name}); +# $database_only{$table}=1 unless $exists; +# } +# return (\%both, \%schema_only, \%database_only); # table in both schema & database; found in schema only; found in database only + return $str."<br/>"; +} + 1; |
From: Mike G. v. a. <we...@ma...> - 2009-01-18 03:34:08
|
Log Message: ----------- changes to support increased error checking when archiving courses Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: CourseAdmin.pm Revision Data ------------- Index: CourseAdmin.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/CourseAdmin.pm,v retrieving revision 1.77 retrieving revision 1.78 diff -Llib/WeBWorK/ContentGenerator/CourseAdmin.pm -Llib/WeBWorK/ContentGenerator/CourseAdmin.pm -u -r1.77 -r1.78 --- lib/WeBWorK/ContentGenerator/CourseAdmin.pm +++ lib/WeBWorK/ContentGenerator/CourseAdmin.pm @@ -35,7 +35,7 @@ use WeBWorK::Debug; use WeBWorK::Utils qw(cryptPassword writeLog listFilesRecursive trim_spaces); use WeBWorK::Utils::CourseManagement qw(addCourse renameCourse deleteCourse listCourses archiveCourse - listArchivedCourses unarchiveCourse); + listArchivedCourses unarchiveCourse checkCourseTables updateCourseTables); use WeBWorK::Utils::DBImportExport qw(dbExport dbImport); # needed for location management use Net::IP; @@ -185,6 +185,14 @@ } else { $method_to_call = "do_archive_course"; } + } elsif (defined $r->param("upgrade_course_tables") ){ + # upgrade and revalidate + @errors = $self->archive_course_validate; + if (@errors) { + $method_to_call = "archive_course_form"; + } else { + $method_to_call = "archive_course_confirm"; + } } else { # form only $method_to_call = "archive_course_form"; @@ -1639,28 +1647,100 @@ %WeBWorK::SeedCE, courseName => $archive_courseID, }); + if ($r->param("missing_database_tables")) { + my @table_names = split(/\s+/, $r->param("missing_database_tables") ); + warn "tables to be updated @table_names"; + my $msg = updateCourseTables($archive_courseID, $ce2->{dbLayoutName},$ce2,[@table_names]); + print CGI::p({-style=>'color:black; font-weight:bold'}, $msg); + } + my ($tables_ok,$both,$schema_only,$database_only); + my %missing_fields; if ($ce2->{dbLayoutName} ) { + ($tables_ok,$both,$schema_only,$database_only) = checkCourseTables($archive_courseID, $ce2->{dbLayoutName},$ce2); print CGI::p("Are you sure you want to archive the course " . CGI::b($archive_courseID) . "? "); - print(CGI::p({-style=>'color:red; font-weight:bold'}, "Are you sure that you want to delete the course ". - CGI::b($archive_courseID). " after archiving? This cannot be undone!")) if $delete_course_flag; - + print CGI::p({-style=>'color:black; font-weight:bold'},"These schema tables are also found in the database:"); + my $str = ''; + foreach my $table (sort keys %$both) { + my $ok = " ok "; + if ( $both->{$table} =~ /MISSING/) { + $missing_fields{$table} = $both->{$table}; # string containing schema fields and their corresponding database table names + $ok = " missing fields "; + } + + $str .= CGI::b($table).$ok.CGI::br(); + $str .= CGI::span( {-style=>'color:gray; font-weight:lighter'},$both->{$table} ); + } + print CGI::p($str); + # print missing from database + if (%$schema_only) { + print CGI::p({-style=>'color:red; font-weight:bold'}, "These schema tables are missing from the database. + Upgrading the database will create these tables." ); + $str = ''; + foreach my $table (sort keys %$schema_only) { + $str .= CGI::b($table)." missing from database".CGI::br(); + } + print CGI::p($str); + } + # print missing from schema + if (%$database_only) { + print CGI::p({-style=>'color:red; font-weight:bold'}, "These database tables are missing from the schema. + These tables wilthe database before archiving this course." ); + $str = ''; + foreach my $table (sort keys %$database_only) { + $str .= CGI::b($table)." exists in database but is missing from schema".CGI::br(); + } + print CGI::p($str); + } + if ($tables_ok) { + print CGI::p({-style=>'color:black; font-weight:bold'},"Course $archive_courseID database is in order"); + print(CGI::p({-style=>'color:red; font-weight:bold'}, "Are you sure that you want to delete the course ". + CGI::b($archive_courseID). " after archiving? This cannot be undone!")) if $delete_course_flag; + } else { + print CGI::p({-style=>'color:red; font-weight:bold'}, "Course $archive_courseID databases must be updated before archiving this course."); + } + print CGI::start_form(-method=>"POST", -action=>$r->uri); + print $self->hidden_authen_fields; + print $self->hidden_fields("subDisplay"); + print $self->hidden_fields(qw/archive_courseID delete_course/); + # grab some values we'll need + my $course_dir = $ce2->{courseDirs}{root}; + my $archive_path = $ce2->{webworkDirs}{courses} . "/$archive_courseID.tar.gz"; + # fail if the source course does not exist + unless (-e $course_dir) { + print CGI::p( "$archive_courseID: The directory for the course not found."); + } + + # fail if a course archive already exists + # FIXME there could be an option to overwrite an existing archive + if (-e $archive_path and -w $archive_path) { + print CGI::p({-style=>'color:red; font-weight:bold'},"The course '$archive_courseID' has already been archived at '$archive_path'. + This earlier archive will be erased. This cannot be undone."); + } + + + if ($tables_ok and not scalar(%missing_fields) ) { # no missing fields + print CGI::p({style=>"text-align: center"}, + CGI::submit(-name=>"decline_archive_course", -value=>"Don't archive"), + " ", + CGI::submit(-name=>"confirm_archive_course", -value=>"archive") , + ); + } else { + print CGI::p({style=>"text-align: center"}, + CGI::hidden(-name => 'missing_database_tables',-value => join(" ",keys %$schema_only)), + CGI::hidden(-name => 'extra_database_tables', -value => join(" ",keys %$database_only) ), + CGI::hidden(-name => 'missing_fields', -value => join(" ", %missing_fields) ), + CGI::submit(-name => "decline_archive_course", -value => "Don't archive"), + " ", + CGI::submit(-name=>"upgrade_course_tables", -value=>"upgrade course tables"), + ); + } + print CGI::end_form(); + } else { + print CGI::p({-style=>'color:red; font-weight:bold'},"Unable to find database layout for $archive_courseID"); } - - print CGI::start_form(-method=>"POST", -action=>$r->uri); - print $self->hidden_authen_fields; - print $self->hidden_fields("subDisplay"); - print $self->hidden_fields(qw/archive_courseID delete_course/); - - print CGI::p({style=>"text-align: center"}, - CGI::submit(-name=>"decline_archive_course", -value=>"Don't archive"), - " ", - CGI::submit(-name=>"confirm_archive_course", -value=>"archive"), - ); - - print CGI::end_form(); } sub do_archive_course { |