def prejoin_class(class_id, link): Class = DATABASE.get_class(class_id) if not Class or Class['link'] != link: return 'No such class', 404 user = {} if request.cookies.get(cookie_name): token = DATABASE.get_token(request.cookies.get(cookie_name)) if token: if token['username'] in Class.get('students', []): return render_template( 'class-already-joined.html', lang=requested_lang(), auth=TRANSLATIONS.get_translations( requested_lang(), 'Auth'), menu=render_main_menu('my-profile'), username=current_user(request)['username'], current_page='my-profile', class_info={'name': Class['name']}) user = DATABASE.user_by_username(token['username']) return render_template( 'class-prejoin.html', lang=requested_lang(), auth=TRANSLATIONS.get_translations(requested_lang(), 'Auth'), menu=render_main_menu('my-profile'), username=current_user(request)['username'], is_teacher=is_teacher(request), current_page='my-profile', class_info={ 'link': os.getenv('BASE_URL') + '/class/' + Class['id'] + '/join/' + Class['link'] + '?lang=' + requested_lang(), 'name': Class['name'] })
def render_code_editor_with_tabs(request, level_defaults, lang, max_level, level_number, menu, translations, version, loaded_program, adventures, adventure_name): if not level_defaults: return utils.page_404 (translations, menu, current_user(request) ['username'], lang, translations.get_translations (lang, 'ui').get ('no_such_level')) arguments_dict = {} # Meta stuff arguments_dict['level_nr'] = str(level_number) arguments_dict['lang'] = lang arguments_dict['level'] = level_number arguments_dict['prev_level'] = int(level_number) - 1 if int(level_number) > 1 else None arguments_dict['next_level'] = int(level_number) + 1 if int(level_number) < max_level else None arguments_dict['menu'] = menu arguments_dict['latest'] = version arguments_dict['selected_page'] = 'code' arguments_dict['page_title'] = f'Level {level_number} – Hedy' arguments_dict['auth'] = translations.get_translations (lang, 'Auth') arguments_dict['username'] = current_user(request) ['username'] arguments_dict['is_teacher'] = is_teacher(request) arguments_dict['loaded_program'] = loaded_program arguments_dict['adventures'] = adventures arguments_dict['adventure_name'] = adventure_name # Translations arguments_dict.update(**translations.get_translations(lang, 'ui')) # Merge level defaults into adventures so it is rendered as the first tab arguments_dict.update(**attr.asdict(level_defaults)) return render_template("code-page.html", **arguments_dict)
def get_quiz(level_source, question_nr, attempt): if not config.get('quiz-enabled') and g.lang != 'nl': return 'Hedy quiz disabled!', 404 else: # Reading the yaml file quiz_data = quiz_data_file_for(level_source) if not quiz_data.exists(): return 'No quiz yaml file found for this level', 404 # set globals g.lang = lang = requested_lang() g.prefix = '/hedy' # Loop through the questions and check that the loop doesn't reach out of bounds q_nr = int(question_nr) if int(attempt) == 1: questionStatus = 'start' if q_nr <= len(quiz_data['questions']): question = quiz_data['questions'][q_nr - 1].get(q_nr) # Convert the indices to the corresponding characters char_array = [] for i in range(len(question['mp_choice_options'])): char_array.append(chr(ord('@') + (i + 1))) return render_template( 'quiz_question.html', quiz=quiz_data, level_source=level_source, questionStatus=questionStatus, questions=quiz_data['questions'], question=quiz_data['questions'][q_nr - 1].get(q_nr), question_nr=q_nr, correct=session.get('correct_answer'), attempt=attempt, char_array=char_array, menu=render_main_menu('adventures'), lang=lang, username=current_user(request)['username'], is_teacher=is_teacher(request), auth=TRANSLATIONS.get_translations(requested_lang(), 'Auth')) else: return render_template('endquiz.html', correct=session.get('correct_answer'), total_score=session.get('total_score'), menu=render_main_menu('adventures'), lang=lang, quiz=quiz_data, level=int(level_source) + 1, questions=quiz_data['questions'], next_assignment=1, username=current_user(request)['username'], is_teacher=is_teacher(request), auth=TRANSLATIONS.get_translations( requested_lang(), 'Auth'))
def load_adventures_per_level(lang, level): loaded_programs = {} # If user is logged in, we iterate their programs that belong to the current level. Out of these, we keep the latest created program for both the level mode (no adventure) and for each of the adventures. if current_user(request)['username']: user_programs = DATABASE.programs_for_user( current_user(request)['username']) for program in user_programs: if program['level'] != level: continue program_key = 'level' if not program.get( 'adventure_name') else program['adventure_name'] if not program_key in loaded_programs: loaded_programs[program_key] = program elif loaded_programs[program_key]['date'] < program['date']: loaded_programs[program_key] = program all_adventures = [] adventures = load_adventure_for_language(lang)['adventures'] for short_name, adventure in adventures.items(): if not level in adventure['levels']: continue # end adventure is the quiz # if quizzes are not enabled, do not load it if short_name == 'end' and not config['quiz-enabled']: continue all_adventures.append({ 'short_name': short_name, 'name': adventure['name'], 'image': adventure.get('image', None), 'default_save_name': adventure['default_save_name'], 'text': adventure['levels'][level].get('story_text', 'No Story Text'), 'start_code': adventure['levels'][level].get('start_code', ''), 'loaded_program': '' if not loaded_programs.get(short_name) else { 'name': loaded_programs.get(short_name)['name'], 'code': loaded_programs.get(short_name)['code'] } }) # We create a 'level' pseudo assignment to store the loaded program for level mode, if any. all_adventures.append({ 'short_name': 'level', 'loaded_program': '' if not loaded_programs.get('level') else { 'name': loaded_programs.get('level')['name'], 'code': loaded_programs.get('level')['code'] } }) return all_adventures
def load_adventure_assignments_per_level(lang, level): loaded_programs = {} # If user is logged in, we iterate their programs that belong to the current level. Out of these, we keep the latest created program for both the level mode (no adventure) and for each of the adventures. if current_user(request)['username']: user_programs = db_get_many( 'programs', {'username': current_user(request)['username']}, True) for program in user_programs: if program['level'] != level: continue program_key = 'level' if not program.get( 'adventure_name') else program['adventure_name'] if not program_key in loaded_programs: loaded_programs[program_key] = program elif loaded_programs[program_key]['date'] < program['date']: loaded_programs[program_key] = program assignments = [] adventures = load_adventure_for_language(lang)['adventures'] for short_name, adventure in adventures.items(): if not level in adventure['levels']: continue assignments.append({ 'short_name': short_name, 'name': adventure['name'], 'image': adventure.get('image', None), 'default_save_name': adventure['default_save_name'], 'text': adventure['levels'][level].get('story_text', 'No Story Text'), 'start_code': adventure['levels'][level].get('start_code', ''), 'loaded_program': '' if not loaded_programs.get(short_name) else loaded_programs.get(short_name)['code'], 'loaded_program_name': '' if not loaded_programs.get(short_name) else loaded_programs.get(short_name)['name'] }) # We create a 'level' pseudo assignment to store the loaded program for level mode, if any. assignments.append({ 'short_name': 'level', 'loaded_program': '' if not loaded_programs.get('level') else loaded_programs.get('level')['code'], 'loaded_program_name': '' if not loaded_programs.get('level') else loaded_programs.get('level')['name'] }) return assignments
def get_quiz(level_source, question_nr): if not config['quiz-enabled'] and g.lang != 'nl': return 'Hedy quiz disabled!', 404 else: # Reading the yaml file if os.path.isfile( f'coursedata/quiz/quiz_questions_lvl{level_source}.yaml'): quiz_data = load_yaml( f'coursedata/quiz/quiz_questions_lvl{level_source}.yaml') else: return 'No quiz yaml file found for this level', 404 # set globals g.lang = lang = requested_lang() g.prefix = '/hedy' # Loop through the questions and check that the loop doesn't reach out of bounds q_nr = int(question_nr) if q_nr <= len(quiz_data['questions']): question = quiz_data['questions'][q_nr - 1].get(q_nr) # Convert the indices to the corresponding characters char_array = [] for i in range(len(question['mp_choice_options'])): char_array.append(chr(ord('@') + (i + 1))) return render_template( 'quiz_question.html', quiz=quiz_data, level_source=level_source, questions=quiz_data['questions'], question=quiz_data['questions'][q_nr - 1].get(q_nr), question_nr=q_nr, correct=session.get('correct_answer'), char_array=char_array, menu=render_main_menu('adventures'), lang=lang, username=current_user(request)['username'], auth=TRANSLATIONS.data[requested_lang()]['Auth']) else: return render_template( 'endquiz.html', correct=session.get('correct_answer'), total_score=session.get('total_score'), menu=render_main_menu('adventures'), lang=lang, quiz=quiz_data, level=int(level_source) + 1, questions=quiz_data['questions'], next_assignment=1, username=current_user(request)['username'], auth=TRANSLATIONS.data[requested_lang()]['Auth'])
def main_page(page): if page == 'favicon.ico': abort(404) lang = requested_lang() effective_lang = lang if page in ['signup', 'login', 'my-profile', 'recover', 'reset', 'admin']: return auth_templates(page, lang, render_main_menu(page), request) if page == 'programs': return programs_page(request) # Default to English if requested language is not available if not path.isfile(f'main/{page}-{effective_lang}.md'): effective_lang = 'en' try: with open(f'main/{page}-{effective_lang}.md', 'r', encoding='utf-8') as f: contents = f.read() except IOError: abort(404) front_matter, markdown = split_markdown_front_matter(contents) menu = render_main_menu(page) if page == 'for-teachers': teacher_classes = [] if not current_user( request)['username'] else DATABASE.get_teacher_classes( current_user(request)['username'], True) return render_template('for-teachers.html', sections=split_teacher_docs(contents), lang=lang, menu=menu, username=current_user(request)['username'], is_teacher=is_teacher(request), auth=TRANSLATIONS.get_translations( lang, 'Auth'), teacher_classes=teacher_classes, **front_matter) return render_template('main-page.html', mkd=markdown, lang=lang, menu=menu, username=current_user(request)['username'], is_teacher=is_teacher(request), auth=TRANSLATIONS.get_translations(lang, 'Auth'), **front_matter)
def programs_page (request): username = current_user(request) ['username'] if not username: return "unauthorized", 403 from_user = request.args.get('user') or None if from_user and not is_admin (request): return "unauthorized", 403 texts=TRANSLATIONS.data [requested_lang ()] ['Programs'] ui=TRANSLATIONS.data [requested_lang ()] ['ui'] adventures = load_adventure_for_language(requested_lang ())['adventures'] result = db_get_many ('programs', {'username': from_user or username}, True) programs = [] now = timems () for item in result: measure = texts ['minutes'] date = round ((now - item ['date']) / 60000) if date > 90: measure = texts ['hours'] date = round (date / 60) if date > 36: measure = texts ['days'] date = round (date / 24) programs.append ({'id': item ['id'], 'code': item ['code'], 'date': texts ['ago-1'] + ' ' + str (date) + ' ' + measure + ' ' + texts ['ago-2'], 'level': item ['level'], 'name': item ['name'], 'adventure_name': item.get ('adventure_name'), 'public': item.get ('public')}) return render_template('programs.html', lang=requested_lang(), menu=render_main_menu('programs'), texts=texts, ui=ui, auth=TRANSLATIONS.data [requested_lang ()] ['Auth'], programs=programs, username=username, current_page='programs', from_user=from_user, adventures=adventures)
def main_page(page): if page == 'favicon.ico': abort(404) lang = requested_lang() effective_lang = lang if page in ['signup', 'login', 'my-profile', 'recover', 'reset', 'admin']: return auth_templates(page, lang, render_main_menu(page), request) if page == 'programs': return programs_page(request) # Default to English if requested language is not available if not path.isfile(f'main/{page}-{effective_lang}.md'): effective_lang = 'en' try: with open(f'main/{page}-{effective_lang}.md', 'r', encoding='utf-8') as f: contents = f.read() except IOError: abort(404) front_matter, markdown = split_markdown_front_matter(contents) menu = render_main_menu(page) return render_template('main-page.html', mkd=markdown, lang=lang, menu=menu, username=current_user(request)['username'], auth=TRANSLATIONS.data[lang]['Auth'], **front_matter)
def view_program(id): g.lang = requested_lang() g.prefix = '/hedy' result = DATABASE.program_by_id(id) if not result: return 'No such program', 404 # Default to the language of the program's author (but still respect) # the switch if given. lang = request.args.get("lang") if not lang: lang = result['lang'] arguments_dict = {} arguments_dict['program_id'] = id arguments_dict['page_title'] = f'{result["name"]} – Hedy' arguments_dict['level'] = result['level'] # Necessary for running arguments_dict['loaded_program'] = result arguments_dict['editor_readonly'] = True arguments_dict['show_edit_button'] = True # Everything below this line has nothing to do with this page and it's silly # that every page needs to put in so much effort to re-set it arguments_dict['lang'] = lang arguments_dict['menu'] = render_main_menu('view') arguments_dict['auth'] = TRANSLATIONS.get_translations(lang, 'Auth') arguments_dict['username'] = current_user(request)['username'] or None arguments_dict['is_teacher'] = is_teacher(request) arguments_dict.update(**TRANSLATIONS.get_translations(lang, 'ui')) return render_template("view-program-page.html", **arguments_dict)
def render_code_editor_with_tabs(level_defaults, max_level, level_number, version, loaded_program, adventures, restrictions, adventure_name): user = current_user() if not level_defaults: return utils.page_404 (ui_message='no_such_level') arguments_dict = {} # Meta stuff arguments_dict['level_nr'] = str(level_number) arguments_dict['level'] = level_number arguments_dict['prev_level'] = int(level_number) - 1 if int(level_number) > 1 else None arguments_dict['next_level'] = int(level_number) + 1 if int(level_number) < max_level else None arguments_dict['example_programs'] = restrictions['example_programs'] arguments_dict['hide_prev_level'] = restrictions['hide_prev_level'] arguments_dict['hide_next_level'] = restrictions['hide_next_level'] arguments_dict['menu'] = True arguments_dict['latest'] = version arguments_dict['selected_page'] = 'code' arguments_dict['page_title'] = f'Level {level_number} – Hedy' arguments_dict['username'] = user['username'] arguments_dict['is_teacher'] = is_teacher(user) arguments_dict['loaded_program'] = loaded_program arguments_dict['adventures'] = adventures arguments_dict['adventure_name'] = adventure_name # Merge level defaults into adventures so it is rendered as the first tab arguments_dict.update(**attr.asdict(level_defaults)) return render_template("code-page.html", **arguments_dict)
def adventure_page(adventure_name, level): user = current_user(request) level = int(level) adventures = load_adventure_for_language(requested_lang()) # If requested adventure does not exist, return 404 if not adventure_name in adventures['adventures']: return 'No such Hedy adventure!', 404 adventure = adventures['adventures'][adventure_name] # If no level is specified (this will happen if the last element of the path (minus the query parameter) is the same as the adventure_name) if re.sub(r'\?.+', '', request.url.split('/')[len(request.url.split('/')) - 1]) == adventure_name: # If user is logged in, check if they have a program for this adventure # If there are many, note the highest level for which there is a saved program desired_level = 0 if user['username']: existing_programs = DATABASE.programs_for_user(user['username']) for program in existing_programs: if 'adventure_name' in program and program[ 'adventure_name'] == adventure_name and program[ 'level'] > desired_level: desired_level = program['level'] # If the user has a saved program for this adventure, redirect them to the level with the highest adventure if desired_level != 0: return redirect(request.url.replace( '/' + adventure_name, '/' + adventure_name + '/' + str(desired_level)), code=302) # If user is not logged in, or has no saved programs for this adventure, default to the lowest level available for the adventure if desired_level == 0: for key in adventure['levels'].keys(): if isinstance(key, int) and (desired_level == 0 or desired_level > key): desired_level = key level = desired_level # If requested level is not in adventure, return 404 if not level in adventure['levels']: abort(404) adventure_assignments = load_adventure_assignments_per_level( requested_lang(), level) g.prefix = '/hedy' return hedyweb.render_assignment_editor( request=request, course=HEDY_COURSE[requested_lang()], level_number=level, assignment_number=1, menu=render_main_menu('hedy'), translations=TRANSLATIONS, version=version(), adventure_assignments=adventure_assignments, # The relevant loaded program will be available to client-side js and it will be loaded by js. loaded_program='', adventure_name=adventure_name)
def adventures_list(): return render_template('adventures.html', lang=lang, adventures=load_adventure_for_language( requested_lang()), menu=render_main_menu('adventures'), username=current_user(request)['username'], auth=TRANSLATIONS.data[lang]['Auth'])
def render_assignment_editor(request, course, level_number, assignment_number, menu, translations, version, loaded_program, loaded_program_name, adventure_assignments, adventure_name): sublevel = None if type_check(level_number, 'str') and re.match('\d+-\d+', level_number): sublevel = int(level_number[level_number.index('-') + 1]) level_number = int(level_number[0:level_number.index('-')]) assignment = course.get_assignment(level_number, assignment_number, sublevel) if not assignment: abort(404) arguments_dict = {} # Meta stuff arguments_dict['course'] = course arguments_dict['level_nr'] = str(level_number) arguments_dict['sublevel'] = str(sublevel) if (sublevel) else None arguments_dict[ 'assignment_nr'] = assignment.step # Give this a chance to be 'None' arguments_dict['lang'] = course.language arguments_dict['level'] = assignment.level arguments_dict['prev_level'] = int(level_number) - 1 if int( level_number) > 1 else None arguments_dict['next_level'] = int(level_number) + 1 if int( level_number) < course.max_level() else None arguments_dict['next_assignment'] = int(assignment_number) + 1 if int( assignment_number) < course.max_step(level_number) else None arguments_dict['menu'] = menu arguments_dict['latest'] = version arguments_dict['selected_page'] = 'code' arguments_dict['page_title'] = f'Level {level_number} – Hedy' arguments_dict['docs'] = [attr.asdict(d) for d in assignment.docs] arguments_dict['auth'] = translations.data[course.language]['Auth'] arguments_dict['username'] = current_user(request)['username'] arguments_dict['loaded_program'] = loaded_program arguments_dict['loaded_program_name'] = loaded_program_name arguments_dict['adventure_assignments'] = adventure_assignments arguments_dict['adventure_name'] = adventure_name # Translations arguments_dict.update( **translations.get_translations(course.language, 'ui')) # Actual assignment arguments_dict.update(**attr.asdict(assignment)) # Add markdowns to docs for doc in arguments_dict['docs']: doc['markdown'] = (course.docs.get(int(level_number), doc['slug']) or { 'markdown': '' }).markdown return render_template("code-page.html", **arguments_dict)
def get_class(user, class_id): if not is_teacher(request): return 'Only teachers can retrieve classes', 403 Class = DATABASE.get_class(class_id) if not Class or Class['teacher'] != user['username']: return 'No such class', 404 students = [] for student_username in Class.get('students', []): student = DATABASE.user_by_username(student_username) programs = DATABASE.programs_for_user(student_username) highest_level = max( program['level'] for program in programs) if len(programs) else 0 sorted_public_programs = list( sorted( [program for program in programs if program.get('public')], key=lambda p: p['date'])) if sorted_public_programs: latest_shared = sorted_public_programs[-1] latest_shared['link'] = os.getenv( 'BASE_URL') + f"/hedy/{latest_shared['id']}/view" else: latest_shared = None students.append({ 'username': student_username, 'last_login': utils.mstoisostring(student['last_login']), 'programs': len(programs), 'highest_level': highest_level, 'latest_shared': latest_shared }) if utils.is_testing_request(request): return jsonify({ 'students': students, 'link': Class['link'], 'name': Class['name'], 'id': Class['id'] }) return render_template( 'class-overview.html', lang=requested_lang(), auth=TRANSLATIONS.get_translations(requested_lang(), 'Auth'), menu=render_main_menu('my-profile'), username=current_user(request)['username'], is_teacher=is_teacher(request), current_page='my-profile', class_info={ 'students': students, 'link': os.getenv('BASE_URL') + '/hedy/l/' + Class['link'], 'name': Class['name'], 'id': Class['id'] })
def adventures_list(): return render_template( 'adventures.html', lang=lang, adventures=load_adventure_for_language(requested_lang()), menu=render_main_menu('adventures'), username=current_user(request)['username'], is_teacher=is_teacher(request), auth=TRANSLATIONS.get_translations(requested_lang(), 'Auth'))
def index(level, step): # Sublevel requested if re.match ('\d+-\d+', level): pass # If level has a dash, we keep it as a string # Normal level requested elif re.match ('\d', level): try: g.level = level = int(level) except: return 'No such Hedy level!', 404 else: return 'No such Hedy level!', 404 g.lang = requested_lang() g.prefix = '/hedy' initialize_gfi_session(g.lang) loaded_program = '' loaded_program_name = '' adventure_name = '' # If step is a string that has more than two characters, it must be an id of a program if step and type_check (step, 'str') and len (step) > 2: result = db_get ('programs', {'id': step}) if not result: return 'No such program', 404 # If the program is not public, allow only the owner of the program, the admin user and the teacher users to access the program user = current_user (request) public_program = 'public' in result and result ['public'] if not public_program and user ['username'] != result ['username'] and not is_admin (request) and not is_teacher (request): return 'No such program!', 404 loaded_program = result ['code'] loaded_program_name = result ['name'] if 'adventure_name' in result: adventure_name = result ['adventure_name'] # We default to step 1 to provide a meaningful default assignment step = 1 adventure_assignments = load_adventure_assignments_per_level(g.lang, level) return hedyweb.render_assignment_editor( request=request, course=HEDY_COURSE[g.lang], level_number=level, assignment_number=step, menu=render_main_menu('hedy'), translations=TRANSLATIONS, version=version(), adventure_assignments=adventure_assignments, loaded_program=loaded_program, loaded_program_name=loaded_program_name, adventure_name=adventure_name)
def report_client_exception(): post_body = request.json querylog.log_value(session=session_id(), date=str(datetime.datetime.now()), client_error=post_body, version=version(), username=current_user(request)['username'] or None, is_test=1 if os.getenv('IS_TEST_ENV') else None) return 'logged', 500
def report_client_exception(): post_body = request.json querylog.log_value(session=session_id(), date=str(datetime.datetime.now()), client_error=post_body, version=version(), username=current_user(request)['username'] or None, is_test=1 if os.getenv('IS_TEST_ENV') else None) # Return a 500 so the HTTP status codes will stand out in our monitoring/logging return 'logged', 500
def resolve_class_link(link_id): Class = DATABASE.resolve_class_link(link_id) if not Class: return utils.page_404( TRANSLATIONS, render_main_menu('my-profile'), current_user(request)['username'], requested_lang(), TRANSLATIONS.get_translations(requested_lang(), 'ui').get('invalid_class_link')) return redirect(request.url.replace( '/hedy/l/' + link_id, '/class/' + Class['id'] + '/prejoin/' + link_id), code=302)
def index(level, step): # Sublevel requested if re.match('\d+-\d+', level): pass # If level has a dash, we keep it as a string # Normal level requested elif re.match('\d', level): try: g.level = level = int(level) except: return 'No such Hedy level!', 404 else: return 'No such Hedy level!', 404 g.lang = requested_lang() g.prefix = '/hedy' loaded_program = '' adventure_name = '' # If step is a string that has more than two characters, it must be an id of a program if step and isinstance(step, str) and len(step) > 2: result = DATABASE.program_by_id(step) if not result: return 'No such program', 404 # If the program is not public, allow only the owner of the program, the admin user and the teacher users to access the program user = current_user(request) public_program = 'public' in result and result['public'] if not public_program and user['username'] != result[ 'username'] and not is_admin(request) and not is_teacher( request): return 'No such program!', 404 loaded_program = { 'code': result['code'], 'name': result['name'], 'adventure_name': result.get('adventure_name') } if 'adventure_name' in result: adventure_name = result['adventure_name'] adventures = load_adventures_per_level(g.lang, level) return hedyweb.render_code_editor_with_tabs(request=request, course=HEDY_COURSE[g.lang], level_number=level, menu=render_main_menu('hedy'), translations=TRANSLATIONS, version=version(), adventures=adventures, loaded_program=loaded_program, adventure_name=adventure_name)
def join_class(user, class_id, link): Class = DATABASE.get_class(class_id) if not Class or Class['link'] != link: return utils.page_404( TRANSLATIONS, render_main_menu('my-profile'), current_user(request)['username'], requested_lang(), TRANSLATIONS.get_translations(requested_lang(), 'ui').get('invalid_class_link')) DATABASE.add_student_to_class(Class['id'], user['username']) return redirect(request.url.replace( '/class/' + class_id + '/join/' + link, '/my-profile'), code=302)
def report_error(): post_body = request.json parse_logger.log({ 'session': session_id(), 'date': str(datetime.datetime.now()), 'level': post_body.get('level'), 'code': post_body.get('code'), 'client_error': post_body.get('client_error'), 'version': version(), 'username': current_user(request)['username'] or None, 'is_test': 1 if os.getenv('IS_TEST_ENV') else None }) return 'logged'
def submit_answer(level_source, question_nr): if not config['quiz-enabled'] and g.lang != 'nl': return 'Hedy quiz disabled!', 404 else: # Get the chosen option from the request form with radio buttons option = request.form["radio_option"] # Reading yaml file if os.path.isfile( f'coursedata/quiz/quiz_questions_lvl{level_source}.yaml'): quiz_data = load_yaml( f'coursedata/quiz/quiz_questions_lvl{level_source}.yaml') else: return 'No quiz yaml file found for this level', 404 # Convert question_nr to an integer q_nr = int(question_nr) # Convert the corresponding chosen option to the index of an option question = quiz_data['questions'][q_nr - 1].get(q_nr) index_option = ord(option.split("-")[1]) - 65 # If the correct answer is chosen, update the total score and the number of correct answered questions if question['correct_answer'] in option: session['total_score'] = session.get( 'total_score') + question['question_score'] session['correct_answer'] = session.get('correct_answer') + 1 # Loop through the questions if q_nr <= len(quiz_data['questions']): return render_template('feedback.html', quiz=quiz_data, question=question, questions=quiz_data['questions'], level_source=level_source, question_nr=q_nr, correct=session.get('correct_answer'), option=option, index_option=index_option, menu=render_main_menu('adventures'), lang=lang, username=current_user(request)['username'], is_teacher=is_teacher(request), auth=TRANSLATIONS.get_translations( requested_lang(), 'Auth')) else: # show a different page for after the last question return 'No end quiz page!', 404
def get_quiz_start(level): if not config['quiz-enabled'] and g.lang != 'nl': return 'Hedy quiz disabled!', 404 else: g.lang = lang = requested_lang() g.prefix = '/hedy' #Sets the values of total_score and correct on the beginning of the quiz at 0 session['total_score'] = 0 session['correct_answer'] = 0 return render_template( 'startquiz.html', level=level, next_assignment=1, menu=render_main_menu('adventures'), lang=lang, username=current_user(request)['username'], auth=TRANSLATIONS.data[requested_lang()]['Auth'])
def parse(): body = request.json if not body: return "body must be an object", 400 if 'code' not in body: return "body.code must be a string", 400 if 'level' not in body: return "body.level must be a string", 400 if 'sublevel' in body and not type_check(body['sublevel'], 'int'): return "If present, body.sublevel must be an integer", 400 if 'adventure_name' in body and not type_check(body['adventure_name'], 'str'): return "if present, body.adventure_name must be a string", 400 code = body['code'] level = int(body['level']) sublevel = body.get('sublevel') or 0 # Language should come principally from the request body, # but we'll fall back to browser default if it's missing for whatever # reason. lang = body.get('lang', requested_lang()) response = {} username = current_user(request)['username'] or None querylog.log_value(level=level, lang=lang, session_id=session_id(), username=username) # Check if user sent code if not code: response["Error"] = "no code found, please send code." # is so, parse else: try: hedy_errors = TRANSLATIONS.get_translations( lang, 'HedyErrorMessages') with querylog.log_time('transpile'): result = hedy.transpile(code, level, sublevel) response["Code"] = "# coding=utf8\nimport random\n" + result except hedy.HedyException as E: traceback.print_exc() # some 'errors' can be fixed, for these we throw an exception, but also # return fixed code, so it can be ran if E.args[0] == "Invalid Space": error_template = hedy_errors[E.error_code] response[ "Code"] = "# coding=utf8\n" + E.arguments['fixed_code'] response["Warning"] = error_template.format(**E.arguments) elif E.args[0] == "Parse": error_template = hedy_errors[E.error_code] # Localize the names of characters. If we can't do that, just show the original # character. if 'character_found' in E.arguments.keys(): E.arguments['character_found'] = hedy_errors.get( E.arguments['character_found'], E.arguments['character_found']) elif 'keyword_found' in E.arguments.keys(): #if we find an invalid keyword, place it in the same location in the error message but without translating E.arguments['character_found'] = E.arguments[ 'keyword_found'] response["Error"] = error_template.format(**E.arguments) elif E.args[0] == "Unquoted Text": error_template = hedy_errors[E.error_code] response["Error"] = error_template.format(**E.arguments) else: error_template = hedy_errors[E.error_code] response["Error"] = error_template.format(**E.arguments) except Exception as E: traceback.print_exc() print(f"error transpiling {code}") response["Error"] = str(E) querylog.log_value(server_error=response.get('Error')) parse_logger.log({ 'session': session_id(), 'date': str(datetime.datetime.now()), 'level': level, 'lang': lang, 'code': code, 'server_error': response.get('Error'), 'version': version(), 'username': username, 'is_test': 1 if os.getenv('IS_TEST_ENV') else None, 'adventure_name': body.get('adventure_name', None) }) return jsonify(response)
def render_code_editor_with_tabs(request, course, level_number, menu, translations, version, loaded_program, adventures, adventure_name): if os.path.isfile( f'coursedata/quiz/quiz_questions_lvl{level_number}.yaml'): quiz_data = utils.load_yaml( f'coursedata/quiz/quiz_questions_lvl{level_number}.yaml') quiz_data_level = quiz_data['level'] else: quiz_data_level = 0 sublevel = None if isinstance(level_number, str) and re.match('\d+-\d+', level_number): sublevel = int(level_number[level_number.index('-') + 1]) level_number = int(level_number[0:level_number.index('-')]) defaults = course.get_default_text(level_number, sublevel) if not defaults: abort(404) if course.custom: adventures = [ x for x in adventures if x['short_name'] in course.adventures ] arguments_dict = {} # Meta stuff arguments_dict['course'] = course arguments_dict['level_nr'] = str(level_number) arguments_dict['sublevel'] = str(sublevel) if (sublevel) else None arguments_dict['lang'] = course.language arguments_dict['level'] = defaults.level arguments_dict['prev_level'] = int(level_number) - 1 if int( level_number) > 1 else None arguments_dict['next_level'] = int(level_number) + 1 if int( level_number) < course.max_level() else None arguments_dict['menu'] = menu arguments_dict['latest'] = version arguments_dict['selected_page'] = 'code' arguments_dict['page_title'] = f'Level {level_number} – Hedy' arguments_dict['auth'] = translations.get_translations( course.language, 'Auth') arguments_dict['username'] = current_user(request)['username'] arguments_dict['is_teacher'] = is_teacher(request) arguments_dict['loaded_program'] = loaded_program arguments_dict['adventures'] = adventures arguments_dict['adventure_name'] = adventure_name arguments_dict['quiz_data_level'] = quiz_data_level arguments_dict[ 'quiz_enabled'] = config['quiz-enabled'] and course.language == 'nl' # Translations arguments_dict.update( **translations.get_translations(course.language, 'ui')) # Actual assignment arguments_dict.update(**attr.asdict(defaults)) return render_template("code-page.html", **arguments_dict)
def parse(): body = request.json if not body: return "body must be an object", 400 if 'code' not in body: return "body.code must be a string", 400 if 'level' not in body: return "body.level must be a string", 400 if 'sublevel' in body and not type_check (body ['sublevel'], 'int'): return "If present, body.sublevel must be an integer", 400 if 'adventure_name' in body and not type_check (body ['adventure_name'], 'str'): return "if present, body.adventure_name must be a string", 400 code = body ['code'] level = int(body ['level']) sublevel = body.get ('sublevel') or 0 # Language should come principally from the request body, # but we'll fall back to browser default if it's missing for whatever # reason. lang = body.get('lang', requested_lang()) querylog.log_value(level=level, lang=lang) response = {} username = current_user(request) ['username'] or None querylog.log_value(level=level, lang=lang, session_id=session_id(), username=username) # Check if user sent code if not code: response["Error"] = "no code found, please send code." # is so, parse else: try: hedy_errors = TRANSLATIONS.get_translations(lang, 'HedyErrorMessages') gradual_feedback = TRANSLATIONS.get_translations(lang, 'GradualFeedback') with querylog.log_time('transpile'): result = hedy.transpile(code, level,sublevel) response["Code"] = "# coding=utf8\nimport random\n" + result if gfi_support(lang): if not (('error_level' in session) or ('similar_code' in session)): return 'session cookie must have error_level & similar_code set', 400 response['prev_feedback_level'] = session['error_level'] response['prev_similar_code'] = session['similar_code'] session ['error_level'] = 0 # Code is correct: reset error_level back to 0 except hedy.HedyException as E: traceback.print_exc() # some 'errors' can be fixed, for these we throw an exception, but also # return fixed code, so it can be ran if E.args[0] == "Invalid Space": error_template = hedy_errors[E.error_code] response["Code"] = "# coding=utf8\n" + E.arguments['fixed_code'] response["Warning"] = error_template.format(**E.arguments) elif E.args[0] == "Parse": error_template = hedy_errors[E.error_code] # Localize the names of characters if 'character_found' in E.arguments: E.arguments['character_found'] = hedy_errors[E.arguments['character_found']] response["Error"] = error_template.format(**E.arguments) elif E.args[0] == "Unquoted Text": error_template = hedy_errors[E.error_code] response["Error"] = error_template.format(**E.arguments) else: error_template = hedy_errors[E.error_code] response["Error"] = error_template.format(**E.arguments) if gfi_support(lang): response.update(gradual_feedback_model(code, level, gradual_feedback, lang, E, hedy_exception=True)) except Exception as E: traceback.print_exc() print(f"error transpiling {code}") response["Error"] = str(E) if gfi_support(lang): response.update(gradual_feedback_model(code, level, gradual_feedback, lang, E, hedy_exception=False)) if gfi_support(lang): session ['code'] = code querylog.log_value(server_error=response.get('Error')) parse_logger.log ({ 'session': session_id(), 'date': str(datetime.datetime.now()), 'level': level, 'lang': lang, 'code': code, 'server_error': response.get('Error'), 'version': version(), 'username': username, 'feedback_level': session['error_level'] if gfi_support (lang) else None, 'GFM': True if gfi_support (lang) else False, 'is_test': 1 if os.getenv('IS_TEST_ENV') else None, 'adventure_name': body.get('adventure_name', None) }) return jsonify(response)
def programs_page(request): username = current_user(request)['username'] if not username: # redirect users to /login if they are not logged in url = request.url.replace('/programs', '/login') return redirect(url, code=302) from_user = request.args.get('user') or None if from_user and not is_admin(request): if not is_teacher(request): return "unauthorized", 403 students = DATABASE.get_teacher_students(username) if from_user not in students: return "unauthorized", 403 texts = TRANSLATIONS.get_translations(requested_lang(), 'Programs') ui = TRANSLATIONS.get_translations(requested_lang(), 'ui') adventures = load_adventure_for_language(requested_lang())['adventures'] result = DATABASE.programs_for_user(from_user or username) programs = [] now = timems() for item in result: program_age = now - item['date'] if program_age < 1000 * 60 * 60: measure = texts['minutes'] date = round(program_age / (1000 * 60)) elif program_age < 1000 * 60 * 60 * 24: measure = texts['hours'] date = round(program_age / (1000 * 60 * 60)) else: measure = texts['days'] date = round(program_age / (1000 * 60 * 60 * 24)) programs.append({ 'id': item['id'], 'code': item['code'], 'date': texts['ago-1'] + ' ' + str(date) + ' ' + measure + ' ' + texts['ago-2'], 'level': item['level'], 'name': item['name'], 'adventure_name': item.get('adventure_name'), 'public': item.get('public') }) return render_template('programs.html', lang=requested_lang(), menu=render_main_menu('programs'), texts=texts, ui=ui, auth=TRANSLATIONS.get_translations( requested_lang(), 'Auth'), programs=programs, username=username, is_teacher=is_teacher(request), current_page='programs', from_user=from_user, adventures=adventures)
def submit_answer(level_source, question_nr, attempt): if not config.get('quiz-enabled') and g.lang != 'nl': return 'Hedy quiz disabled!', 404 else: # Get the chosen option from the request form with radio buttons option = request.form["radio_option"] # Reading yaml file quiz_data = quiz_data_file_for(level_source) if not quiz_data.exists(): return 'No quiz yaml file found for this level', 404 # Convert question_nr to an integer q_nr = int(question_nr) session['quiz-attempt'] = int(attempt) questionStatus = 'false' if int(attempt) == 1: questionStatus = 'start' # Convert the corresponding chosen option to the index of an option question = quiz_data['questions'][q_nr - 1].get(q_nr) index_option = ord(option.split("-")[1]) - 65 session['chosen_option'] = option.split("-")[1] # If the correct answer is chosen, update the total score and the number of correct answered questions if question['correct_answer'] in option: if session.get('total_score'): session['total_score'] = session.get('total_score') + ( config.get('quiz-max-attempts') - session.get( 'quiz-attempt')) * 0.5 * question['question_score'] else: session['total_score'] = (config.get('quiz-max-attempts') - session.get('quiz-attempt') ) * 0.5 * question['question_score'] if session.get('correct_answer'): session['correct_answer'] = session.get('correct_answer') + 1 else: session['correct_answer'] = 1 # Loop through the questions and check that the loop doesn't reach out of bounds q_nr = int(question_nr) if q_nr <= len(quiz_data['questions']): if question['correct_answer'] in option: return render_template( 'feedback.html', quiz=quiz_data, question=question, questions=quiz_data['questions'], level_source=level_source, question_nr=q_nr, correct=session.get('correct_answer'), option=option, index_option=index_option, menu=render_main_menu('adventures'), lang=lang, username=current_user(request)['username'], auth=TRANSLATIONS.data[requested_lang()]['Auth']) elif session.get('quiz-attempt') <= config.get( 'quiz-max-attempts'): question = quiz_data['questions'][q_nr - 1].get(q_nr) # Convert the indices to the corresponding characters char_array = [] for i in range(len(question['mp_choice_options'])): char_array.append(chr(ord('@') + (i + 1))) return render_template( 'quiz_question.html', quiz=quiz_data, level_source=level_source, questions=quiz_data['questions'], question=quiz_data['questions'][q_nr - 1].get(q_nr), question_nr=q_nr, correct=session.get('correct_answer'), attempt=session.get('quiz-attempt'), questionStatus=questionStatus, chosen_option=session.get('chosen_option'), char_array=char_array, menu=render_main_menu('adventures'), lang=lang, username=current_user(request)['username'], auth=TRANSLATIONS.data[requested_lang()]['Auth']) elif session.get('quiz-attempt') > config.get('quiz-max-attempts'): return render_template( 'feedback.html', quiz=quiz_data, question=question, questions=quiz_data['questions'], level_source=level_source, question_nr=q_nr, correct=session.get('correct_answer'), questionStatus=questionStatus, option=option, index_option=index_option, menu=render_main_menu('adventures'), lang=lang, username=current_user(request)['username'], auth=TRANSLATIONS.data[requested_lang()]['Auth']) else: # show a different page for after the last question return 'No end quiz page!', 404