Example #1
0
def get_snapshot_listing(project: Project):
    """

    :param project:
    :return:
    """

    snapshots_directory = project.snapshot_path()
    if not os.path.exists(snapshots_directory):
        return []

    out = []
    for item in os.listdir(snapshots_directory):
        item_path = os.path.join(snapshots_directory, item)

        results_path = os.path.join(item_path, 'results.js')
        if not os.path.exists(results_path):
            continue

        out.append(dict(
            name=item,
            url=project.snapshot_url(item),
            directory=item_path,
            last_modified=os.path.getmtime(results_path)
        ))

    out = sorted(out, key=lambda x: x['last_modified'])
    return out
Example #2
0
def toggle_muting(response: Response,
                  project: Project,
                  step_name: str,
                  value: bool = None) -> Response:
    """

    :param response:
    :param project:
    :param step_name:
    :param value:
    :return:
    """

    index = project.index_of_step(step_name)
    if index is None:
        return response.fail(
            code='NO_SUCH_STEP',
            message='No step found with name: "{}"'.format(step_name)).kernel(
                name=step_name).console().response

    step = project.steps[index]
    if value is None:
        value = not bool(step.is_muted)

    step.is_muted = value

    return response.notify(
        kind='SUCCESS',
        code='STEP_MUTE_ENABLED' if step.is_muted else 'STEP_MUTE_DISABLED',
        message='Muting has been {}'.format(
            'enabled' if step.is_muted else 'disabled')).kernel(
                project=project.kernel_serialize()).console().response
Example #3
0
def index_from_location(response: Response,
                        project: Project,
                        location: typing.Union[str, int] = None,
                        default: int = None) -> int:
    """

    :param response:
    :param project:
    :param location:
    :param default:
    :return:
    """

    if location is None:
        return default

    if isinstance(location, (int, float)):
        return int(location)

    if isinstance(location, str):
        location = location.strip('"')

        try:
            location = int(location)
            return None if location < 0 else location
        except Exception:
            index = project.index_of_step(location)
            return default if index is None else (index + 1)

    return default
Example #4
0
def index_from_location(
        response: Response,
        project: Project,
        location: typing.Union[str, int] = None,
        default: int = None
) -> int:
    """

    :param response:
    :param project:
    :param location:
    :param default:
    :return:
    """

    if location is None:
        return default

    if isinstance(location, (int, float)):
        return int(location)

    if isinstance(location, str):
        location = location.strip('"')

        try:
            location = int(location)
            return None if location < 0 else location
        except Exception:
            index = project.index_of_step(location)
            return default if index is None else (index + 1)

    return default
Example #5
0
def initialize(project: typing.Union[str, Project]):
    """

    :param project:
    :return:
    """

    if isinstance(project, str):
        project = Project(source_directory=project)

    cauldron.project.load(project)
    return project
Example #6
0
def toggle_muting(
        response: Response,
        project: Project,
        step_name: str,
        value: bool = None
) -> Response:
    """

    :param response:
    :param project:
    :param step_name:
    :param value:
    :return:
    """

    index = project.index_of_step(step_name)
    if index is None:
        return response.fail(
            code='NO_SUCH_STEP',
            message='No step found with name: "{}"'.format(step_name)
        ).kernel(
            name=step_name
        ).console().response

    step = project.steps[index]
    if value is None:
        value = not bool(step.is_muted)

    step.is_muted = value

    return response.notify(
        kind='SUCCESS',
        code='STEP_MUTE_ENABLED' if step.is_muted else 'STEP_MUTE_DISABLED',
        message='Muting has been {}'.format(
            'enabled' if step.is_muted else 'disabled'
        )
    ).kernel(
        project=project.kernel_serialize()
    ).console().response
