<?php if ( ! defined ( 'PmWiki' )) exit ();
error_reporting(0);
// for demo
# generate exercises based on wiki content and its structure
#
# still to implement (cf http://fabien.benetou.fr/Cookbook/Cognition#DailyExercisesFeed )
# refactor in classes?
# random type is not implemented properly enough for score tracking
# e.g. highest score from random appear as other exercise
# after 1 random exercise, going back to the same type of the last picked exercise
# which of those group corresponds the graph visualization V, A, B or C?
# using groupnetworkvisualization.php
# what is the correct order for pages A, B and C on descending criteria i A>B>C? A>C>B? B>C>A? B>A>C? C>B>A? C>A>B?
# e.g. size, frequency update, last edition, ...
# what is the color hashing used in RevertedPIM for this group, color A, B or C?
# might require JS
# keyword or URL in pages
# definition of word and vice versa
# limited to pages with definitions (e.g. some languages)
# list lines with "* word = definition"
# split word and definition
# pick one and display the other with alternatives
# extend type=pagehasexpression
# implemented
# is word X from page A, B, C or D? (type=expressioninpage)
# does page A contains word X, Y or Z? (type=pagehasexpression)
# which of those page corresponds the updates visualization V, A, B or C? (type=historyvisualization)
# is page A linked to page B? (type=pagelink)
# difficulty parameter
# should be linked to the counter
# e.g. increasing the number of possibilities (at least 1 out of 5 pages instead of at least 1 out of 3)
# do a graph traversal rather than random jumps
# i.e. if pick randomly from the linked pages or from the group or from the the whole wiki of the previous page
# consider https://research.cc.gatech.edu/inc/game-forge
$RecipeInfo['Exercises']['Version'] = '2015-16-08';
SDV($HandleActions['Exercises'], 'Exercises');
SDV($HandleAuth['Exercises'],'read');
function Exercises($pagename, $auth){
global $ScriptUrl;
$type = $_GET["type"];
$counter = (int) $_GET["counter"];
$difficulty = (int) $_GET["difficulty"];
list($group,$page) = explode(".",$pagename);
$availableexercisetypes = array("pagelink", "expressioninpage", "pagehasexpression");
if (file_exists($FarmD."/pub/visualization/edits_per_page/" ))
//the pim_functions.php generated the PJS has been loaded before
$availableexercisetypes[] = "historyvisualization";
if ($type=="random" || $type=="") {
$type= $availableexercisetypes[array_rand($availableexercisetypes)];
$random=true;
}
$result = "";
$verbatimresult = "";
$n=3;
// n should be based on $difficulty, e.g. n=2+$difficulty or n=2^$difficulty
// it used to determine the number of false answers
// it can be overwritten per each exercise
$minlinelength = 20;
// this could also be consider a difficulty, the shorter the harder
// it can be overwritten per each exercise
// tracking the current score
$counterparam = "";
if ( $counter > 0) {
$counterparam = "counter=$counter&";
}
// the pattern should be improve, e.g. remove PmWiki. , RecentChanges, GroupFooter, GroupHeader, Template, ...
if ($group == "AllPages"){
// equivalent to getting ALL pages
$pages = ListPages();
} else {
$pages = ListPages("/$group\./e");
}
//randomly pick a page in the possible pages
$sourcepage = $pages[array_rand($pages)];
unset($pages[array_search($sourcepage,$pages)]);
switch ($type){
case "pagelink":
$content = ReadPage($sourcepage,READPAGE_CURRENT);
$links = $content["targets"];
$links_array = explode(",",$links);
// XXX this does not seem to be used
while ((count($links_array)< 1 ) & & ( count ( $ pages ) > 0)) {
$sourcepage = $pages[array_rand($pages)];
unset($pages[array_search($sourcepage,$pages)]);
$content = ReadPage($sourcepage,READPAGE_CURRENT);
$links = $content["targets"];
$links_array = explode(",",$links);
}
if ((count($links_array)< 1 ) ) {
$result .= "Unfortunately it seems no suitable page has been found for this game. ";
$result .= "Try in another group or [[AllPages/AllPages?type=$type& counter=$counter|in the entire wiki]].";
break;
}
//randomly pick a page amongst the linked pages
$answers[] = $links_array[array_rand($links_array)];
unset($pages[array_search($answers[0],$links_array)]);
unset($pages[array_search($answers[0],$pages)]);
//randomly pick n-1 others pages which may not be amongst the list of linked page
for ($i=1;$i< $n;$i++) {
$answers[] = $pages[array_rand($pages)];
unset($pages[array_search($answers[$i],$pages)]);
}
//display
$result .="Is page [[$sourcepage]] linked to ";
// there should now be at least 1 needle in the haystack, the first one
// note that there might be more but this is not a problem as long as the question is stated clearly.
shuffle($answers);
for ($i=0;$i< count ( $ answers ) ; $ i + + ) {
if ( $i == count($answers) -1 )
$result .= " or ";
$currentpage = $answers[$i];
//display the groupname everytime only if using AllPages
if ($group != "AllPages")
list($currentgroup,$currentpage) = explode(".",$answers[$i]);
$result .="[[$pagename?action=ExercisesCheck& ".$counterparam
."type=$type& source=$sourcepage& answer=".$answers[$i]."|".$currentpage."]], ";
}
$result .="? ";
$result .="\n\nClick on the answer.";
break;
case "historyvisualization":
$processingpath = "$ScriptUrl/pub/libraries/processing.js";
$processinglib = "< script src = \"$processingpath\" type = \"text/javascript\" > < / script > ";
$processingfile = "$ScriptUrl/pub/visualization/edits_per_page/$sourcepage.pjs";
// TODO if it does not exist, pick another page, adapt code above
$answers[] = $sourcepage;
//randomly pick n-1 others pages which may not be amongst the list of linked page
for ($i=1;$i< $n;$i++) {
$answers[] = $pages[array_rand($pages)];
unset($pages[array_search($answers[$i],$pages)]);
}
//display
$historyvisualization = $processinglib.'< canvas data-src = "'.$processingfile.'" > < / canvas > ';
$verbatimresult = $historyvisualization;
$result .="Which of those page corresponds the updates visualization:";
// note that there might be more than 1 correct answer but this is not a problem as long as the question is stated clearly.
shuffle($answers);
for ($i=0;$i< count ( $ answers ) ; $ i + + ) {
if ( $i == count($answers) -1 )
$result .= " or ";
//display the groupname everytime only if using AllPages
$currentpage = $answers[$i];
if ($group != "AllPages")
list($currentgroup,$currentpage) = explode(".",$answers[$i]);
$result .="[[$pagename?action=ExercisesCheck& ".$counterparam
."type=$type& source=$sourcepage& answer=".md5($answers[$i])."|".$currentpage."]], ";
}
$result .="? ";
$result .="\n\nClick on the answer.";
break;
case "pagehasexpression":
$content = ReadPage($sourcepage,READPAGE_CURRENT);
$text = $content["text"];
$lines = explode("\n",$text);
$pickedline = $lines[rand(0,count($lines)-1)];
unset($lines[array_search($pickedline,$lines)]);
// remove markups
//pregreplace("/(:.*:)/","",$pickedline);
while (( strlen($pickedline) < $minlinelength ) & & ( count($lines) > 0 ) ) {
$pickedline = $lines[rand(0,count($lines)-1)];
//pregreplace("/(:.*:)/","",$pickedline);
unset($lines[array_search($pickedline,$lines)]);
}
$actualanswer = $pickedline;
$answers[] = $pickedline;
for ($i=1;$i< $n;$i++) {
$otherpage = $pages[rand(0,count($pages)-1)];
$otherpage = $pages[array_rand($pages)];
unset($pages[array_search($otherpage,$pages)]);
$content = ReadPage($otherpage,READPAGE_CURRENT);
$text = $content["text"];
$lines = explode("\n",$text);
$pickedline = $lines[rand(0,count($lines)-1)];
unset($lines[array_search($pickedline,$lines)]);
while (( strlen($pickedline) < $minlinelength ) & & ( count($lines) > 0 ) ) {
$pickedline = $lines[rand(0,count($lines)-1)];
unset($lines[array_search($pickedline,$lines)]);
}
$answers[] = $pickedline;
}
//display
$result .= "Does the page [[$sourcepage]] has expression ";
shuffle($answers);
for ($i=0;$i< $n;$i++) {
$hashedanswer = md5($answers[$i]);
$result .="\n* [[$pagename?action=ExercisesCheck& ".$counterparam
."type=$type& source=$sourcepage& answer=$hashedanswer|$i]] [@".$answers[$i]."@] ";
//should be better parsed!
}
$result .="\n\nClick on the answer.";
break;
case "expressioninpage":
$content = ReadPage($sourcepage,READPAGE_CURRENT);
$text = $content["text"];
$lines = explode("\n",$text);
$pickedline = $lines[array_rand($lines)];
unset($lines[array_search($pickedline,$lines)]);
while (( strlen($pickedline) < $minlinelength ) & & ( count($lines) > 0 ) ) {
$pickedline = $lines[rand(0,count($lines)-1)];
unset($lines[array_search($pickedline,$lines)]);
}
$answers[] = $sourcepage;
//randomly pick n-1 others pages which may not be amongst the list of linked page
for ($i=1;$i< $n;$i++) {
$otherpage = $pages[array_rand($pages)];
unset($pages[array_search($otherpage,$pages)]);
$answers[] = $otherpage;
}
//display
$result .= "Does the page expression \n* [@$pickedline@] \n\nbelongs to ";
// there should now be at least 1 needle in the haystack, the first one
// note that there might be more but this is not a problem as long as the question is stated clearly.
shuffle($answers);
for ($i=0;$i< $n;$i++) {
$hashedanswer = md5($answers[$i]);
if ( $i == count($answers) -1 )
$result .= " or ";
$result .="[[$pagename?action=ExercisesCheck& ".$counterparam
."type=$type& source=$sourcepage& answer=$hashedanswer|".$answers[$i]."]], ";
}
$result .="?\n\nClick on the answer.";
// explode content by line "%0a" or word " "
break;
default:
$result .= "Exercise type unknown.";
}
unset($availableexercisetypes[array_search($type,$availableexercisetypes)]);
if (count($availableexercisetypes) > 0) {
$result .= "\n\nTry other types of exercises:";
foreach ($availableexercisetypes as $e) {
$result .= " [[$pagename?action=Exercises&type=$e|$e]],";
}
$result .= " or a [[$pagename?action=Exercises& type=random|random]] one.";
if ( $counter > 0) {
$result .= " (note that it resets the counter)";
}
}
$result .= "\n\n%center%[-Generated by [[http://fabien.benetou.fr/MemoryRecalls/ImprovingPIM#PIMBasedExercises|PIM Based Exercises]].-]%%";
$renderedresult = MarkupToHTML($pagename, $result);
print "< html > ".$verbatimresult.$renderedresult."< / html > ";
//$text = RetrieveAuthSection($pagename,$auth);
//$content = MarkupToHTML($pagename, $text);
//print $content;
}
SDV($HandleActions['ExercisesCheck'], 'ExercisesCheck');
SDV($HandleAuth['ExercisesCheck'],'read');
function ExercisesCheck($pagename, $auth){
global $ScriptUrl, $FarmD, $Author;
$activityfile = $FarmD."/wiki.d/.exercisesscores";
$type = $_GET["type"];
$sourcepage = $_GET["source"];
$answer = $_GET["answer"];
$counter = 0;
if ( isset($_GET["counter"]) )
$counter = (int) $_GET["counter"];
$score_lower_threshold = 3;
// this could also be a value relative to the top score if there is one
list($group,$page) = explode(".",$pagename);
if ($group == "AllPages"){
// equivalent to getting ALL pages
$pages = ListPages();
} else {
$pages = ListPages("/$group\./e");
}
switch ($type) {
case "pagelink":
$content = ReadPage($sourcepage,READPAGE_CURRENT);
$links = $content["targets"];
$links_array = explode(",",$links);
$formattedlinks = implode(", ",$links_array);
if ( in_array($answer,$links_array) ) {
$result .="Excellent! [[$sourcepage]] is indeed linked to [[$answer]]. ";
$result .="Note that $formattedlinks also are. ";
if ( $counter > 0) {
$counter++;
$result .="\n\nSee if you can [[$pagename?action=Exercises& type=$type& counter=$counter|solve yet another one]]. ";
} else {
$result .="\n\nSee if you can [[$pagename?action=Exercises& type=$type& counter=1|solve yet another one]]. ";
}
} else {
$result .="No, [[$sourcepage]] is not linked to [[$answer]] ";
$result .="but $formattedlinks are. ";
if ($counter > 0) $result .="\nIt means you are losing your $counter points.";
$result .="\n\nTry to redeem yourself by [[$pagename?action=Exercises& type=$type|trying another time]]. ";
}
break;
case "historyvisualization":
if ( md5($sourcepage) === $answer ) {
$result .="Excellent! [[$sourcepage]] indeed has that history of editions.";
if ( $counter > 0) {
$counter++;
$result .="\n\nSee if you can [[$pagename?action=Exercises& type=$type& counter=$counter|solve yet another one]]. ";
} else {
$result .="\n\nSee if you can [[$pagename?action=Exercises& type=$type& counter=1|solve yet another one]]. ";
}
} else {
$result .="No, it was the visualization of [[$sourcepage]] history of editions.";
// display it again
if ($counter > 0) $result .="\nIt means you are losing your $counter points.";
$result .="\n\nTry to redeem yourself by [[$pagename?action=Exercises& type=$type|trying another time]]. ";
}
break;
case "pagehasexpression":
if ( md5($sourcepage) === $answer ) {
$result .="Excellent! [[$sourcepage]] indeed has that expression.";
if ( $counter > 0) {
$counter++;
$result .="\n\nSee if you can [[$pagename?action=Exercises& type=$type& counter=$counter|solve yet another one]]. ";
} else {
$result .="\n\nSee if you can [[$pagename?action=Exercises& type=$type& counter=1|solve yet another one]]. ";
}
} else {
$result .="No, it was the expression of [[$sourcepage]].";
// display it again
if ($counter > 0) $result .="\nIt means you are losing your $counter points.";
$result .="\n\nTry to redeem yourself by [[$pagename?action=Exercises& type=$type|trying another time]]. ";
}
break;
case "expressioninpage":
if ( md5($sourcepage) === $answer ) {
$result .="Excellent! that expression did come from [[$sourcepage]].";
if ( $counter > 0) {
$counter++;
$result .="\n\nSee if you can [[$pagename?action=Exercises& type=$type& counter=$counter|solve yet another one]]. ";
} else {
$result .="\n\nSee if you can [[$pagename?action=Exercises& type=$type& counter=1|solve yet another one]]. ";
}
} else {
$result .="No, [[$sourcepage]] was the page that expression came from.";
// display it again
if ($counter > 0) $result .="\nIt means you are losing your $counter points.";
$result .="\n\nTry to redeem yourself by [[$pagename?action=Exercises& type=$type|trying another time]]. ";
}
break;
default:
$result .= "Exercise type unknown, this most likely indicate that the checking for the solution has not yet been implemented, please consider notifying the author. For now try [[$pagename?action=Exercises& type=random|a random type of exercise]].";
}
$result .= "\n\n%center%[-Generated by [[http://fabien.benetou.fr/MemoryRecalls/ImprovingPIM#PIMBasedExercises|PIM Based Exercises]].-]%%";
$renderedresult = MarkupToHTML($pagename, $result);
print $renderedresult;
if ( ( isset($Author) ) & & ( $counter > $score_lower_threshold ) ) {
$score = "$counter,$Author,$type,".time()."\n";
$write_result = file_put_contents($activityfile,$score, FILE_APPEND | LOCK_EX);
if ($write_result < 1 )
print "There seems to be an error updating the score file, please check that $activityfile has write permission for httpd.";
}
}
Markup("exercisesresults", "directives", "/\(:exercisesresults:\)/", ExercisesResults());
# markup to display results
# default to all participant, not just the currently logged in user
# hall of fame could be useful in collaborative learning wikis
# consider first sparklines for visuals
function ExercisesResults(){
global $ScriptUrl, $FarmD, $Author;
$availableexercisetypes = array("pagelink", "expressioninpage", "pagehasexpression", "historyvisualization");
$activityfile = $FarmD."/wiki.d/.exercisesscores";
if (file_exists($activityfile)) {
$raw_scores = file_get_contents($activityfile);
$allscores = explode("\n",$raw_scores);
rsort($allscores,SORT_NUMERIC);
//dirty, but array_filter with an lambda to test for current exercise didn't work
foreach ($allscores as $score ) {
list($counter,$author,$type,$time) = explode(",",$score);
if ($counter.$author.$type.$time != "")
$structuredanswers[] = array("counter"=>$counter,"author"=>$author,"type"=>$type,"time"=>$time);
}
// could instead filter by exercise and sort by highest score, listing the top3 score per per exercise
$past_scores .= "< table > < tr > ";
foreach ($availableexercisetypes as $exercisetype){
$past_scores .= "< td > < a href = \"?action=Exercises&type=$exercisetype\" > $exercisetype< / a > < / td > ";
}
$past_scores .= "< / tr > < tr > ";
foreach ($availableexercisetypes as $exercisetype){
$past_scores .= "< td valign = \"top\" > ";
$answers_added = 1;
foreach ($structuredanswers as $sa){
if ($sa["type"]==$exercisetype & & $answers_added< 4 ) {
$past_scores .= "".$sa["counter"]." done by ".$sa["author"]." (".date("H:m m/d/y",$sa["time"]).")< br / > ";
$answers_added++;
}
}
$past_scores .= "< / td > ";
}
$past_scores .= "< / tr > < / table > ";
} else {
$past_scores = "Currently no scores have been recorded. Consider starting an exercise first";
// print "Go above the minimum threshold of good answers while being logged in.";
// removed for now since the threshold is at 1
}
return $past_scores;
}
?>