def create_config_if_needed(): """ If no configuration file exists, create the default configuration file. Note that this will not download the initial tutorial set. """ from tutorlib.config.configuration import config_exists, save_config from tutorlib.config.namespaces import Namespace if not config_exists(): print('Creating default config...', end='', flush=True) default_config = Namespace(**DEFAULT_CONFIG) save_config(default_config) print('done')
def write_tutorial_hashes(f, path): """ Create the tutorial hashes file for the tutorial package in the given destination directory. It is assumed that destination_dir contains a valid tutorial package. As a result, this function must only be called after the package itself has been created. Args: f (file): The file to write the tutorial hashes data to. path (str): The path to the tutorial package. """ options = Namespace(tut_dir=path, ans_dir='/tmp/notreal') tutorial_package = TutorialPackage(path, options) tutorial_package_name = tutorial_package.name.replace(' ', '_') for problem_set in tutorial_package.problem_sets: date_obj = datetime.strptime(problem_set.date, INPUT_DATE_FORMAT) date_obj = date_obj.replace(hour=DUE_DATE_HOUR) date_obj -= TIMEZONE_OFFSET # reverse timezone offset due_date_str = date_obj.strftime(DATE_FORMAT) problem_set_name = problem_set.name.replace(' ', '_') for tutorial in problem_set: b32hash = base64.b32encode(tutorial.hash).decode('utf8') tutorial_name = tutorial.name.replace(' ', '_') data = [ b32hash, due_date_str, tutorial_package_name, problem_set_name, tutorial_name, ] f.write(' '.join(data) + '\n')
def get_tutorial_package(path): options = Namespace(tut_dir=path, ans_dir='/tmp/notreal') return TutorialPackage(os.path.basename(path), options)
def load_config(): """ Load the MyPyTutor configuration file. If the file does not exist, cannot be opened, or cannot be parsed, then revert to using default configuration values. If the config file can be opened and parsed, but is missing any default configuration value, revert to using that value for the relevant key. All values will be unwrapped (and so converted to the appropriate format, according to the SPECIAL_FORMATS module variable). Returns: A Namespace mapping configuration sections to a Namespace mapping section options to values. For example, if the configuration file looks like this: [section_name] key1 = value Then the attribute `result.section_name.key1` will equal `value`. """ # parse the config file parser = configparser.ConfigParser() try: with open(CONFIG_FILE, 'rU') as f: parser.read_file(f) except (IOError, FileNotFoundError, configparser.ParsingError): # ignore parsing errors - we will just revert to defaults pass # transform this to a more useful format # this involves hard-coding the keys, but that would have to happen in some # place to *use* them anyway defaults = { 'online': { 'store_credentials': '1', 'username': '', }, 'resolution': { 'height': '800', 'width': '600', }, 'tutorials': { 'names': '', 'default': '', }, } cfg_dict = defaults # add in all parsed config values for section in parser.sections(): if section not in defaults: defaults[section] = {} for option in parser.options(section): cfg_dict[section][option] = parser.get(section, option) # our final step is 'unwrapping' values # this handles non-standard config formats, such as lists # (side note: this is why it would have been better to use json) cfg_dict = { section: { option: unwrap_value(section, option, value) for option, value in options.items() } for section, options in cfg_dict.items() } return Namespace(**cfg_dict)