Exemplo n.º 1
0
def get_app(config):
    """
    :param config: the configuration dict
    :return: A new app
    """
    config = _put_configuration_defaults(config)

    mongo_client = MongoClient(
        host=config.get('mongo_opt', {}).get('host', 'localhost'))
    database = mongo_client[config.get('mongo_opt',
                                       {}).get('database', 'INGInious')]
    gridfs = GridFS(database)

    # Init database if needed
    db_version = database.db_version.find_one({})
    if db_version is None:
        database.submissions.ensure_index([("username", pymongo.ASCENDING)])
        database.submissions.ensure_index([("courseid", pymongo.ASCENDING)])
        database.submissions.ensure_index([("courseid", pymongo.ASCENDING),
                                           ("taskid", pymongo.ASCENDING)])
        database.submissions.ensure_index([("submitted_on", pymongo.DESCENDING)
                                           ])  # sort speed
        database.user_tasks.ensure_index([("username", pymongo.ASCENDING),
                                          ("courseid", pymongo.ASCENDING),
                                          ("taskid", pymongo.ASCENDING)],
                                         unique=True)
        database.user_tasks.ensure_index([("username", pymongo.ASCENDING),
                                          ("courseid", pymongo.ASCENDING)])
        database.user_tasks.ensure_index([("courseid", pymongo.ASCENDING),
                                          ("taskid", pymongo.ASCENDING)])
        database.user_tasks.ensure_index([("courseid", pymongo.ASCENDING)])
        database.user_tasks.ensure_index([("username", pymongo.ASCENDING)])

    appli = CookieLessCompatibleApplication(MongoStore(database, 'sessions'))

    # Init gettext
    available_languages = {"en": "English", "fr": "Français"}

    for lang in available_languages.keys():
        appli.add_translation(
            lang,
            gettext.translation('messages',
                                get_root_path() + '/frontend/i18n', [lang]))

    builtins.__dict__['_'] = appli.gettext

    if config.get("maintenance", False):
        template_helper = TemplateHelper(PluginManager(), None,
                                         'frontend/templates',
                                         'frontend/templates/layout',
                                         'frontend/templates/layout_lti',
                                         config.get('use_minified_js', True))
        template_helper.add_to_template_globals("get_homepath",
                                                appli.get_homepath)
        template_helper.add_to_template_globals("_", _)
        appli.template_helper = template_helper
        appli.init_mapping(urls_maintenance)
        return appli.wsgifunc(), appli.stop

    default_allowed_file_extensions = config['allowed_file_extensions']
    default_max_file_size = config['max_file_size']

    zmq_context, __ = start_asyncio_and_zmq(config.get('debug_asyncio', False))

    # Init the different parts of the app
    plugin_manager = PluginManager()

    # Create the FS provider
    if "fs" in config:
        fs_provider = filesystem_from_config_dict(config["fs"])
    else:
        task_directory = config["tasks_directory"]
        fs_provider = LocalFSProvider(task_directory)

    default_problem_types = {
        problem_type.get_type(): problem_type
        for problem_type in [
            DisplayableCodeProblem, DisplayableCodeSingleLineProblem,
            DisplayableFileProblem, DisplayableMultipleChoiceProblem,
            DisplayableMatchProblem
        ]
    }

    course_factory, task_factory = create_factories(fs_provider,
                                                    default_problem_types,
                                                    plugin_manager,
                                                    WebAppCourse, WebAppTask)

    user_manager = UserManager(appli.get_session(), database,
                               config.get('superadmins', []))

    update_pending_jobs(database)

    client = create_arch(config, fs_provider, zmq_context, course_factory)

    lti_outcome_manager = LTIOutcomeManager(database, user_manager,
                                            course_factory)

    submission_manager = WebAppSubmissionManager(client, user_manager,
                                                 database, gridfs,
                                                 plugin_manager,
                                                 lti_outcome_manager)

    template_helper = TemplateHelper(plugin_manager, user_manager,
                                     'frontend/templates',
                                     'frontend/templates/layout',
                                     'frontend/templates/layout_lti',
                                     config.get('use_minified_js', True))

    # Init web mail
    smtp_conf = config.get('smtp', None)
    if smtp_conf is not None:
        web.config.smtp_server = smtp_conf["host"]
        web.config.smtp_port = int(smtp_conf["port"])
        web.config.smtp_starttls = bool(smtp_conf.get("starttls", False))
        web.config.smtp_username = smtp_conf.get("username", "")
        web.config.smtp_password = smtp_conf.get("password", "")
        web.config.smtp_sendername = smtp_conf.get("sendername",
                                                   "*****@*****.**")

    # Add some helpers for the templates
    template_helper.add_to_template_globals("_", _)
    template_helper.add_to_template_globals("str", str)
    template_helper.add_to_template_globals("available_languages",
                                            available_languages)
    template_helper.add_to_template_globals("get_homepath", appli.get_homepath)
    template_helper.add_to_template_globals(
        "allow_registration", config.get("allow_registration", True))
    template_helper.add_to_template_globals("user_manager", user_manager)
    template_helper.add_to_template_globals("default_allowed_file_extensions",
                                            default_allowed_file_extensions)
    template_helper.add_to_template_globals("default_max_file_size",
                                            default_max_file_size)
    template_helper.add_other(
        "course_admin_menu",
        lambda course, current: course_admin_utils.get_menu(
            course, current, template_helper.get_renderer(False),
            plugin_manager, user_manager))
    template_helper.add_other(
        "preferences_menu", lambda current: preferences_utils.get_menu(
            appli, current, template_helper.get_renderer(False),
            plugin_manager, user_manager))

    # Not found page
    appli.notfound = lambda: web.notfound(template_helper.get_renderer().
                                          notfound('Page not found'))

    # Enable stacktrace display if logging is at level DEBUG
    if config.get('log_level', 'INFO') == 'DEBUG':
        appli.internalerror = debugerror

    # Insert the needed singletons into the application, to allow pages to call them
    appli.plugin_manager = plugin_manager
    appli.course_factory = course_factory
    appli.task_factory = task_factory
    appli.submission_manager = submission_manager
    appli.user_manager = user_manager
    appli.template_helper = template_helper
    appli.database = database
    appli.gridfs = gridfs
    appli.default_allowed_file_extensions = default_allowed_file_extensions
    appli.default_max_file_size = default_max_file_size
    appli.backup_dir = config.get("backup_directory", './backup')
    appli.webterm_link = config.get("webterm", None)
    appli.lti_outcome_manager = lti_outcome_manager
    appli.allow_registration = config.get("allow_registration", True)
    appli.allow_deletion = config.get("allow_deletion", True)
    appli.available_languages = available_languages
    appli.welcome_page = config.get("welcome_page", None)
    appli.static_directory = config.get("static_directory", "./static")
    appli.webdav_host = config.get("webdav_host", None)

    # Init the mapping of the app
    appli.init_mapping(urls)

    # Loads plugins
    plugin_manager.load(client, appli, course_factory, task_factory, database,
                        user_manager, submission_manager,
                        config.get("plugins", []))

    # Start the inginious.backend
    client.start()

    return appli.wsgifunc(), lambda: _close_app(appli, mongo_client, client)