Example #7
0
def create_snapshot(
        project: Project,
        *args: typing.List[str],
        show: bool = True
):
    """

    :param project:
    :param show:
    :return:
    """

    if len(args) < 1:
        snapshot_name = datetime.now().strftime('%Y%b%d-%H-%M-%S')
    else:
        snapshot_name = args[0]

    snapshot_directory = project.snapshot_path()
    if not os.path.exists(snapshot_directory):
        os.makedirs(snapshot_directory)

    snapshot_name = snapshot_name.replace(' ', '-')
    snapshot_directory = project.snapshot_path(snapshot_name)
    environ.systems.remove(snapshot_directory)

    shutil.copytree(project.output_directory, snapshot_directory)

    url = project.snapshot_url(snapshot_name)

    environ.log_header('Snapshot URL', 5)
    environ.log(
        '* {}'.format(url),
        whitespace_bottom=1,
        indent_by=2
    )

    if show:
        webbrowser.open(url)
Example #8
0
def open_snapshot(project: Project, name: str) -> dict:
    """

    :param project:
    :param name:
    :return:
    """

    snapshots_directory = project.snapshot_path()
    if not os.path.exists(snapshots_directory):
        return None

    item_path = os.path.join(snapshots_directory, name)
    results_path = os.path.join(item_path, 'results.js')

    if not os.path.exists(results_path):
        return None

    return dict(
        name=name,
        url=project.snapshot_url(name),
        directory=item_path,
        last_modified=os.path.getmtime(results_path)
    )
Example #9
0
def create_step(response: Response,
                project: Project,
                name: str,
                position: typing.Union[str, int],
                title: str = None) -> Response:
    """

    :param response:
    :param project:
    :param name:
    :param position:
    :param title:
    :return:
    """

    name = name.strip('"')
    title = title.strip('"') if title else title
    index = index_from_location(response, project, position)
    if index is None:
        index = len(project.steps)

    name_parts = naming.explode_filename(name, project.naming_scheme)

    if not project.naming_scheme and not name_parts['name']:
        name_parts['name'] = naming.find_default_filename(
            [s.definition.name for s in project.steps])

    name_parts['index'] = index
    name = naming.assemble_filename(scheme=project.naming_scheme, **name_parts)

    res = step_support.synchronize_step_names(project, index)
    response.consume(res)
    if response.failed:
        return response

    step_renames = res.returned
    step_data = {'name': name}

    if title:
        step_data['title'] = title

    result = project.add_step(step_data, index=index)

    if not os.path.exists(result.source_path):
        contents = ('import cauldron as cd\n\n'
                    if result.source_path.endswith('.py') else '')

        with open(result.source_path, 'w+') as f:
            f.write(contents)

    project.save()
    project.write()

    index = project.steps.index(result)

    step_changes = [
        dict(name=result.definition.name,
             filename=result.filename,
             action='added',
             step=writing.step_writer.serialize(result)._asdict(),
             after=None if index < 1 else project.steps[index -
                                                        1].definition.name)
    ]

    return response.update(
        project=project.kernel_serialize(),
        step_name=result.definition.name,
        step_path=result.source_path,
        step_changes=step_changes,
        step_renames=step_renames).notify(
            kind='CREATED',
            code='STEP_CREATED',
            message='"{}" step has been created'.format(
                result.definition.name)).console(whitespace=1).response
