Exemplo n.º 1
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']
            })
Exemplo n.º 2
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'))
Exemplo n.º 3
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)
Exemplo n.º 4
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)
Exemplo n.º 5
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']
            })
Exemplo n.º 6
0
    def get_class_info(user, class_id):
        if not is_teacher(user):
            return utils.page_403(ui_message='retrieve_class')
        Class = DATABASE.get_class(class_id)
        if not Class or Class['teacher'] != user['username']:
            return utils.page_404(ui_message='no_such_class')

        if hedy_content.Adventures(g.lang).has_adventures():
            adventures = hedy_content.Adventures(
                g.lang).get_adventure_keyname_name_levels()
        else:
            adventures = hedy_content.Adventures(
                "en").get_adventure_keyname_name_levels()
        levels = hedy_content.LevelDefaults(g.lang).levels
        preferences = DATABASE.get_customizations_class(class_id)

        return render_template(
            'customize-class.html',
            page_title=hedyweb.get_page_title('customize class'),
            class_info={
                'name': Class['name'],
                'id': Class['id']
            },
            levels=levels,
            adventures=adventures,
            preferences=preferences,
            current_page='for-teachers')
Exemplo n.º 7
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)
Exemplo n.º 8
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)
Exemplo n.º 9
0
    def create_class(user):
        if not is_teacher(user):
            return 'Only teachers can create classes', 403

        body = request.json
        # Validations
        if not isinstance(body, dict):
            return 'body must be an object', 400
        if not isinstance(body.get('name'), str):
            return 'name must be a string', 400

        # We use this extra call to verify if the class name doesn't already exist, if so it's a duplicate
        Classes = DATABASE.get_teacher_classes(user['username'], True)
        for Class in Classes:
            if Class['name'] == body['name']:
                return "duplicate", 200

        Class = {
            'id': uuid.uuid4().hex,
            'date': utils.timems(),
            'teacher': user['username'],
            'link': utils.random_id_generator(7),
            'name': body['name']
        }

        DATABASE.store_class(Class)

        return {'id': Class['id']}, 200
Exemplo n.º 10
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'))
Exemplo n.º 11
0
Arquivo: app.py Projeto: LauraTSD/hedy
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']
        # 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,
        adventure_name=adventure_name)
Exemplo n.º 12
0
    def get_class(user, class_id):
        app.logger.info('This is info output')
        if not is_teacher(user):
            return utils.page_403(ui_message='retrieve_class')
        Class = DATABASE.get_class(class_id)
        if not Class or Class['teacher'] != user['username']:
            return utils.page_404(ui_message='no_such_class')
        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'] = f"/hedy/{latest_shared['id']}/view"
            else:
                latest_shared = None
            students.append({
                'username':
                student_username,
                'last_login':
                utils.datetotimeordate(
                    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',
            current_page='for-teachers',
            page_title=hedyweb.get_page_title('class overview'),
            class_info={
                'students': students,
                'link': '/hedy/l/' + Class['link'],
                'name': Class['name'],
                'id': Class['id']
            })
Exemplo n.º 13
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
Exemplo n.º 14
0
    def update_class(user, class_id):
        if not is_teacher(request):
            return 'Only teachers can update classes', 403

        body = request.json
        # Validations
        if not isinstance(body, dict):
            return 'body must be an object', 400
        if not isinstance(body.get('name'), str):
            return 'name must be a string', 400

        Class = DATABASE.get_class(class_id)
        if not Class or Class['teacher'] != user['username']:
            return 'No such class', 404

        Class = DATABASE.update_class(class_id, body['name'])

        return {}, 200
Exemplo n.º 15
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'],
                               is_teacher=is_teacher(request),
                               auth=TRANSLATIONS.get_translations(
                                   requested_lang(), 'Auth'))
Exemplo n.º 16
0
    def create_class(user):
        if not is_teacher(request):
            return 'Only teachers can create classes', 403

        body = request.json
        # Validations
        if not isinstance(body, dict):
            return 'body must be an object', 400
        if not isinstance(body.get('name'), str):
            return 'name must be a string', 400

        Class = {
            'id': uuid.uuid4().hex,
            'date': utils.timems(),
            'teacher': user['username'],
            'link': utils.random_id_generator(7),
            'name': body['name']
        }

        DATABASE.store_class(Class)

        return {}, 200
Exemplo n.º 17
0
    def update_class(user, class_id):
        if not is_teacher(user):
            return 'Only teachers can update classes', 403

        body = request.json
        # Validations
        if not isinstance(body, dict):
            return 'body must be an object', 400
        if not isinstance(body.get('name'), str):
            return 'name must be a string', 400

        Class = DATABASE.get_class(class_id)
        if not Class or Class['teacher'] != user['username']:
            return 'No such class', 404

        # We use this extra call to verify if the class name doesn't already exist, if so it's a duplicate
        Classes = DATABASE.get_teacher_classes(user['username'], True)
        for Class in Classes:
            if Class['name'] == body['name']:
                return "duplicate", 200

        Class = DATABASE.update_class(class_id, body['name'])

        return {}, 200
Exemplo n.º 18
0
    def update_level_preferences(user, class_id):
        if not is_teacher(user):
            return 'Only teachers can update class preferences', 403

        body = request.json
        print(body)
        # Validations
        if not isinstance(body, dict):
            return 'body must be an object', 400
        if not isinstance(body.get('example_programs'), bool):
            return 'amount of example programs must be an integer', 400
        if not isinstance(body.get('hide_level'), bool):
            return 'level switch must be a boolean', 400
        if not isinstance(body.get('hide_prev_level'), bool):
            return 'level switch must be a boolean', 400
        if not isinstance(body.get('hide_next_level'), bool):
            return 'level switch must be a boolean', 400
        if not isinstance(int(body.get('level')), int):
            return 'level must ben an integer', 400

        Class = DATABASE.get_class(class_id)
        if not Class or Class['teacher'] != user['username']:
            return 'No such class', 404

        customizations = {}
        customizations['id'] = class_id
        customizations['level'] = int(body.get('level'))
        customizations['adventures'] = body.get('adventures')
        customizations['example_programs'] = body.get('example_programs')
        customizations['hide'] = body.get('hide_level')
        customizations['hide_prev_level'] = body.get('hide_prev_level')
        customizations['hide_next_level'] = body.get('hide_next_level')

        Class = DATABASE.update_customizations_class(customizations)

        return {}, 200
Exemplo n.º 19
0
 def get_classes(user):
     if not is_teacher(request):
         return 'Only teachers can retrieve classes', 403
     return jsonify(DATABASE.get_teacher_classes(user['username'], True))
Exemplo n.º 20
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)
Exemplo n.º 21
0
 def get_classes(user):
     if not is_teacher(user):
         return utils.page_403(ui_message='retrieve_class')
     return jsonify(DATABASE.get_teacher_classes(user['username'], True))
Exemplo n.º 22
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)