def _cp_template_for_char(name, character, prefs, fn=None): """ Copy the template for a character Copies the configured template file for `character` and optionally modifies the template's body using `fn`. Args: name (str): Character name character (Character): Character that needs a template prefs (Settings): Settings object used to find the template fn (callable): Optional function that is called before the new file is saved. It must accept a single string argument which will contain the template contents. Returns: Result object. Openable will contain the path to the new character file. """ # get template path template_path = prefs.get('types.{}.sheet_template'.format( character.type_key)) if not template_path: return result.ConfigError( errmsg="Could not find template {}".format(character.type_key)) # get path for the new file target_path = create_path_from_character(character, prefs=prefs) filename = name + path.splitext(template_path)[1] target_path = path.join(target_path, filename) if path.exists(target_path): return result.FSError( errmsg="Character '{}' already exists!".format(name)) # Add tags header = character.build_header() + '\n\n' # Copy template try: with open(template_path, 'r') as template_data: data = header + template_data.read() except IOError as err: return result.FSError(errmsg=err.strerror + " ({})".format(template_path)) if callable(fn): data = fn(data) # Write the new file try: with open(target_path, 'w') as char_file: char_file.write(data) except IOError as err: return result.FSError(errmsg=err.strerror + " ({})".format(target_path)) return result.Success(openable=[target_path])
def latest(thingtype, **kwargs): """ Open the latest plot and/or session file Args: prefs (Settings): Settings object to use. Uses internal settings by default. Returns: Result object. Openable will contain the path(s) to the requested file(s). """ prefs = kwargs.get('prefs', settings.InternalSettings()) plot_path = prefs.get('paths.required.plot') session_path = prefs.get('paths.required.session') if not (path.exists(plot_path) and path.exists(session_path)): return result.FSError( errmsg="Cannot access paths '{}' and/or '{}'".format( plot_path, session_path)) latest_plot = latest_file_info(plot_path, PLOT_REGEX) latest_session = latest_file_info(session_path, SESSION_REGEX) if thingtype == 'both': openable = [latest_plot['path'], latest_session['path']] elif thingtype == 'session': openable = [latest_session['path']] elif thingtype == 'plot': openable = [latest_plot['path']] else: return result.OptionError( errmsg="Unrecognized type '{}'".format(thingtype)) return result.Success(openable=openable)
def reorg(*search, ignore=None, purge=False, verbose=False, commit=False, **kwargs): """ Move character files into the correct paths. Character files are moved so that their path matches the ideal path as closely as possible. No new directories are created. This function ignores tags not found in Character.KNOWN_TAGS. Args: search (list): Paths to search for character files. Items can be strings or lists of strings. ignore (list): Paths to ignore purge (bool): Whether empty directories should be deleted after all files have been moved. verbose (bool): Whether to print changes as they are made commit (bool): Whether to actually move files around prefs (Settings): Settings object to use. Uses internal settings by default. Returns: Result object. Openable will be empty. """ prefs = kwargs.get('prefs', settings.InternalSettings()) if not ignore: ignore = [] ignore.extend(prefs.get_ignored_paths('reorg')) show_changes = verbose or not commit changelog = [] base_path = prefs.get('paths.required.characters') if not path.exists(base_path): return result.FSError(errmsg="Cannot access '{}'".format(base_path)) if show_changes: changelog.append("Move characters") for parsed_character in parser.get_characters(flatten(search), ignore): new_path = util.create_path_from_character(parsed_character, base_path=base_path) if path.normcase(path.normpath(new_path)) != path.normcase(path.normpath(path.dirname(parsed_character['path']))): if show_changes: changelog.append("* Move {} to {}".format(parsed_character['path'], new_path)) if commit: try: shmove(parsed_character['path'], new_path) except OSError as e: if show_changes: changelog.append("\t- dest path already exists; skipping") if purge: if show_changes: changelog.append("Purge empty directories") for empty_path in util.find_empty_dirs(base_path): if show_changes: changelog.append("* Remove empty directory {}".format(empty_path)) if commit: rmdir(empty_path) return result.Success(printables=changelog)
def latest(thingtype='', **kwargs): """ Open the latest plot and/or session file Args: thingtype (str): Type of the things to return. Use "session" to get the latest session file, "plot" to get the latest plot, and anything else to get all plot and session files. prefs (Settings): Settings object to use. Uses internal settings by default. Returns: Result object. Openable will contain the path(s) to the requested file(s). """ prefs = kwargs.get('prefs', settings.InternalSettings()) plot_dir = Path(prefs.get('paths.required.plot')) session_dir = Path(prefs.get('paths.required.session')) if not plot_dir.exists(): return result.FSError( errmsg="Cannot access plot path '{}'".format(plot_dir)) if not session_dir.exists(): return result.FSError( errmsg="Cannot access session path '{}'".format(session_dir)) plot_template = prefs.get('story.templates.plot') plot_regex = regex_from_template(plot_template) latest_plot = latest_file(plot_dir, plot_regex) session_template = prefs.get('story.templates.session') session_regex = regex_from_template(session_template) latest_session = latest_file(session_dir, session_regex) if thingtype == 'session': openable = [str(latest_session.path)] elif thingtype == 'plot': openable = [str(latest_plot.path)] else: openable = [str(latest_session.path), str(latest_plot.path)] return result.Success(openable=openable)
def session(**kwargs): """ Create the files for a new game session. Finds the plot and session log files for the last session, copies the plot, and creates a new empty session log. If the latest plot file is ahead of the latest session, a new plot file will *not* be created. Likewise if the latest session file is ahead, a new session file will *not* be created. Args: prefs (Settings): Settings object to use. Uses internal settings by default. Returns: Result object. Openable will contain the current and previous session log and plot planning files. """ prefs = kwargs.get('prefs', settings.InternalSettings()) plot_path = prefs.get('paths.required.plot') session_path = prefs.get('paths.required.session') if not (path.exists(plot_path) and path.exists(session_path)): return result.FSError( errmsg="Cannot access paths '{}' and/or '{}'".format( plot_path, session_path)) latest_plot = latest_file_info(plot_path, PLOT_REGEX) latest_session = latest_file_info(session_path, SESSION_REGEX) new_number = min(latest_plot['number'], latest_session['number']) + 1 openable = [] if latest_session['exists']: if latest_session['number'] < new_number: # create new session log old_session_path = latest_session['path'] new_session_path = path.join( session_path, "session {num}{ext}".format(num=new_number, ext=latest_session['ext'])) shcopy(prefs.get('story.session_template'), new_session_path) else: # present existing session files, since we don't have to create one old_session_path = path.join( session_path, "session {num}{ext}".format(num=latest_session['number'] - 1, ext=latest_session['ext'])) new_session_path = latest_session['path'] openable.extend((new_session_path, old_session_path)) else: # no existing session, so just copy the template template_path = prefs.get('story.session_template') new_session_path = path.join( session_path, "session {num}{ext}".format(num=new_number, ext=path.splitext(template_path)[1])) shcopy(template_path, new_session_path) openable.append(new_session_path) if latest_plot['exists']: if latest_plot['number'] < new_number: # copy old plot old_plot_path = latest_plot['path'] new_plot_path = path.join( plot_path, "plot {num}{ext}".format(num=new_number, ext=latest_plot['ext'])) shcopy(old_plot_path, new_plot_path) else: # present existing plot files, since we don't have to create one old_plot_path = path.join( plot_path, "plot {num}{ext}".format(num=latest_plot['number'] - 1, ext=latest_plot['ext'])) new_plot_path = latest_plot['path'] openable.extend((new_plot_path, old_plot_path)) else: # no old plot to copy, so create a blank new_plot_path = path.join( plot_path, "plot {num}{ext}".format(num=new_number, ext=prefs.get('story.plot_ext'))) with open(new_plot_path, 'w') as new_plot: new_plot.write(' ') openable.append(new_plot_path) return result.Success(openable=openable)
def session(**kwargs): """ Create the files for a new game session. Finds the plot and session log files for the last session, copies the plot, and creates a new empty session log. If the latest plot file is ahead of the latest session, a new plot file will *not* be created. Likewise if the latest session file is ahead, a new session file will *not* be created. Args: prefs (Settings): Settings object to use. Uses internal settings by default. Returns: Result object. Openable will contain the current and previous session log and plot planning files. """ prefs = kwargs.get('prefs', settings.InternalSettings()) plot_dir = Path(prefs.get('paths.required.plot')) session_dir = Path(prefs.get('paths.required.session')) if not plot_dir.exists(): return result.FSError( errmsg="Cannot access plot path '{}'".format(plot_dir)) if not session_dir.exists(): return result.FSError( errmsg="Cannot access session path '{}'".format(session_dir)) plot_template = prefs.get('story.templates.plot') if SEQUENCE_KEYWORD not in str(plot_template): return result.ConfigError( errmsg="Plot template has no number placeholder ({})".format( SEQUENCE_KEYWORD)) plot_regex = regex_from_template(plot_template) latest_plot = latest_file(plot_dir, plot_regex) session_template = prefs.get('story.templates.session') if SEQUENCE_KEYWORD not in str(session_template): return result.ConfigError( errmsg="Session template has no number placeholder ({})".format( SEQUENCE_KEYWORD)) session_regex = regex_from_template(session_template) latest_session = latest_file(session_dir, session_regex) new_number = min(latest_plot.number, latest_session.number) + 1 def copy_templates(dest_dir, templates): """ Create new story files from templates. This is responsible for creating the new file name based on `new_number`, loading the template contents, substituting the "NNN" and "((COPY))" keywords, and writing the result to the new file. """ def old_file_contents(old_file_path): """ Get the previous file's contents. """ try: with open(old_file_path, 'r') as old_file: return old_file.read() except (FileNotFoundError, IsADirectoryError): return '' for template_path in templates: if SEQUENCE_KEYWORD not in str(template_path): print_err("Template {} has no number placeholder ({})".format( template_path, SEQUENCE_KEYWORD)) continue new_file_name = template_path.name.replace(SEQUENCE_KEYWORD, str(new_number)) destination = dest_dir.joinpath(new_file_name) if destination.exists(): continue with open(template_path, 'r') as f: data = f.read() data = data.replace(SEQUENCE_KEYWORD, str(new_number)) if COPY_KEYWORD in data: file_regex = regex_from_template(template_path) old_file_path = latest_file(dest_dir, file_regex).path data = data.replace(COPY_KEYWORD, old_file_contents(old_file_path)) with open(destination, 'w') as f: f.write(data) plot_templates = flatten([ prefs.get('story.templates.plot'), prefs.get('story.templates.plot_extras') ]) copy_templates(plot_dir, plot_templates) session_templates = flatten([ prefs.get('story.templates.session'), prefs.get('story.templates.session_extras') ]) copy_templates(session_dir, session_templates) openable = [ str(latest_file(session_dir, session_regex).path), str(latest_file(plot_dir, plot_regex).path) ] old_session_name = session_template.name.replace(SEQUENCE_KEYWORD, str(new_number - 1)) old_session = session_dir.joinpath(old_session_name) if old_session.exists(): openable.append(str(old_session)) old_plot_name = plot_template.name.replace(SEQUENCE_KEYWORD, str(new_number - 1)) old_plot = plot_dir.joinpath(old_plot_name) if old_plot.exists(): openable.append(str(old_plot)) return result.Success(openable=openable)