Example #10
0
def create_step(
        response: Response,
        project: Project,
        name: str,
        position: typing.Union[str, int],
        title: str = None
) -> Response:
    """

    :param response:
    :param project:
    :param name:
    :param position:
    :param title:
    :return:
    """

    name = name.strip('"')
    title = title.strip('"') if title else title
    index = index_from_location(response, project, position)
    if index is None:
        index = len(project.steps)

    name_parts = naming.explode_filename(name, project.naming_scheme)

    if not project.naming_scheme and not name_parts['name']:
        name_parts['name'] = naming.find_default_filename(
            [s.definition.name for s in project.steps]
        )

    name_parts['index'] = index
    name = naming.assemble_filename(
        scheme=project.naming_scheme,
        **name_parts
    )

    res = step_support.synchronize_step_names(project, index)
    response.consume(res)
    if response.failed:
        return response

    step_renames = res.returned
    step_data = {'name': name}

    if title:
        step_data['title'] = title

    result = project.add_step(step_data, index=index)

    if not os.path.exists(result.source_path):
        contents = (
            'import cauldron as cd\n\n'
            if result.source_path.endswith('.py')
            else ''
        )

        with open(result.source_path, 'w+') as f:
            f.write(contents)

    project.save()
    project.write()

    index = project.steps.index(result)

    step_changes = [dict(
        name=result.definition.name,
        filename=result.filename,
        action='added',
        step=writing.step_writer.serialize(result)._asdict(),
        after=None if index < 1 else project.steps[index - 1].definition.name
    )]

    return response.update(
        project=project.kernel_serialize(),
        step_name=result.definition.name,
        step_path=result.source_path,
        step_changes=step_changes,
        step_renames=step_renames
    ).notify(
        kind='CREATED',
        code='STEP_CREATED',
        message='"{}" step has been created'.format(result.definition.name)
    ).console(
        whitespace=1
    ).response
Example #11
0
def run_step(
        response: Response,
        project: Project,
        step: typing.Union[ProjectStep, str],
        force: bool = False
) -> bool:
    """

    :param response:
    :param project:
    :param step:
    :param force:
    :return:
    """

    step = get_step(project, step)
    if step is None:
        return False

    status = check_status(response, project, step, force)
    if status == ERROR_STATUS:
        return False

    step.error = None

    if status == SKIP_STATUS:
        return True

    os.chdir(os.path.dirname(step.source_path))
    project.current_step = step
    step.report.clear()
    step.dom = None
    step.is_running = True
    step.progress_message = None
    step.progress = 0

    # Set the top-level display and cache values to the current project values
    # before running the step for availability within the step scripts
    cauldron.shared = cauldron.project.shared

    redirection.enable(step)

    try:
        result = _execute_step(project, step)
    except Exception as error:
        result = dict(
            success=False,
            message='{}'.format(error),
            html_message='<pre>{}</pre>'.format(error)
        )

    os.chdir(os.path.expanduser('~'))

    step.mark_dirty(not result['success'])
    step.error = result.get('html_message')
    step.last_modified = time.time() if result['success'] else 0.0
    step.is_running = False
    step.progress = 0
    step.progress_message = None
    step.dumps()

    # Make sure this is called prior to printing response information to the
    # console or that will come along for the ride
    redirection.disable(step)

    if result['success']:
        environ.log('[{}]: Updated'.format(step.definition.name))
    else:
        response.fail(
            message='Step execution error',
            code='EXECUTION_ERROR',
            project=project.kernel_serialize(),
            step_name=step.definition.name
        ).console_raw(result['message'])

    return result['success']
Example #12
0
def remove_snapshot(
        project: Project,
        *args: typing.List[str]
):
    """

    :param project:
    :param args:
    :return:
    """

    if len(args) < 1 or not args[0].strip():
        environ.log(
            """
            Are you sure you want to remove all snapshots in this project?
            """)

        if not query.confirm('Confirm Delete All', False):
            environ.log(
                '[ABORTED]: No snapshots were deleted',
                whitespace=1
            )
            return

        if not environ.systems.remove(project.snapshot_path()):
            environ.log(
                '[ERROR]: Failed to delete snapshots',
                whitespace=1
            )
            return

        environ.log(
            '[SUCCESS]: All snapshots have been removed',
            whitespace=1
        )
        return

    snapshot_name = args[0]

    environ.log(
        """
        Are you sure you want to remove the snapshot "{}"?
        """.format(snapshot_name),
        whitespace=1
    )

    if not query.confirm('Confirm Deletion', False):
        environ.log(
            """
            [ABORTED]: "{}" was not removed
            """.format(snapshot_name),
            whitespace=1
        )
        return

    if not environ.systems.remove(project.snapshot_path(snapshot_name)):
        environ.log(
            """
            [ERROR]: Unable to delete snapshot "{}" at this time
            """.format(snapshot_name),
            whitespace=1
        )
        return

    environ.log(
        """
        [SUCCESS]: Snapshot "{}" was removed
        """.format(snapshot_name),
        whitespace=1
    )
    return