Exemplo n.º 2
0
def get_app(config):
    """
    :param config: the configuration dict
    :return: A new app
    """
    # First, disable debug. It will be enabled in the configuration, later.

    config = _put_configuration_defaults(config)
    mongo_client = MongoClient(
        host=config.get('mongo_opt', {}).get('host', 'localhost'))
    database = mongo_client[config.get('mongo_opt',
                                       {}).get('database', 'INGInious')]
    gridfs = GridFS(database)

    # Init database if needed
    db_version = database.db_version.find_one({})
    if db_version is None:
        database.submissions.ensure_index([("username", pymongo.ASCENDING)])
        database.submissions.ensure_index([("courseid", pymongo.ASCENDING)])
        database.submissions.ensure_index([("courseid", pymongo.ASCENDING),
                                           ("taskid", pymongo.ASCENDING)])
        database.submissions.ensure_index([("submitted_on", pymongo.DESCENDING)
                                           ])  # sort speed
        database.user_tasks.ensure_index([("username", pymongo.ASCENDING),
                                          ("courseid", pymongo.ASCENDING),
                                          ("taskid", pymongo.ASCENDING)],
                                         unique=True)
        database.user_tasks.ensure_index([("username", pymongo.ASCENDING),
                                          ("courseid", pymongo.ASCENDING)])
        database.user_tasks.ensure_index([("courseid", pymongo.ASCENDING),
                                          ("taskid", pymongo.ASCENDING)])
        database.user_tasks.ensure_index([("courseid", pymongo.ASCENDING)])
        database.user_tasks.ensure_index([("username", pymongo.ASCENDING)])

    flask_app = flask.Flask(__name__)

    flask_app.config.from_mapping(**config)
    flask_app.session_interface = MongoDBSessionInterface(
        mongo_client,
        config.get('mongo_opt', {}).get('database', 'INGInious'),
        "sessions",
        config.get('SESSION_USE_SIGNER', False),
        True  # config.get('SESSION_PERMANENT', True)
    )

    # Init gettext
    available_translations = {
        "fr": "Français",
        "es": "Español",
        "pt": "Português",
        "el": "ελληνικά",
        "vi": "Tiếng Việt",
        "nl": "Nederlands",
        "de": "Deutsch"
    }

    available_languages = {"en": "English"}
    available_languages.update(available_translations)

    l10n_manager = L10nManager()

    l10n_manager.translations["en"] = gettext.NullTranslations(
    )  # English does not need translation ;-)
    for lang in available_translations.keys():
        l10n_manager.translations[lang] = gettext.translation(
            'messages',
            get_root_path() + '/frontend/i18n', [lang])

    builtins.__dict__['_'] = l10n_manager.gettext

    if config.get("maintenance", False):
        template_helper = TemplateHelper(PluginManager(), None,
                                         config.get('use_minified_js', True))
        template_helper.add_to_template_globals("get_homepath", get_homepath)
        template_helper.add_to_template_globals("pkg_version", __version__)
        template_helper.add_to_template_globals("available_languages",
                                                available_languages)
        template_helper.add_to_template_globals("_", _)
        flask_app.template_helper = template_helper
        init_flask_maintenance_mapping(flask_app)
        return flask_app.wsgi_app, lambda: None

    default_allowed_file_extensions = config['allowed_file_extensions']
    default_max_file_size = config['max_file_size']

    zmq_context, __ = start_asyncio_and_zmq(config.get('debug_asyncio', False))

    # Init the different parts of the app
    plugin_manager = PluginManager()

    # Add the "agent types" inside the frontend, to allow loading tasks and managing envs
    register_base_env_types()

    # Create the FS provider
    if "fs" in config:
        fs_provider = filesystem_from_config_dict(config["fs"])
    else:
        task_directory = config["tasks_directory"]
        fs_provider = LocalFSProvider(task_directory)

    default_task_dispensers = {
        task_dispenser.get_id(): task_dispenser
        for task_dispenser in [TableOfContents, CombinatoryTest]
    }

    default_problem_types = {
        problem_type.get_type(): problem_type
        for problem_type in [
            DisplayableCodeProblem, DisplayableCodeSingleLineProblem,
            DisplayableFileProblem, DisplayableMultipleChoiceProblem,
            DisplayableMatchProblem
        ]
    }

    course_factory, task_factory = create_factories(fs_provider,
                                                    default_task_dispensers,
                                                    default_problem_types,
                                                    plugin_manager)

    user_manager = UserManager(database, config.get('superadmins', []))

    update_pending_jobs(database)

    client = create_arch(config, fs_provider, zmq_context, course_factory)

    lti_outcome_manager = LTIOutcomeManager(database, user_manager,
                                            course_factory)

    submission_manager = WebAppSubmissionManager(client, user_manager,
                                                 database, gridfs,
                                                 plugin_manager,
                                                 lti_outcome_manager)

    template_helper = TemplateHelper(plugin_manager, user_manager,
                                     config.get('use_minified_js', True))

    register_utils(database, user_manager, template_helper)

    is_tos_defined = config.get("privacy_page", "") and config.get(
        "terms_page", "")

    # Init web mail
    mail.init_app(flask_app)

    # Add some helpers for the templates
    template_helper.add_to_template_globals("_", _)
    template_helper.add_to_template_globals("str", str)
    template_helper.add_to_template_globals("available_languages",
                                            available_languages)
    template_helper.add_to_template_globals("get_homepath", get_homepath)
    template_helper.add_to_template_globals("pkg_version", __version__)
    template_helper.add_to_template_globals(
        "allow_registration", config.get("allow_registration", True))
    template_helper.add_to_template_globals("sentry_io_url",
                                            config.get("sentry_io_url"))
    template_helper.add_to_template_globals("user_manager", user_manager)
    template_helper.add_to_template_globals("default_allowed_file_extensions",
                                            default_allowed_file_extensions)
    template_helper.add_to_template_globals("default_max_file_size",
                                            default_max_file_size)
    template_helper.add_to_template_globals("is_tos_defined", is_tos_defined)
    template_helper.add_other(
        "course_admin_menu",
        lambda course, current: course_admin_utils.get_menu(
            course, current, template_helper.render, plugin_manager,
            user_manager))
    template_helper.add_other(
        "preferences_menu", lambda current: preferences_utils.get_menu(
            config.get("allow_deletion", True), current, template_helper.
            render, plugin_manager, user_manager))

    # Not found page
    def flask_not_found(e):
        return template_helper.render("notfound.html",
                                      message=e.description), 404

    flask_app.register_error_handler(404, flask_not_found)

    # Forbidden page
    def flask_forbidden(e):
        return template_helper.render("forbidden.html",
                                      message=e.description), 403

    flask_app.register_error_handler(403, flask_forbidden)

    # Enable stacktrace display if needed
    web_debug = config.get('web_debug', False)

    def flask_internalerror(e):
        return template_helper.render("internalerror.html",
                                      message=e.description), 500

    flask_app.register_error_handler(InternalServerError, flask_internalerror)

    if web_debug is True:
        flask_app.debug = True
    elif isinstance(web_debug, str):
        flask_app.debug = False

    # Insert the needed singletons into the application, to allow pages to call them
    flask_app.get_homepath = get_homepath
    flask_app.plugin_manager = plugin_manager
    flask_app.course_factory = course_factory
    flask_app.task_factory = task_factory
    flask_app.submission_manager = submission_manager
    flask_app.user_manager = user_manager
    flask_app.l10n_manager = l10n_manager
    flask_app.template_helper = template_helper
    flask_app.database = database
    flask_app.gridfs = gridfs
    flask_app.client = client
    flask_app.default_allowed_file_extensions = default_allowed_file_extensions
    flask_app.default_max_file_size = default_max_file_size
    flask_app.backup_dir = config.get("backup_directory", './backup')
    flask_app.webterm_link = config.get("webterm", None)
    flask_app.lti_outcome_manager = lti_outcome_manager
    flask_app.allow_registration = config.get("allow_registration", True)
    flask_app.allow_deletion = config.get("allow_deletion", True)
    flask_app.available_languages = available_languages
    flask_app.welcome_page = config.get("welcome_page", None)
    flask_app.terms_page = config.get("terms_page", None)
    flask_app.privacy_page = config.get("privacy_page", None)
    flask_app.static_directory = config.get("static_directory", "./static")
    flask_app.webdav_host = config.get("webdav_host", None)

    # Init the mapping of the app
    init_flask_mapping(flask_app)

    # Loads plugins
    plugin_manager.load(client, flask_app, course_factory, task_factory,
                        database, user_manager, submission_manager,
                        config.get("plugins", []))

    # Start the inginious.backend
    client.start()

    return flask_app.wsgi_app, lambda: _close_app(mongo_client, client)
