diff --git a/doc/content/stack.bib b/doc/content/stack.bib index f64e56d24cf..0aa7b281b63 100755 --- a/doc/content/stack.bib +++ b/doc/content/stack.bib @@ -221,6 +221,100 @@ @article{Kinnear2022 keywords = {computer-aided assessment,Course design,online and blended learning,specifications grading,university mathematics}, } +@article{Cameron2024, + title = {Designing a Blended Delivery Foundation Mathematics Course: {{Targeting}} Self-Efficacy, Skill Development and Social Connectedness}, + shorttitle = {Designing a Blended Delivery Foundation Mathematics Course}, + author = {Cameron, Rosie A.}, + year = 2024, + month = jan, + journal = {MSOR Connections}, + volume = {22}, + number = {1}, + pages = {29--41}, + issn = {2051-4220}, + doi = {10.21100/msor.v22i1.1451}, +} + +@article{Cameron2025, + title = {`{{The}} Green Tick': How Instant Feedback Shaped Self-Efficacy in a First-Year Mathematics Course}, + shorttitle = {`{{The}} Green Tick'}, + author = {Cameron, Rosie A.}, + year = 2025, + journal = {International Journal of Mathematical Education in Science and Technology}, + pages = {1--17}, + publisher = {Taylor \& Francis}, + issn = {0020-739X}, + doi = {10.1080/0020739X.2025.2574947}, +} + +@article{Gratwick2023, + title = {Using Online {{STACK}} Assessment to Teach Complex Analysis: A Prototype Course Design?}, + shorttitle = {Using Online {{STACK}} Assessment to Teach Complex Analysis}, + author = {Gratwick, Richard and O'Hagan, Steven}, + year = 2023, + month = mar, + journal = {MSOR Connections}, + volume = {21}, + number = {1}, + pages = {58--64}, + issn = {2051-4220}, + doi = {10.21100/msor.v21i1.1394}, +} + +@article{Lowe2026, + title = {A Decade of Computer-Algebra-Aided Online Assessment of Mathematics at the {{UK Open University}}}, + author = {Lowe, Tim and Hunt, Tim}, + year = 2026, + month = mar, + journal = {Teaching Mathematics and its Applications: An International Journal of the IMA}, + volume = {45}, + number = {1}, + pages = {1--18}, + issn = {0268-3679, 1471-6976}, + doi = {10.1093/teamat/hraf021}, +} + +@article{Wallace2026, + title = {Students Writing {{STACK}} Questions: E-Assessment as the Focus of a Final-Year Project}, + shorttitle = {Students Writing {{STACK}} Questions}, + author = {Wallace, Clare and Fearn, Sam}, + year = 2026, + month = mar, + journal = {Teaching Mathematics and Its Applications}, + volume = {45}, + number = {1}, + pages = {124--138}, + issn = {0268-3679, 1471-6976}, + doi = {10.1093/teamat/hrag006}, +} + +@article{Chelleri2025, + title = {Assessing Student Perspectives on {{STACK}} Implementation in University Mathematics Courses}, + author = {Chelleri, Elisabetta and Doz, Daniel and Juma, Zevick Otieno and {d'Assise Dogbalou}, Motognon Wastalas and Lewa{\'n}ski, Danilo}, + year = 2025, + month = oct, + journal = {International Journal of Mathematical Education in Science and Technology}, + pages = {1--32}, + issn = {0020-739X, 1464-5211}, + doi = {10.1080/0020739X.2025.2563115}, +} + +@article{Doz2026, + title = {Latent Class Analysis of Student Attitudes and Performance in Online Mathematics Assessments: A {{Technology Acceptance Model}} Approach}, + shorttitle = {Latent Class Analysis of Student Attitudes and Performance in Online Mathematics Assessments}, + author = {Doz, Daniel and {d'Assise Dogbalou}, Motognon Wastalas and Juma, Zevick Otieno and Lewa{\'n}ski, Danilo}, + year = 2026, + month = may, + journal = {International Journal of Mathematical Education in Science and Technology}, + volume = {57}, + number = {5}, + pages = {944--966}, + issn = {0020-739X, 1464-5211}, + doi = {10.1080/0020739X.2025.2502859}, +} + + + @INPROCEEDINGS{WebALT2006, author = {Sangwin, C.~J. and Grove, M.~J.}, @@ -256,7 +350,7 @@ @INPROCEEDINGS{Sangwin2012ICME } @INPROCEEDINGS{Sangwin2013HEA, - AUTHOR = {Butcher, P. and Sangwin, C.~. and Hunt, T.}, + AUTHOR = {Butcher, P. and Sangwin, C. and Hunt, T.}, TITLE = {Embedding and enhancing eAssessment in the leading open source {VLE}}, BOOKTITLE = {Proceedings of the Higher Education Academy Conference, Birmingham}, YEAR = {2013}, @@ -395,6 +489,52 @@ @MASTERSTHESIS{CervalPena2008 % More general research about online assessment, in which STACK played a significant part. +@incollection{Iannone2026, + title = {The Transfer of Assessment from Pen and Paper to E-Assessment: {{Two}} Case Studies from One Theoretical Perspective}, + shorttitle = {The Transfer of Assessment from Pen and Paper to E-Assessment}, + booktitle = {Digital {{Technology}} and {{Artificial Intelligence}} in {{Mathematics Education Assessment}}}, + author = {Iannone, Paola and Lemmo, Alice and Kinnear, George}, + editor = {Geraniou, Eirini and Crisan, Cosette and Mavrikis, Manolis}, + year = 2026, + publisher = {Routledge}, + doi = {10.4324/9781003533764-2}, +} +@incollection{Pinkernell2026a, + title = {Adaptive Feedback and (Digital) Assessment of Conceptual Knowledge}, + booktitle = {Digital {{Technology}} and {{Artificial Intelligence}} in {{Mathematics Education Assessment}}}, + author = {Pinkernell, Guido and Weigand, Hans-Georg}, + editor = {Geraniou, Eirini and Crisan, Cosette and Mavrikis, Manolis}, + year = 2026, + publisher = {Routledge}, + doi = {10.4324/9781003533764-6}, +} + +@incollection{Sangwin2026a, + title = {Using Computer Algebra to Support Automatic Assessment of Mathematics}, + booktitle = {Digital {{Technology}} and {{Artificial Intelligence}} in {{Mathematics Education Assessment}}}, + author = {Sangwin, Christopher and Oyengo, Michael O.}, + editor = {Geraniou, Eirini and Crisan, Cosette and Mavrikis, Manolis}, + year = 2026, + publisher = {Routledge}, + doi = {10.4324/9781003533764-12}, +} + + +@article{Kontorovich2023b, + title = {When Learning Stumbles upon Identity and Affect: A Loaded Student--Student Collaboration in Linear Algebra}, + shorttitle = {When Learning Stumbles upon Identity and Affect}, + author = {Kontorovich, Igor'}, + year = 2023, + month = sep, + journal = {International Journal of Mathematical Education in Science and Technology}, + volume = {54}, + number = {8}, + pages = {1526--1540}, + publisher = {Taylor \& Francis}, + issn = {0020-739X}, + doi = {10.1080/0020739X.2023.2173102}, +} + @inproceedings{KinnearIannone2024PME, title = {Students' Views of e-Assessment Feedback in Undergraduate Mathematics}, booktitle = {Proceedings of the 47th {{Conference}} of the {{International Group}} for the {{Psychology}} of {{Mathematics Education}}}, diff --git a/doc/content/stack_publications.pdf b/doc/content/stack_publications.pdf index 8e88306cd04..53eb8b0c6e1 100755 Binary files a/doc/content/stack_publications.pdf and b/doc/content/stack_publications.pdf differ diff --git a/doc/content/stack_publications.tex b/doc/content/stack_publications.tex index d771c0593fb..d6db43125f3 100755 --- a/doc/content/stack_publications.tex +++ b/doc/content/stack_publications.tex @@ -63,15 +63,19 @@ \section{Theses} \printbibliography[heading=none] \section{Research and conference papers} -\nocite{Gratwick2020,Akveld2024,2018Mastery,Yoshitomi2018,Barbas2016,Nakamura2016,Sangwin2015KHDM} +\nocite{Gratwick2020,Gratwick2023,Akveld2024,2018Mastery,Yoshitomi2018,Barbas2016,Nakamura2016,Sangwin2015KHDM} \nocite{Rasila2016,Rasila2016b,Rasila2016c} \nocite{Shirai2015a,Shirai2015b,Pavia2015,Nakamura2014,Shirai2014,Nakamura2013} \nocite{Sangwin2012ICME,Sangwin2013HEA,Lowe2010,Sangwin2009Equality,Sangwin2010IGI,Nakamura2010b} \nocite{Rasila2010,Sangwin2009Calculumus,Sangwin2009ICMI,Rasila2007} \nocite{Sangwin2006CASAlgebra,WebALT2006,Sangwin2006UniServe,SangwinTMA03,SangwinHSkills02} -\nocite{lowe_mestel_2019,erskine_mestel_2018,Paiva2017} -\nocite{Kinnear2022,Kinnear2022,Kontorovich2023,Kinnear2023CERME,Kinnear2024RME,Akveld2024,KinnearIannone2024PME} +\nocite{lowe_mestel_2019,erskine_mestel_2018,Lowe2026,Paiva2017} +\nocite{Kinnear2022,Kinnear2022,Kontorovich2023,Kontorovich2023b,Kinnear2023CERME,Kinnear2024RME,Akveld2024,KinnearIannone2024PME} \nocite{Kinnear2024FAME,Razeghpour2024FAME,Taha2024FAME} +\nocite{Iannone2026,Pinkernell2026a,Sangwin2026a} +\nocite{Chelleri2025,Doz2026} +\nocite{Wallace2026} +\nocite{Cameron2024,Cameron2025} \printbibliography[heading=none] \section{Proceedings of the first STACK conference, 2018} diff --git a/locallib.php b/locallib.php index 586a22c14b2..33dbc667a87 100644 --- a/locallib.php +++ b/locallib.php @@ -310,3 +310,21 @@ function get_latest_question_version($questionid) { $result = reset($result); return [$result->version, $result->questionid, $result->questionbankentryid, $versions]; } + +/** + * Get the base URL for the plot.php script. + * @return string + */ +function stack_plot_url_base() { + global $CFG; + return $CFG->wwwroot . '/question/type/stack/plot.php/'; +} + +/** + * Get the directory path for the corsscripts directory. + * @return string + */ +function stack_cors_scripts_dir() { + global $CFG; + return $CFG->dirroot . '/question/type/stack/corsscripts/'; +} diff --git a/stack/cas/castext2/blocks/jsxgraph.block.php b/stack/cas/castext2/blocks/jsxgraph.block.php index 3d3e23be51b..bd6144eea87 100644 --- a/stack/cas/castext2/blocks/jsxgraph.block.php +++ b/stack/cas/castext2/blocks/jsxgraph.block.php @@ -355,10 +355,7 @@ public function validate( ) { $valid = false; $err[] = stack_string('stackBlock_jsxgraph_unknown_style', ['style' => $stylename]); - } else if ( - !file_exists($CFG->dirroot . '/question/type/stack/corsscripts/jsxgraphstyles/' . - $stylename . '.css') - ) { + } else if (!file_exists(stack_cors_scripts_dir() . 'jsxgraphstyles/' . $stylename . '.css')) { $valid = false; $err[] = stack_string('stackBlock_jsxgraph_unknown_style', ['style' => $stylename]); } diff --git a/stack/cas/castext2/blocks/parsons.block.php b/stack/cas/castext2/blocks/parsons.block.php index 0c6b1182b37..8eeb3be71a4 100644 --- a/stack/cas/castext2/blocks/parsons.block.php +++ b/stack/cas/castext2/blocks/parsons.block.php @@ -584,10 +584,7 @@ public function validate( ) { $valid = false; $err[] = stack_string('stackBlock_parsons_unknown_style', ['style' => $stylename]); - } else if ( - !file_exists($CFG->dirroot . '/question/type/stack/corsscripts/parsonsstyles/' . - $stylename . '.min.css') - ) { + } else if (!file_exists(stack_cors_scripts_dir() . 'parsonsstyles/' . $stylename . '.min.css')) { $valid = false; $err[] = stack_string('stackBlock_parsons_unknown_style', ['style' => $stylename]); } diff --git a/stack/cas/connector.dbcache.class.php b/stack/cas/connector.dbcache.class.php index ddcca6ede22..c78db413926 100644 --- a/stack/cas/connector.dbcache.class.php +++ b/stack/cas/connector.dbcache.class.php @@ -52,9 +52,10 @@ public function compute($command) { // @codingStandardsIgnoreEnd if (!stack_connection_helper::check_stackmaxima_version($cached->result)) { stack_connection_helper::warn_about_version_mismatch($this->debug); - // We could consider automatically purging the cache here. + $this->delete_cached_result($cached->key); + } else { + return $cached->result; } - return $cached->result; } $this->debug->log('Maxima command not found in the cache. Using the raw connection.'); $result = $this->rawconnection->compute($command); @@ -78,7 +79,12 @@ public function json_compute($command): array { // @codingStandardsIgnoreStart $this->debug->log('Unpacked result found in the DB cache', print_r($cached->result, true)); // @codingStandardsIgnoreEnd - return $cached->result; + if (!$this->check_json_stackmaxima_version($cached->result)) { + stack_connection_helper::warn_about_version_mismatch($this->debug); + $this->delete_cached_result($cached->key); + } else { + return $cached->result; + } } $this->debug->log('Maxima command not found in the cache. Using the raw connection.'); $this->debug->log('Maxima command', $command); @@ -160,6 +166,28 @@ protected function add_to_cache($command, $result, $key = null) { $this->db->insert_record('qtype_stack_cas_cache', $data); } + /** + * Delete a cached result by cache key. + * @param string $key the cache key to delete. + */ + protected function delete_cached_result($key) { + $this->db->delete_records('qtype_stack_cas_cache', ['hash' => $key]); + } + + /** + * Check version information in JSON CAS session results. + * @param array $result the JSON result from Maxima. + * @return bool whether the cached result used compatible STACK-Maxima libraries. + */ + protected function check_json_stackmaxima_version($result) { + if (!array_key_exists('values', $result) || !is_array($result['values']) || + !array_key_exists('__stackmaximaversion', $result['values'])) { + return true; + } + + return stack_utils::get_config()->stackmaximaversion === $result['values']['__stackmaximaversion']; + } + /** * Add description here * @param string $command Maxima code to execute. diff --git a/stack/input/equiv/equiv.class.php b/stack/input/equiv/equiv.class.php index 8dcaf66d7b8..5386d74b700 100644 --- a/stack/input/equiv/equiv.class.php +++ b/stack/input/equiv/equiv.class.php @@ -620,7 +620,7 @@ public function render_validation(stack_input_state $state, $fieldname, $lang = if (self::INVALID == $state->status) { $feedback .= html_writer::tag( - 'span', + defined('MOODLE_INTERNAL') ? 'span' : 'div', stack_string('studentValidation_invalidAnswer'), ['class' => 'alert alert-danger stackinputerror'] ); diff --git a/stack/input/inputbase.class.php b/stack/input/inputbase.class.php index 3d1fa8ad448..b4e71f53e0f 100644 --- a/stack/input/inputbase.class.php +++ b/stack/input/inputbase.class.php @@ -1790,7 +1790,7 @@ public function maxima_to_response_array($in) { * This function is responsible for removing the validation tags from the question stem and replacing * them with the validation feedback. Only the equiv input type currently does anything different here. */ - public function replace_validation_tags($state, $fieldname, $questiontext) { + public function replace_validation_tags($state, $fieldname, $questiontext, $custom_validation = null) { $name = $this->name; // ISS879 Set language override to null as we should be in the question render here. It's only @@ -1807,6 +1807,10 @@ public function replace_validation_tags($state, $fieldname, $questiontext) { $class = "stackinputfeedback compact"; } + if ($custom_validation !== null) { + $feedback = $custom_validation; + } + if (!$feedback) { $class .= ' empty'; } @@ -1898,4 +1902,8 @@ public function get_api_solution($tavalue) { public function get_api_solution_render($tadisplay, $ta) { return $tadisplay; } + + public function get_name(): string { + return $this->name; + } } diff --git a/stack/questionlibrary.class.php b/stack/questionlibrary.class.php index 4af79db8f5d..944cf7ee61e 100644 --- a/stack/questionlibrary.class.php +++ b/stack/questionlibrary.class.php @@ -120,7 +120,7 @@ public static function render_question(object $question): string { } foreach ($plots as $original => $new) { - $questiontext = str_replace($original, $CFG->wwwroot . '/question/type/stack/plot.php/' . $new, $questiontext); + $questiontext = str_replace($original, stack_plot_url_base() . $new, $questiontext); } $formatoptions = new stdClass();