Example #13
0
def run_step(
        response: Response,
        project: Project,
        step: typing.Union[ProjectStep, str],
        force: bool = False
) -> bool:
    """

    :param response:
    :param project:
    :param step:
    :param force:
    :return:
    """
    step = get_step(project, step)
    if step is None:
        return False

    status = check_status(response, project, step, force)
    if status == ERROR_STATUS:
        return False

    step.error = None

    if status == SKIP_STATUS:
        return True

    os.chdir(os.path.dirname(step.source_path))
    project.current_step = step
    step.report.clear()
    step.dom = None
    step.is_visible = True
    step.is_running = True
    step.progress_message = None
    step.progress = 0
    step.sub_progress_message = None
    step.sub_progress = 0
    step.start_time = datetime.utcnow()
    step.end_time = None

    # Set the top-level display and cache values to the current project values
    # before running the step for availability within the step scripts
    cauldron.shared = cauldron.project.shared

    redirection.enable(step)

    try:
        result = _execute_step(project, step)
    except Exception as error:
        result = dict(
            success=False,
            message='{}'.format(error),
            html_message='<pre>{}</pre>'.format(error)
        )

    step.end_time = datetime.utcnow()
    os.chdir(os.path.expanduser('~'))

    step.mark_dirty(not result['success'])
    step.error = result.get('html_message')
    step.last_modified = time.time() if result['success'] else 0.0
    step.is_running = False
    step.progress = 0
    step.progress_message = None
    step.dumps()

    # Make sure this is called prior to printing response information to the
    # console or that will come along for the ride
    redirection.disable(step)

    step.project.stop_condition = result.get(
        'stop_condition',
        StopCondition(False, False)
    )

    if result['success']:
        environ.log('[{}]: Updated in {}'.format(
            step.definition.name,
            step.get_elapsed_timestamp()
        ))
    else:
        response.fail(
            message='Step execution error',
            code='EXECUTION_ERROR',
            project=project.kernel_serialize(),
            step_name=step.definition.name
        ).console_raw(result['message'])

    return result['success']
Example #14
0
def run_local(
        context: cli.CommandContext,
        project: projects.Project,
        project_steps: typing.List[projects.ProjectStep],
        force: bool,
        continue_after: bool,
        single_step: bool,
        limit: int,
        print_status: bool,
        skip_library_reload: bool = False
) -> environ.Response:
    """
    Execute the run command locally within this cauldron environment

    :param context:
    :param project:
    :param project_steps:
    :param force:
    :param continue_after:
    :param single_step:
    :param limit:
    :param print_status:
    :param skip_library_reload:
        Whether or not to skip reloading all project libraries prior to
        execution of the project. By default this is False in which case
        the project libraries are reloaded prior to execution.
    :return:
    """
    skip_reload = (
        skip_library_reload
        or environ.modes.has(environ.modes.TESTING)
    )
    if not skip_reload:
        runner.reload_libraries()

    environ.log_header('RUNNING', 5)

    steps_run = []

    if single_step:
        # If the user specifies the single step flag, only run one step. Force
        # the step to be run if they specified it explicitly

        ps = project_steps[0] if len(project_steps) > 0 else None
        force = force or (single_step and bool(ps is not None))
        steps_run = runner.section(
            response=context.response,
            project=project,
            starting=ps,
            limit=1,
            force=force
        )

    elif continue_after or len(project_steps) == 0:
        # If the continue after flag is set, start with the specified step
        # and run the rest of the project after that. Or, if no steps were
        # specified, run the entire project with the specified flags.

        ps = project_steps[0] if len(project_steps) > 0 else None
        steps_run = runner.complete(
            context.response,
            project,
            ps,
            force=force,
            limit=limit
        )
    else:
        for ps in project_steps:
            steps_run += runner.section(
                response=context.response,
                project=project,
                starting=ps,
                limit=max(1, limit),
                force=force or (limit < 1 and len(project_steps) < 2),
                skips=steps_run + []
            )

    project.write()
    environ.log_blanks()

    step_changes = []
    for ps in steps_run:
        step_changes.append(dict(
            name=ps.definition.name,
            action='updated',
            step=writing.step_writer.serialize(ps)._asdict()
        ))

    context.response.update(step_changes=step_changes)

    if print_status or context.response.failed:
        context.response.update(project=project.kernel_serialize())

    return context.response