Exemplo n.º 3
0
def get_app(config):
    """
    :param config: the configuration dict
    :return: A new app
    """
    # First, disable debug. It will be enabled in the configuration, later.
    web.config.debug = False

    config = _put_configuration_defaults(config)

    web.config.session_parameters.update(config['session_parameters'])

    mongo_client = MongoClient(
        host=config.get('mongo_opt', {}).get('host', 'localhost'))
    database = mongo_client[config.get('mongo_opt',
                                       {}).get('database', 'INGInious')]
    gridfs = GridFS(database)

    # Init database if needed
    db_version = database.db_version.find_one({})
    if db_version is None:
        database.submissions.ensure_index([("username", pymongo.ASCENDING)])
        database.submissions.ensure_index([("courseid", pymongo.ASCENDING)])
        database.submissions.ensure_index([("courseid", pymongo.ASCENDING),
                                           ("taskid", pymongo.ASCENDING)])
        database.submissions.ensure_index([("submitted_on", pymongo.DESCENDING)
                                           ])  # sort speed
        database.user_tasks.ensure_index([("username", pymongo.ASCENDING),
                                          ("courseid", pymongo.ASCENDING),
                                          ("taskid", pymongo.ASCENDING)],
                                         unique=True)
        database.user_tasks.ensure_index([("username", pymongo.ASCENDING),
                                          ("courseid", pymongo.ASCENDING)])
        database.user_tasks.ensure_index([("courseid", pymongo.ASCENDING),
                                          ("taskid", pymongo.ASCENDING)])
        database.user_tasks.ensure_index([("courseid", pymongo.ASCENDING)])
        database.user_tasks.ensure_index([("username", pymongo.ASCENDING)])

    appli = CookieLessCompatibleApplication(MongoStore(database, 'sessions'))

    # Init gettext
    available_translations = {
        "fr": "Français",
        "es": "Español",
        "pt": "Português",
        "el": "ελληνικά",
        "vi": "Tiếng Việt",
        "nl": "Nederlands",
        "de": "Deutsch"
    }

    available_languages = {"en": "English"}
    available_languages.update(available_translations)

    appli.add_translation(
        "en",
        gettext.NullTranslations())  # English does not need translation ;-)
    for lang in available_translations.keys():
        appli.add_translation(
            lang,
            gettext.translation('messages',
                                get_root_path() + '/frontend/i18n', [lang]))

    builtins.__dict__['_'] = appli.gettext

    if config.get("maintenance", False):
        template_helper = TemplateHelper(PluginManager(), None,
                                         'frontend/templates',
                                         'frontend/templates/layout',
                                         'frontend/templates/layout_lti',
                                         config.get('use_minified_js', True))
        template_helper.add_to_template_globals("get_homepath",
                                                appli.get_homepath)
        template_helper.add_to_template_globals("_", _)
        appli.template_helper = template_helper
        appli.init_mapping(urls_maintenance)
        return appli.wsgifunc(), appli.stop

    default_allowed_file_extensions = config['allowed_file_extensions']
    default_max_file_size = config['max_file_size']

    zmq_context, __ = start_asyncio_and_zmq(config.get('debug_asyncio', False))

    # Init the different parts of the app
    plugin_manager = PluginManager()

    # Add the "agent types" inside the frontend, to allow loading tasks and managing envs
    register_base_env_types()

    # Create the FS provider
    if "fs" in config:
        fs_provider = filesystem_from_config_dict(config["fs"])
    else:
        task_directory = config["tasks_directory"]
        fs_provider = LocalFSProvider(task_directory)

    default_task_dispensers = {
        task_dispenser.get_id(): task_dispenser
        for task_dispenser in [TableOfContents, CombinatoryTest]
    }

    default_problem_types = {
        problem_type.get_type(): problem_type
        for problem_type in [
            DisplayableCodeProblem, DisplayableCodeSingleLineProblem,
            DisplayableFileProblem, DisplayableMultipleChoiceProblem,
            DisplayableMatchProblem
        ]
    }

    course_factory, task_factory = create_factories(fs_provider,
                                                    default_task_dispensers,
                                                    default_problem_types,
                                                    plugin_manager)

    user_manager = UserManager(appli.get_session(), database,
                               config.get('superadmins', []))

    update_pending_jobs(database)

    client = create_arch(config, fs_provider, zmq_context, course_factory)

    lti_outcome_manager = LTIOutcomeManager(database, user_manager,
                                            course_factory)

    submission_manager = WebAppSubmissionManager(client, user_manager,
                                                 database, gridfs,
                                                 plugin_manager,
                                                 lti_outcome_manager)

    template_helper = TemplateHelper(plugin_manager, user_manager,
                                     'frontend/templates',
                                     'frontend/templates/layout',
                                     'frontend/templates/layout_lti',
                                     config.get('use_minified_js', True))

    register_utils(database, user_manager, template_helper)

    is_tos_defined = config.get("privacy_page", "") and config.get(
        "terms_page", "")

    # Init web mail
    smtp_conf = config.get('smtp', None)
    if smtp_conf is not None:
        web.config.smtp_server = smtp_conf["host"]
        web.config.smtp_port = int(smtp_conf["port"])
        web.config.smtp_starttls = bool(smtp_conf.get("starttls", False))
        web.config.smtp_username = smtp_conf.get("username", "")
        web.config.smtp_password = smtp_conf.get("password", "")
        web.config.smtp_sendername = smtp_conf.get("sendername",
                                                   "*****@*****.**")

    # Add some helpers for the templates
    template_helper.add_to_template_globals("_", _)
    template_helper.add_to_template_globals("str", str)
    template_helper.add_to_template_globals("available_languages",
                                            available_languages)
    template_helper.add_to_template_globals("get_homepath", appli.get_homepath)
    template_helper.add_to_template_globals(
        "allow_registration", config.get("allow_registration", True))
    template_helper.add_to_template_globals("sentry_io_url",
                                            config.get("sentry_io_url"))
    template_helper.add_to_template_globals("user_manager", user_manager)
    template_helper.add_to_template_globals("default_allowed_file_extensions",
                                            default_allowed_file_extensions)
    template_helper.add_to_template_globals("default_max_file_size",
                                            default_max_file_size)
    template_helper.add_to_template_globals("is_tos_defined", is_tos_defined)
    template_helper.add_other(
        "course_admin_menu",
        lambda course, current: course_admin_utils.get_menu(
            course, current, template_helper.get_renderer(False),
            plugin_manager, user_manager))
    template_helper.add_other(
        "preferences_menu", lambda current: preferences_utils.get_menu(
            appli, current, template_helper.get_renderer(False),
            plugin_manager, user_manager))

    # Not found page
    appli.notfound = lambda message='Page not found': web.notfound(
        template_helper.get_renderer().notfound(message))

    # Forbidden page
    appli.forbidden = lambda message='Forbidden': web.forbidden(
        template_helper.get_renderer().forbidden(message))

    # Enable stacktrace display if needed
    web_debug = config.get('web_debug', False)
    appli.internalerror = internalerror_generator(
        template_helper.get_renderer(False))
    if web_debug is True:
        web.config.debug = True
        appli.internalerror = debugerror
    elif isinstance(web_debug, str):
        web.config.debug = False
        appli.internalerror = emailerrors(web_debug, appli.internalerror)

    # Insert the needed singletons into the application, to allow pages to call them
    appli.plugin_manager = plugin_manager
    appli.course_factory = course_factory
    appli.task_factory = task_factory
    appli.submission_manager = submission_manager
    appli.user_manager = user_manager
    appli.template_helper = template_helper
    appli.database = database
    appli.gridfs = gridfs
    appli.client = client
    appli.default_allowed_file_extensions = default_allowed_file_extensions
    appli.default_max_file_size = default_max_file_size
    appli.backup_dir = config.get("backup_directory", './backup')
    appli.webterm_link = config.get("webterm", None)
    appli.lti_outcome_manager = lti_outcome_manager
    appli.allow_registration = config.get("allow_registration", True)
    appli.allow_deletion = config.get("allow_deletion", True)
    appli.available_languages = available_languages
    appli.welcome_page = config.get("welcome_page", None)
    appli.terms_page = config.get("terms_page", None)
    appli.privacy_page = config.get("privacy_page", None)
    appli.static_directory = config.get("static_directory", "./static")
    appli.webdav_host = config.get("webdav_host", None)

    # Init the mapping of the app
    appli.init_mapping(urls)

    # Loads plugins
    plugin_manager.load(client, appli, course_factory, task_factory, database,
                        user_manager, submission_manager,
                        config.get("plugins", []))

    # Start the inginious.backend
    client.start()

    return appli.wsgifunc(), lambda: _close_app(appli, mongo_client, client)
