def studentreport(): data_analyzer = DashboardDataAnalyzer(auth.user.course_id) for_dashboard = verifyInstructorStatus(auth.user.course_id, auth.user.id) if "id" in request.vars and for_dashboard: sid = request.vars.id else: sid = auth.user.username response.view = "assignments/index.html" data_analyzer.load_user_metrics(sid) data_analyzer.load_assignment_metrics(sid, not for_dashboard) chapters = [] for chapter_label, chapter in six.iteritems( data_analyzer.chapter_progress.chapters): chapters.append({ "label": chapter.chapter_label, "status": chapter.status_text(), "subchapters": chapter.get_sub_chapter_progress(), }) activity = data_analyzer.formatted_activity logger.debug("GRADES = %s", data_analyzer.grades) pd_dict = dict() if response.view == "assignments/index.html": ( pd_dict["now"], pd_dict["now_local"], pd_dict["practice_message1"], pd_dict["practice_message2"], pd_dict["practice_graded"], pd_dict["spacing"], pd_dict["interleaving"], pd_dict["practice_completion_count"], pd_dict["remaining_days"], pd_dict["max_days"], pd_dict["max_questions"], pd_dict["day_points"], pd_dict["question_points"], pd_dict["presentable_flashcards"], pd_dict["flashcard_count"], pd_dict["practiced_today_count"], pd_dict["questions_to_complete_day"], pd_dict["practice_today_left"], pd_dict["points_received"], pd_dict["total_possible_points"], pd_dict["flashcard_creation_method"], ) = _get_practice_data( auth.user, float(session.timezoneoffset) if "timezoneoffset" in session else 0, db, ) pd_dict["total_today_count"] = min( pd_dict["practice_today_left"] + pd_dict["practiced_today_count"], pd_dict["questions_to_complete_day"], ) if request.vars.action == "dlcsv": mtbl = pd.read_sql_query( """ select * from useinfo where sid = %(sid)s and course_id = %(course)s """, settings.database_uri, params={ "sid": sid, "course": auth.user.course_name }, ) response.headers["Content-Type"] = "application/vnd.ms-excel" response.headers[ "Content-Disposition"] = "attachment; filename=data_for_{}.csv".format( sid) session.flash = f"Downloading to data_for_{sid}.csv" return mtbl.to_csv(na_rep=" ") if request.vars.action == "dlcode": mtbl = pd.read_sql_query( """ select * from code where sid = %(sid)s and course_id = %(course)s """, settings.database_uri, params={ "sid": auth.user.username, "course": auth.user.course_id }, ) response.headers["Content-Type"] = "application/vnd.ms-excel" response.headers[ "Content-Disposition"] = "attachment; filename=code_for_{}.csv".format( sid) session.flash = f"Downloading to code_for_{sid}.csv" return mtbl.to_csv(na_rep=" ") return dict( course=get_course_row(db.courses.ALL), user=data_analyzer.user, chapters=chapters, activity=activity, assignments=data_analyzer.grades, **pd_dict, )
def practice(): if not session.timezoneoffset: session.timezoneoffset = 0 feedback_saved = request.vars.get("feedback_saved", None) if feedback_saved is None: feedback_saved = "" ( now, now_local, message1, 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, ) = _get_practice_data( auth.user, float(session.timezoneoffset) if "timezoneoffset" in session else 0, db, ) try: db.useinfo.insert( sid=auth.user.username, act=message1 or "beginning practice", div_id="/runestone/assignments/practice", event="practice", timestamp=datetime.datetime.utcnow(), course_id=auth.user.course_name, ) except Exception as e: logger.error(f"failed to insert log record for practice: {e}") if message1 != "": # session.flash = message1 + " " + message2 return redirect( URL("practiceNotStartedYet", vars=dict(message1=message1, message2=message2))) # Since each authenticated user has only one active course, we retrieve the course this way. course = db(db.courses.id == auth.user.course_id).select().first() all_flashcards = db( (db.user_topic_practice.course_name == auth.user.course_name) & (db.user_topic_practice.user_id == auth.user.id) & (db.user_topic_practice.chapter_label == db.chapters.chapter_label) & (db.user_topic_practice.sub_chapter_label == db.sub_chapters.sub_chapter_label) & (db.chapters.course_id == course.base_course) & (db.sub_chapters.chapter_id == db.chapters.id)).select( db.chapters.chapter_name, db.sub_chapters.sub_chapter_name, db.user_topic_practice.i_interval, db.user_topic_practice.next_eligible_date, db.user_topic_practice.e_factor, db.user_topic_practice.q, db.user_topic_practice.last_completed, orderby=db.user_topic_practice.id, ) for f_card in all_flashcards: if interleaving == 1: f_card["remaining_days"] = max( 0, (f_card.user_topic_practice.next_eligible_date - now_local.date()).days, ) # f_card["mastery_percent"] = int(100 * f_card["remaining_days"] // 55) f_card["mastery_percent"] = int(f_card["remaining_days"]) else: # The maximum q is 5.0 and the minimum e_factor that indicates mastery of the topic is 2.5. `5 * 2.5 = 12.5` # I learned that when students under the blocking condition answer something wrong multiple times, # it becomes too difficult for them to pass it and the system asks them the same question many times # (because most subchapters have only one question). To solve this issue, I changed the blocking formula. f_card["mastery_percent"] = int( 100 * f_card.user_topic_practice.e_factor * f_card.user_topic_practice.q / 12.5) if f_card["mastery_percent"] > 100: f_card["mastery_percent"] = 100 f_card["mastery_color"] = "danger" if f_card["mastery_percent"] >= 75: f_card["mastery_color"] = "success" elif f_card["mastery_percent"] >= 50: f_card["mastery_color"] = "info" elif f_card["mastery_percent"] >= 25: f_card["mastery_color"] = "warning" # If an instructor removes the practice flag from a question in the middle of the semester # and students are in the middle of practicing it, the following code makes sure the practice tool does not crash. questions = [] if len(presentable_flashcards) > 0: # Present the first one. flashcard = presentable_flashcards[0] # Get eligible questions. questions = _get_qualified_questions(course.base_course, flashcard.chapter_label, flashcard.sub_chapter_label, db) # If the student has any flashcards to practice and has not practiced enough to get their points for today or they # have intrinsic motivation to practice beyond what they are expected to do. if (available_flashcards_num > 0 and len(questions) > 0 and (practiced_today_count != questions_to_complete_day or request.vars.willing_to_continue or spacing == 0)): # Find index of the last question asked. question_names = [q.name for q in questions] try: qIndex = question_names.index(flashcard.question_name) except Exception: qIndex = 0 # present the next one in the list after the last one that was asked question = questions[(qIndex + 1) % len(questions)] # This replacement is to render images question.htmlsrc = question.htmlsrc.replace( 'src="../_static/', 'src="' + get_course_url("_static/")) question.htmlsrc = question.htmlsrc.replace("../_images/", get_course_url("_images/")) autogradable = 1 # If it is possible to autograde it: if (question.autograde is not None) or (question.question_type is not None and question.question_type in [ "mchoice", "parsonsprob", "fillintheblank", "clickablearea", "dragndrop", ]): autogradable = 2 questioninfo = [ question.htmlsrc, question.name, question.id, autogradable ] # This is required to check the same question in do_check_answer(). flashcard.question_name = question.name # This is required to only check answers after this timestamp in do_check_answer(). flashcard.last_presented = now flashcard.timezoneoffset = (float(session.timezoneoffset) if "timezoneoffset" in session else 0) flashcard.update_record() else: questioninfo = None # Add a practice completion record for today, if there isn't one already. practice_completion_today = db( (db.user_topic_practice_completion.course_name == auth.user.course_name) & (db.user_topic_practice_completion.user_id == auth.user.id) & (db.user_topic_practice_completion.practice_completion_date == now_local.date())) if practice_completion_today.isempty(): db.user_topic_practice_completion.insert( user_id=auth.user.id, course_name=auth.user.course_name, practice_completion_date=now_local.date(), ) practice_completion_count = _get_practice_completion( auth.user.id, auth.user.course_name, spacing, db) if practice_graded == 1: # send practice grade via lti, if setup for that lti_record = _get_lti_record(session.oauth_consumer_key) practice_grade = _get_student_practice_grade( auth.user.id, auth.user.course_name) course_settings = _get_course_practice_record( auth.user.course_name) if spacing == 1: total_possible_points = day_points * max_days points_received = day_points * practice_completion_count else: total_possible_points = question_points * max_questions points_received = question_points * practice_completion_count if (lti_record and practice_grade and practice_grade.lis_outcome_url and practice_grade.lis_result_sourcedid and course_settings): if spacing == 1: send_lti_grade( assignment_points=max_days, score=practice_completion_count, consumer=lti_record.consumer, secret=lti_record.secret, outcome_url=practice_grade.lis_outcome_url, result_sourcedid=practice_grade. lis_result_sourcedid, ) else: send_lti_grade( assignment_points=max_questions, score=practice_completion_count, consumer=lti_record.consumer, secret=lti_record.secret, outcome_url=practice_grade.lis_outcome_url, result_sourcedid=practice_grade. lis_result_sourcedid, ) set_latex_preamble(course.base_course) return dict( course=course, q=questioninfo, all_flashcards=all_flashcards, flashcard_count=available_flashcards_num, # The number of days the student has completed their practice. practice_completion_count=practice_completion_count, remaining_days=remaining_days, max_questions=max_questions, max_days=max_days, # The number of times remaining to practice today to get the completion point. practice_today_left=practice_today_left, # The number of times this user has submitted their practice from the beginning of today (12:00 am) # till now. practiced_today_count=practiced_today_count, total_today_count=min(practice_today_left + practiced_today_count, questions_to_complete_day), questions_to_complete_day=questions_to_complete_day, points_received=points_received, total_possible_points=total_possible_points, practice_graded=practice_graded, spacing=spacing, interleaving=interleaving, flashcard_creation_method=flashcard_creation_method, feedback_saved=feedback_saved, )
def studentreport(): data_analyzer = DashboardDataAnalyzer(auth.user.course_id) for_dashboard = verifyInstructorStatus(auth.user.course_id, auth.user.id) if "id" in request.vars and for_dashboard: sid = request.vars.id else: sid = auth.user.username response.view = "assignments/index.html" data_analyzer.load_user_metrics(sid) data_analyzer.load_assignment_metrics(sid) chapters = [] for chapter_label, chapter in six.iteritems( data_analyzer.chapter_progress.chapters): chapters.append({ "label": chapter.chapter_label, "status": chapter.status_text(), "subchapters": chapter.get_sub_chapter_progress(), }) activity = data_analyzer.formatted_activity logger.debug("GRADES = %s", data_analyzer.grades) pd = dict() if response.view == "assignments/index.html": ( pd["now"], pd["now_local"], pd["practice_message1"], pd["practice_message2"], pd["practice_graded"], pd["spacing"], pd["interleaving"], pd["practice_completion_count"], pd["remaining_days"], pd["max_days"], pd["max_questions"], pd["day_points"], pd["question_points"], pd["presentable_flashcards"], pd["flashcard_count"], pd["practiced_today_count"], pd["questions_to_complete_day"], pd["practice_today_left"], pd["points_received"], pd["total_possible_points"], pd["flashcard_creation_method"], ) = _get_practice_data( auth.user, float(session.timezoneoffset) if "timezoneoffset" in session else 0, db, ) pd["total_today_count"] = min( pd["practice_today_left"] + pd["practiced_today_count"], pd["questions_to_complete_day"], ) return dict(course=get_course_row(db.courses.ALL), user=data_analyzer.user, chapters=chapters, activity=activity, assignments=data_analyzer.grades, **pd)