Example #15
0
def run_local(context: cli.CommandContext, project: projects.Project,
              project_steps: typing.List[projects.ProjectStep], force: bool,
              continue_after: bool, single_step: bool, limit: int,
              print_status: bool) -> environ.Response:
    """
    Execute the run command locally within this cauldron environment

    :param context:
    :param project:
    :param project_steps:
    :param force:
    :param continue_after:
    :param single_step:
    :param limit:
    :param print_status:
    :return:
    """

    runner.reload_libraries()

    environ.log_header('RUNNING', 5)

    steps_run = []

    if single_step:
        # If the user specifies the single step flag, only run one step. Force
        # the step to be run if they specified it explicitly

        ps = project_steps[0] if len(project_steps) > 0 else None
        force = force or (single_step and bool(ps is not None))
        steps_run = runner.section(response=context.response,
                                   project=project,
                                   starting=ps,
                                   limit=1,
                                   force=force)

    elif continue_after or len(project_steps) == 0:
        # If the continue after flag is set, start with the specified step
        # and run the rest of the project after that. Or, if no steps were
        # specified, run the entire project with the specified flags.

        ps = project_steps[0] if len(project_steps) > 0 else None
        steps_run = runner.complete(context.response,
                                    project,
                                    ps,
                                    force=force,
                                    limit=limit)
    else:
        for ps in project_steps:
            steps_run += runner.section(response=context.response,
                                        project=project,
                                        starting=ps,
                                        limit=max(1, limit),
                                        force=force or
                                        (limit < 1 and len(project_steps) < 2),
                                        skips=steps_run + [])

    project.write()
    environ.log_blanks()

    step_changes = []
    for ps in steps_run:
        step_changes.append(
            dict(name=ps.definition.name,
                 action='updated',
                 step=writing.step_writer.serialize(ps)._asdict()))

    context.response.update(step_changes=step_changes)

    if print_status or context.response.failed:
        context.response.update(project=project.kernel_serialize())

    return context.response
Example #16
0
def modify_step(
        response: Response,
        project: Project,
        name: str,
        new_name: str = None,
        position: typing.Union[str, int] = None,
        title: str = None
):
    """

    :param response:
    :param project:
    :param name:
    :param new_name:
    :param position:
    :param title:
    :return:
    """

    new_name = new_name if new_name else name
    old_index = project.index_of_step(name)
    new_index = index_from_location(response, project, position, old_index)

    if new_index > old_index:
        # If the current position of the step occurs before the new position
        # of the step, the new index has to be shifted by one to account for
        # the fact that this step will no longer be in this position when it
        # get placed in the position within the project
        new_index -= 1

    old_step = project.remove_step(name)
    if not old_step:
        response.fail(
            code='NO_SUCH_STEP',
            message='Unable to modify unknown step "{}"'.format(name)
        ).console(
            whitespace=1
        )
        return False

    source_path = old_step.source_path
    if os.path.exists(source_path):
        temp_path = '{}.cauldron_moving'.format(source_path)
        shutil.move(source_path, temp_path)
    else:
        temp_path = None

    res = step_support.synchronize_step_names(project, new_index)
    response.consume(res)
    step_renames = res.returned

    new_name_parts = naming.explode_filename(new_name, project.naming_scheme)
    new_name_parts['index'] = new_index

    if not project.naming_scheme and not new_name_parts['name']:
        new_name_parts['name'] = naming.find_default_filename(
            [s.definition.name for s in project.steps]
        )

    new_name = naming.assemble_filename(
        scheme=project.naming_scheme,
        **new_name_parts
    )

    step_data = {'name': new_name}
    if title is None:
        if old_step.definition.title:
            step_data['title'] = old_step.definition.title
    else:
        step_data['title'] = title.strip('"')

    new_step = project.add_step(step_data, new_index)

    project.save()

    if not os.path.exists(new_step.source_path):
        if temp_path and os.path.exists(temp_path):
            shutil.move(temp_path, new_step.source_path)
        else:
            with open(new_step.source_path, 'w+') as f:
                f.write('')

    if new_index > 0:
        before_step = project.steps[new_index - 1].definition.name
    else:
        before_step = None

    step_renames[old_step.definition.name] = {
        'name': new_step.definition.name,
        'title': new_step.definition.title
    }

    step_changes = [dict(
        name=new_step.definition.name,
        filename=new_step.filename,
        action='modified',
        after=before_step
    )]

    response.update(
        project=project.kernel_serialize(),
        step_name=new_step.definition.name,
        step_changes=step_changes,
        step_renames=step_renames
    ).notify(
        kind='SUCCESS',
        code='STEP_MODIFIED',
        message='Step modifications complete'
    ).console(
        whitespace=1
    )

    project.write()

    return True
