Beispiel #1
0
    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']
            })
Beispiel #2
0
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)
Beispiel #3
0
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'))
Beispiel #4
0
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
Beispiel #5
0
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
Beispiel #6
0
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'])
Beispiel #7
0
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)
Beispiel #8
0
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)
Beispiel #9
0
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)
Beispiel #10
0
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)
Beispiel #11
0
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)
Beispiel #12
0
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)
Beispiel #13
0
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'])
Beispiel #14
0
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)
Beispiel #15
0
    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']
            })
Beispiel #16
0
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'))
Beispiel #17
0
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)
Beispiel #18
0
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
Beispiel #19
0
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
Beispiel #20
0
 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)
Beispiel #21
0
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)
Beispiel #22
0
    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)
Beispiel #23
0
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'
Beispiel #24
0
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
Beispiel #25
0
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'])
Beispiel #26
0
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)
Beispiel #27
0
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)
Beispiel #28
0
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)
Beispiel #29
0
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)
Beispiel #30
0
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