Get practice data for this student and create flashcards for them is they are newcomers.
def _get_practice_data(user, timezoneoffset, db):
practice_message1 = ""
practice_message2 = ""
practice_completion_count = 0
remaining_days = 0
max_days = 0
max_questions = 0
day_points = 0
question_points = 0
presentable_flashcards = []
available_flashcards_num = 0
practiced_today_count = 0
practice_today_left = 0
points_received = 0
total_possible_points = 0
flashcard_creation_method = 2
questions_to_complete_day = 0
practice_graded = 1
spacing = 0
interleaving = 0
now = datetime.datetime.utcnow()
now_local = now - datetime.timedelta(hours=timezoneoffset)
Since each authenticated user has only one active course, we retrieve the course this way.
course = db(db.courses.id == user.course_id).select().first()
practice_settings = db(db.course_practice.course_name == user.course_name)
if (
practice_settings.isempty()
or practice_settings.select().first().end_date is None
):
practice_message1 = "Practice tool is not set up for this course yet."
practice_message2 = "Please ask your instructor to set it up."
else:
practice_settings = practice_settings.select().first()
practice_start_date = practice_settings.start_date
flashcard_creation_method = practice_settings.flashcard_creation_method
Calculates the remaining days to the end of the semester.
Define how many questions you expect your students practice every day.
questions_to_complete_day = practice_settings.questions_to_complete_day
practice_graded = practice_settings.graded
spacing = practice_settings.spacing
interleaving = practice_settings.interleaving
if practice_start_date > now_local.date():
days_to_start = (practice_start_date - now_local.date()).days
practice_message1 = (
"Practice period will start in this course on "
+ str(practice_start_date)
+ "."
)
practice_message2 = (
"Please return in "
+ str(days_to_start)
+ " day"
+ ("." if days_to_start == 1 else "s.")
)
else:
Check whether flashcards are created for this user in the current course.
flashcards = db(
(db.user_topic_practice.course_name == user.course_name)
& (db.user_topic_practice.user_id == user.id)
)
if flashcards.isempty():
if flashcard_creation_method == 0:
practice_message1 = (
"Only pages that you mark as complete, at the bottom of the page, are the"
+ " ones that are eligible for practice."
)
practice_message2 = (
"You've not marked any pages as complete yet. Please mark some pages first"
+ " to practice them."
)
else:
new student; create flashcards We only create flashcards for those sections that are marked by the instructor as taught.
subchaptersTaught = db(
(db.sub_chapter_taught.course_name == user.course_name)
& (
db.sub_chapter_taught.chapter_label
== db.chapters.chapter_label
)
& (
db.sub_chapter_taught.sub_chapter_label
== db.sub_chapters.sub_chapter_label
)
& (db.chapters.course_id == user.course_name)
& (db.sub_chapters.chapter_id == db.chapters.id)
)
if subchaptersTaught.isempty():
practice_message1 = (
"The practice period is already started, but your instructor has not"
+ " added topics of your course to practice."
)
practice_message2 = (
"Please ask your instructor to add topics to practice."
)
else:
subchaptersTaught = subchaptersTaught.select(
db.chapters.chapter_label,
db.chapters.chapter_name,
db.sub_chapters.sub_chapter_label,
orderby=db.chapters.id | db.sub_chapters.id,
)
for subchapterTaught in subchaptersTaught:
We only retrieve questions to be used in flashcards if they are marked for practice purpose.
There is at least one qualified question in this subchapter, so insert a flashcard for the subchapter.
Treat it as if the first eligible question is the last one asked.
add as if yesterday, so can practice right away
Retrieve all the flashcards created for this user in the current course and order them by their order of creation.
We need the following for
loop to make sure the number of repetitions for both blocking and interleaving
groups are the same.
for f in flashcards:
f_logs = db(
(db.user_topic_practice_log.course_name == user.course_name)
& (db.user_topic_practice_log.user_id == user.id)
& (db.user_topic_practice_log.chapter_label == f.chapter_label)
& (
db.user_topic_practice_log.sub_chapter_label
== f.sub_chapter_label
)
).select(orderby=db.user_topic_practice_log.end_practice)
f["blocking_eligible_date"] = f.next_eligible_date
if len(f_logs) > 0:
days_to_add = sum([f_log.i_interval for f_log in f_logs[0:-1]])
days_to_add -= (
f_logs[-1].end_practice - f_logs[0].end_practice
).days
if days_to_add > 0:
f["blocking_eligible_date"] += datetime.timedelta(
days=days_to_add
)
if interleaving == 1:
Select only those where enough time has passed since last presentation.
Select only those that are not mastered yet.
presentable_flashcards = [
f
for f in flashcards
if (
f.q * f.e_factor < 12.5
and f.blocking_eligible_date < practice_settings.end_date
and (
f.q != -1
or (f.next_eligible_date - now_local.date()).days != 1
)
)
]
available_flashcards_num = len(presentable_flashcards)
if len(presentable_flashcards) > 0:
It’s okay to continue with the next chapter if there is no more question in the current chapter eligible to be asked (not postponed). Note that this is not an implementation of pure blocking, because a postponed question from the current chapter could be asked tomorrow, after some questions from the next chapter that are asked today.
How many times has this user submitted their practice from the beginning of today (12:00 am) till now?
practiced_log = db(
(db.user_topic_practice_log.course_name == user.course_name)
& (db.user_topic_practice_log.user_id == user.id)
& (db.user_topic_practice_log.q != 0)
& (db.user_topic_practice_log.q != -1)
).select()
practiced_today_count = 0
for pr in practiced_log:
if pr.end_practice - datetime.timedelta(
hours=pr.timezoneoffset
) >= datetime.datetime(
now_local.year, now_local.month, now_local.day, 0, 0, 0, 0
):
practiced_today_count += 1
practice_completion_count = _get_practice_completion(
user.id, user.course_name, spacing, db
)
if practice_graded == 1:
if spacing == 1:
total_possible_points = practice_settings.day_points * max_days
points_received = round(day_points * practice_completion_count, 2)
else:
total_possible_points = (
practice_settings.question_points * max_questions
)
points_received = round(
question_points * practice_completion_count, 2
)
Calculate the number of questions left for the student to practice today to get the completion point.
if spacing == 1:
practice_today_left = min(
available_flashcards_num,
max(0, questions_to_complete_day - practiced_today_count),
)
else:
practice_today_left = available_flashcards_num
return (
now,
now_local,
practice_message1,
practice_message2,
practice_graded,
spacing,
interleaving,
practice_completion_count,
remaining_days,
max_days,
max_questions,
day_points,
question_points,
presentable_flashcards,
available_flashcards_num,
practiced_today_count,
questions_to_complete_day,
practice_today_left,
points_received,
total_possible_points,
flashcard_creation_method,
)
def _get_practice_completion(user_id, course_name, spacing, db):
if spacing == 1:
return db(
(db.user_topic_practice_completion.course_name == course_name)
& (db.user_topic_practice_completion.user_id == user_id)
).count()
return db(
(db.user_topic_practice_log.course_name == course_name)
& (db.user_topic_practice_log.user_id == user_id)
& (db.user_topic_practice_log.q != 0)
& (db.user_topic_practice_log.q != -1)
).count()
Only questions that are marked for practice are eligible for the spaced practice.
def _get_qualified_questions(base_course, chapter_label, sub_chapter_label, db):
return db(
(db.questions.base_course == base_course)
& (
(db.questions.topic == "{}/{}".format(chapter_label, sub_chapter_label))
| (
(db.questions.chapter == chapter_label)
& (db.questions.topic == None) # noqa: E711
& (db.questions.subchapter == sub_chapter_label)
)
)
& (db.questions.practice == True) # noqa: E712
& (db.questions.review_flag == False)
).select()