Example #17
0
def modify_step(response: Response,
                project: Project,
                name: str,
                new_name: str = None,
                position: typing.Union[str, int] = None,
                title: str = None):
    """

    :param response:
    :param project:
    :param name:
    :param new_name:
    :param position:
    :param title:
    :return:
    """

    new_name = new_name if new_name else name
    old_index = project.index_of_step(name)
    new_index = index_from_location(response, project, position, old_index)

    if new_index > old_index:
        # If the current position of the step occurs before the new position
        # of the step, the new index has to be shifted by one to account for
        # the fact that this step will no longer be in this position when it
        # get placed in the position within the project
        new_index -= 1

    old_step = project.remove_step(name)
    if not old_step:
        response.fail(
            code='NO_SUCH_STEP',
            message='Unable to modify unknown step "{}"'.format(name)).console(
                whitespace=1)
        return False

    source_path = old_step.source_path
    if os.path.exists(source_path):
        temp_path = '{}.cauldron_moving'.format(source_path)
        shutil.move(source_path, temp_path)
    else:
        temp_path = None

    res = step_support.synchronize_step_names(project, new_index)
    response.consume(res)
    step_renames = res.returned

    new_name_parts = naming.explode_filename(new_name, project.naming_scheme)
    new_name_parts['index'] = new_index

    if not project.naming_scheme and not new_name_parts['name']:
        new_name_parts['name'] = naming.find_default_filename(
            [s.definition.name for s in project.steps])

    new_name = naming.assemble_filename(scheme=project.naming_scheme,
                                        **new_name_parts)

    step_data = {'name': new_name}
    if title is None:
        if old_step.definition.get('title'):
            step_data['title'] = old_step.definition.title
    else:
        step_data['title'] = title.strip('"')

    new_step = project.add_step(step_data, new_index)

    project.save()

    if not os.path.exists(new_step.source_path):
        if temp_path and os.path.exists(temp_path):
            shutil.move(temp_path, new_step.source_path)
        else:
            with open(new_step.source_path, 'w+') as f:
                f.write('')

    if new_index > 0:
        before_step = project.steps[new_index - 1].definition.name
    else:
        before_step = None

    step_renames[old_step.definition.name] = {
        'name': new_step.definition.name,
        'title': new_step.definition.title
    }

    step_changes = [
        dict(name=new_step.definition.name,
             filename=new_step.filename,
             action='modified',
             after=before_step)
    ]

    response.update(
        project=project.kernel_serialize(),
        step_name=new_step.definition.name,
        step_changes=step_changes,
        step_renames=step_renames).notify(
            kind='SUCCESS',
            code='STEP_MODIFIED',
            message='Step modifications complete').console(whitespace=1)

    project.write()

    return True