Base diagnostic algorithm
state (Produces the initial value of the state object): // should be renamed "initial_state"
[
"topics": map(
[
"topic": topic,
"status": "unknown" // "unknown", "passed", or "failed"
],
topic,
values(topics)
),
"retries": 3,
"finished": false,
]
topics_by_objective (A dictionary mapping a learning objective name to a list of indices of topics):
dict(map(
let(
ltopics, values(topics),
indices, filter(lo["name"] in ltopics[j]["learning_objectives"], j, 0..len(ltopics)-1),
[lo["name"],indices]
),
lo,
learning_objectives
))
unknown_topics (Which topics are still unknown?):
map(x["topic"],x,filter(x["status"]="unknown",x,state["topics"]))
first_topic (The first topic to pick a question on):
unknown_topics[floor(len(unknown_topics)/2)]["name"]
first_question (The first question to show the student):
random(topics[first_topic]["questions"])
get_dependents (An expression which gets the topics to update after answering a question):
expression("""
[target] + flatten(map(eval(get_dependents,["target":t,"correct":correct]),t,topics[target][if(correct,"depends_on","leads_to")]))
""")
correct (Did the student get the current question right?):
current_question["credit"]=1
after_answering (Update the state after the student answers a question):
let(
ntopics, eval(get_dependents,["target":current_topic,"correct":correct])
, nstate, state + ['topics': map(
if(tstate["topic"]["name"] in ntopics, tstate + ["status":if(correct,"passed","failed")], tstate),
tstate,
state["topics"]
)]
, nstate
)
action_retry (Use up one retry and visit the same topic again):
[
"label": translate("diagnostic.use retry"),
"state": state + ["retries": state["retries"]-1],
"next_question": random(topics[current_topic]["questions"])
]
action_stop (Stop the exam):
[
"label": translate("diagnostic.end test"),
"state": state,
"next_question": nothing
]
action_move_on (Move to the next topic, or end the exam if there are no more):
let(
state, after_answering,
immediate_next_topics, topics[current_topic][if(correct, "leads_to", "depends_on")],
unknown_topics, map(x["topic"],x,filter(x["status"]="unknown",x,state["topics"])),
unknown_immediate_topics, filter(x["name"] in immediate_next_topics,x,unknown_topics),
next_topics, if(len(unknown_immediate_topics), unknown_immediate_topics, unknown_topics),
finished, len(next_topics)=0 or state["finished"],
topic,
if(not finished,
next_topics[floor(len(next_topics)/2)]["name"]
,
nothing
),
[
"label": translate("diagnostic.move to next topic"),
"state": after_answering,
"next_question": if(not finished, random(topics[topic]["questions"]), nothing)
]
)
can_move_on:
action_move_on["next_question"]<>nothing
next_actions (Actions to offer to the student when they ask to move on):
let(
feedback, retries_feedback+"\n\n"+translate("diagnostic.next step question")
, [
"feedback": feedback,
"actions": if(not correct and state["retries"]>0, [action_retry], []) + if(can_move_on,[action_move_on],[action_stop])
]
)
after_exam_ended (Update the state after the exam ends):
let(
state, after_answering,
ntopics, map(t+["status": if(t["status"]="unknown","failed",t["status"])],t,state["topics"]),
state+["finished": true]
)
finished (Is the test finished? True if there are no unknown topics):
len(unknown_topics)=0 or state["finished"]
total_progress:
let(
num_topics, len(state["topics"]),
known, filter(tstate["status"]<>"unknown",tstate,state["topics"]),
passed, filter(tstate["status"]="passed",tstate,known),
num_known, len(known),
num_passed, len(passed),
[
"name": translate("control.total"),
"progress": if(num_topics>0,num_known/num_topics,0),
"credit": if(num_known>0,num_passed/num_topics,0)
]
)
learning_objective_progress:
map(
let(
tstates, map(state["topics"][j],j,topics_by_objective[lo["name"]]),
known, filter(tstate["status"]<>"unknown",tstate,tstates),
passed, filter(tstate["status"]="passed",tstate,known),
num_topics, len(tstates),
num_known, len(known),
num_passed, len(passed),
["name": lo["name"], "progress": if(num_topics>0,num_known/num_topics,0), "credit": if(finished,num_passed/num_topics,if(num_known>0,num_passed/num_known,0))]
),
lo,
learning_objectives
)
progress (Progress on each of the learning objectives, plus total progress):
learning_objective_progress+
total_progress
retries_feedback:
translate("diagnostic.now assessing topic", ["current_topic": current_topic]) + " " +
let(
retries, state["retries"],
pluralise(retries, translate("diagnostic.one retry left"), translate("diagnostic.retries left", ["retries": retries ]))
)
+ " " +
let(
p,total_progress["progress"],
percentage, dpformat(100p, 0),
translate("diagnostic.percentage completed", ["percentage": percentage])
)
weak_objective_threshold (The amount of credit below which a learning objective is considered weak):
0.6
finished_feedback:
let(
weak_objectives, filter(p["credit"]<weak_objective_threshold, p, learning_objective_progress),
//
translate("diagnostic.test is over") + " "
+
if(len(weak_objectives)=0,
translate("diagnostic.passed all lo")
,
translate("diagnostic.more work on lo", ["los":
join(map(lo["name"], lo, weak_objectives),", ")])
)
)
feedback:
if(finished,
finished_feedback,
retries_feedback
)