Beispiel #1
0
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])
Beispiel #2
0
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)
Beispiel #3
0
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)
Beispiel #4
0
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)
Beispiel #5
0
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)
Beispiel #6
0
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)