7
0
mirror of https://github.com/ChristianLight/tutor.git synced 2024-05-31 13:20:48 +00:00
tutor/tutor/config.py
Régis Behmo 07b3d113d4 Simplify environment generation
Environment is no longer generated separately for each target, but only
once the configuration is saved.

Note that the environment is automatically updated during
re-configuration, based on a "version" file stored in the environment.
2019-03-18 18:34:42 +01:00

188 lines
5.8 KiB
Python

import json
import os
import yaml
import click
from . import exceptions
from . import env
from . import fmt
from . import opts
from .__about__ import __version__
@click.group(
short_help="Configure Open edX",
help="""Configure Open edX and store configuration values in $TUTOR_ROOT/config.yml"""
)
def config():
pass
@click.command(help="Create and save configuration interactively")
@opts.root
@click.option("--silent", is_flag=True, help="Run non-interactively")
@opts.key_value
def save(root, silent, set_):
config = {}
load_files(config, root)
for k, v in set_:
config[k] = v
if not silent:
load_interactive(config)
save_config(root, config)
load_defaults(config)
save_env(root, config)
@click.command(
help="Print the project root",
)
@opts.root
def printroot(root):
click.echo(root)
def load(root):
"""
Load configuration, and generate it interactively if the file does not
exist.
"""
config = {}
load_files(config, root)
should_update_env = False
if not os.path.exists(config_path(root)):
load_interactive(config)
should_update_env = True
save_config(root, config)
load_defaults(config)
if not env.is_up_to_date(root):
click.echo(fmt.alert(
"The current environment stored at {} is not up-to-date: it is at "
"v{} while the 'tutor' binary is at v{}. The environment will be "
"upgraded now. Any change you might have made will be overwritten.".format(
env.base_dir(root), env.version(root), __version__
)
))
should_update_env = True
if should_update_env:
save_env(root, config)
return config
def load_files(config, root):
convert_json2yml(root)
# Load base values
base = yaml.load(env.read("config.yml"))
for k, v in base.items():
config[k] = v
# Load user file
path = config_path(root)
if os.path.exists(path):
with open(path) as fi:
loaded = yaml.load(fi.read())
for key, value in loaded.items():
config[key] = value
def load_interactive(config):
ask("Your website domain name for students (LMS)", "LMS_HOST", config)
ask("Your website domain name for teachers (CMS)", "CMS_HOST", config)
ask("Your platform name/title", "PLATFORM_NAME", config)
ask("Your public contact email address", "CONTACT_EMAIL", config)
ask_choice(
"The default language code for the platform",
"LANGUAGE_CODE", config,
['en', 'am', 'ar', 'az', 'bg-bg', 'bn-bd', 'bn-in', 'bs', 'ca',
'ca@valencia', 'cs', 'cy', 'da', 'de-de', 'el', 'en-uk', 'en@lolcat',
'en@pirate', 'es-419', 'es-ar', 'es-ec', 'es-es', 'es-mx', 'es-pe',
'et-ee', 'eu-es', 'fa', 'fa-ir', 'fi-fi', 'fil', 'fr', 'gl', 'gu',
'he', 'hi', 'hr', 'hu', 'hy-am', 'id', 'it-it', 'ja-jp', 'kk-kz',
'km-kh', 'kn', 'ko-kr', 'lt-lt', 'ml', 'mn', 'mr', 'ms', 'nb', 'ne',
'nl-nl', 'or', 'pl', 'pt-br', 'pt-pt', 'ro', 'ru', 'si', 'sk', 'sl',
'sq', 'sr', 'sv', 'sw', 'ta', 'te', 'th', 'tr-tr', 'uk', 'ur', 'vi',
'uz', 'zh-cn', 'zh-hk', 'zh-tw'],
)
ask_bool(
("Activate SSL/TLS certificates for HTTPS access? Important note:"
"this will NOT work in a development environment."),
"ACTIVATE_HTTPS", config
)
ask_bool(
"Activate Student Notes service (https://open.edx.org/features/student-notes)?",
"ACTIVATE_NOTES", config
)
ask_bool(
"Activate Xqueue for external grader services (https://github.com/edx/xqueue)?",
"ACTIVATE_XQUEUE", config
)
def load_defaults(config):
defaults = yaml.load(env.read("config-defaults.yml"))
for k, v in defaults.items():
if k not in config:
config[k] = v
def ask(question, key, config):
default = env.render_str(config[key], config)
config[key] = click.prompt(
fmt.question(question),
prompt_suffix=" ", default=default, show_default=True,
)
def ask_bool(question, key, config):
default = "y" if config[key] else "n"
suffix = " [Yn]" if config[key] else " [yN]"
answer = click.prompt(
fmt.question(question) + suffix,
type=click.Choice(["y", "n"]),
prompt_suffix=" ", default=default, show_default=False, show_choices=False,
)
config[key] = answer == "y"
def ask_choice(question, key, config, choices):
default = config[key]
answer = click.prompt(
fmt.question(question),
type=click.Choice(choices),
prompt_suffix=" ", default=default, show_choices=False,
)
config[key] = answer
def convert_json2yml(root):
json_path = os.path.join(root, "config.json")
if not os.path.exists(json_path):
return
if os.path.exists(config_path(root)):
raise exceptions.TutorError(
"Both config.json and config.yml exist in {}: only one of these files must exist to continue".format(root)
)
with open(json_path) as fi:
config = json.load(fi)
save_config(root, config)
os.remove(json_path)
click.echo(fmt.info("File config.json detected in {} and converted to config.yml".format(root)))
def save_config(root, config):
env.render_dict(config)
path = config_path(root)
directory = os.path.dirname(path)
if not os.path.exists(directory):
os.makedirs(directory)
with open(path, "w") as of:
yaml.dump(config, of, default_flow_style=False)
click.echo(fmt.info("Configuration saved to {}".format(path)))
def save_env(root, config):
env.render_full(root, config)
click.echo(fmt.info("Environment generated in {}".format(env.base_dir(root))))
def config_path(root):
return os.path.join(root, "config.yml")
config.add_command(save)
config.add_command(printroot)