Exemplo n.º 4
0
def get_app(config):
    """
    :param config: the configuration dict
    :return: A new app
    """
    # First, disable debug. It will be enabled in the configuration, later.
    web.config.debug = False

    config = _put_configuration_defaults(config)

    mongo_client = MongoClient(host=config.get('mongo_opt', {}).get('host', 'localhost'))
    database = mongo_client[config.get('mongo_opt', {}).get('database', 'INGInious')]
    gridfs = GridFS(database)

    # Init database if needed
    db_version = database.db_version.find_one({})
    if db_version is None:
        database.submissions.ensure_index([("username", pymongo.ASCENDING)])
        database.submissions.ensure_index([("courseid", pymongo.ASCENDING)])
        database.submissions.ensure_index([("courseid", pymongo.ASCENDING), ("taskid", pymongo.ASCENDING)])
        database.submissions.ensure_index([("submitted_on", pymongo.DESCENDING)])  # sort speed
        database.user_tasks.ensure_index(
            [("username", pymongo.ASCENDING), ("courseid", pymongo.ASCENDING), ("taskid", pymongo.ASCENDING)],
            unique=True)
        database.user_tasks.ensure_index([("username", pymongo.ASCENDING), ("courseid", pymongo.ASCENDING)])
        database.user_tasks.ensure_index([("courseid", pymongo.ASCENDING), ("taskid", pymongo.ASCENDING)])
        database.user_tasks.ensure_index([("courseid", pymongo.ASCENDING)])
        database.user_tasks.ensure_index([("username", pymongo.ASCENDING)])

    appli = CookieLessCompatibleApplication(MongoStore(database, 'sessions'))

    # Init gettext
    available_languages = {
        "en": "English",
        "fr": "Français"
    }

    for lang in available_languages.keys():
        appli.add_translation(lang, gettext.translation('messages', get_root_path() + '/frontend/i18n', [lang]))

    builtins.__dict__['_'] = appli.gettext

    if config.get("maintenance", False):
        template_helper = TemplateHelper(PluginManager(), None,
                                         'frontend/templates',
                                         'frontend/templates/layout',
                                         'frontend/templates/layout_lti',
                                         config.get('use_minified_js', True))
        template_helper.add_to_template_globals("get_homepath", appli.get_homepath)
        template_helper.add_to_template_globals("_", _)
        appli.template_helper = template_helper
        appli.init_mapping(urls_maintenance)
        return appli.wsgifunc(), appli.stop

    default_allowed_file_extensions = config['allowed_file_extensions']
    default_max_file_size = config['max_file_size']

    zmq_context, __ = start_asyncio_and_zmq(config.get('debug_asyncio', False))

    # Init the different parts of the app
    plugin_manager = PluginManager()

    # Create the FS provider
    if "fs" in config:
        fs_provider = filesystem_from_config_dict(config["fs"])
    else:
        task_directory = config["tasks_directory"]
        fs_provider = LocalFSProvider(task_directory)

    default_problem_types = {
        problem_type.get_type(): problem_type for problem_type in [DisplayableCodeProblem,
                                                                   DisplayableCodeSingleLineProblem,
                                                                   DisplayableFileProblem,
                                                                   DisplayableMultipleChoiceProblem,
                                                                   DisplayableMatchProblem]
    }

    course_factory, task_factory = create_factories(fs_provider, default_problem_types, plugin_manager, WebAppCourse, WebAppTask)

    user_manager = UserManager(appli.get_session(), database, config.get('superadmins', []))

    update_pending_jobs(database)

    client = create_arch(config, fs_provider, zmq_context, course_factory)

    lti_outcome_manager = LTIOutcomeManager(database, user_manager, course_factory)

    submission_manager = WebAppSubmissionManager(client, user_manager, database, gridfs, plugin_manager, lti_outcome_manager)

    template_helper = TemplateHelper(plugin_manager, user_manager, 'frontend/templates',
                                     'frontend/templates/layout',
                                     'frontend/templates/layout_lti',
                                     config.get('use_minified_js', True))



    # Init web mail
    smtp_conf = config.get('smtp', None)
    if smtp_conf is not None:
        web.config.smtp_server = smtp_conf["host"]
        web.config.smtp_port = int(smtp_conf["port"])
        web.config.smtp_starttls = bool(smtp_conf.get("starttls", False))
        web.config.smtp_username = smtp_conf.get("username", "")
        web.config.smtp_password = smtp_conf.get("password", "")
        web.config.smtp_sendername = smtp_conf.get("sendername", "*****@*****.**")

    # Add some helpers for the templates
    template_helper.add_to_template_globals("_", _)
    template_helper.add_to_template_globals("str", str)
    template_helper.add_to_template_globals("available_languages", available_languages)
    template_helper.add_to_template_globals("get_homepath", appli.get_homepath)
    template_helper.add_to_template_globals("allow_registration", config.get("allow_registration", True))
    template_helper.add_to_template_globals("user_manager", user_manager)
    template_helper.add_to_template_globals("default_allowed_file_extensions", default_allowed_file_extensions)
    template_helper.add_to_template_globals("default_max_file_size", default_max_file_size)
    template_helper.add_other("course_admin_menu",
                              lambda course, current: course_admin_utils.get_menu(course, current, template_helper.get_renderer(False),
                                                                                  plugin_manager, user_manager))
    template_helper.add_other("preferences_menu",
                              lambda current: preferences_utils.get_menu(appli, current, template_helper.get_renderer(False),
                                                                                 plugin_manager, user_manager))

    # Not found page
    appli.notfound = lambda: web.notfound(template_helper.get_renderer().notfound('Page not found'))

    # Enable stacktrace display if needed
    web_debug = config.get('web_debug', False)
    appli.internalerror = internalerror_generator(template_helper.get_renderer(False))
    if web_debug is True:
        web.config.debug = True
        appli.internalerror = debugerror
    elif isinstance(web_debug, str):
        web.config.debug = False
        appli.internalerror = emailerrors(web_debug, appli.internalerror)

    # Insert the needed singletons into the application, to allow pages to call them
    appli.plugin_manager = plugin_manager
    appli.course_factory = course_factory
    appli.task_factory = task_factory
    appli.submission_manager = submission_manager
    appli.user_manager = user_manager
    appli.template_helper = template_helper
    appli.database = database
    appli.gridfs = gridfs
    appli.default_allowed_file_extensions = default_allowed_file_extensions
    appli.default_max_file_size = default_max_file_size
    appli.backup_dir = config.get("backup_directory", './backup')
    appli.webterm_link = config.get("webterm", None)
    appli.lti_outcome_manager = lti_outcome_manager
    appli.allow_registration = config.get("allow_registration", True)
    appli.allow_deletion = config.get("allow_deletion", True)
    appli.available_languages = available_languages
    appli.welcome_page = config.get("welcome_page", None)
    appli.static_directory = config.get("static_directory", "./static")
    appli.webdav_host = config.get("webdav_host", None)

    # Init the mapping of the app
    appli.init_mapping(urls)

    # Loads plugins
    plugin_manager.load(client, appli, course_factory, task_factory, database, user_manager, submission_manager, config.get("plugins", []))

    # Start the inginious.backend
    client.start()

    return appli.wsgifunc(), lambda: _close_app(appli, mongo_client, client)