Example #1
0
def check_status(response: Response,
                 project: Project,
                 step: ProjectStep,
                 force: bool = False) -> str:
    """

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

    path = step.source_path

    if step.is_muted:
        environ.log('[{}]: Muted (skipped)'.format(step.definition.name))
        return SKIP_STATUS

    if not os.path.exists(path):
        response.fail(code='MISSING_SOURCE_FILE',
                      message='Source file not found "{}"'.format(path),
                      id=step.definition.name,
                      path=path).console('[{id}]: Not found "{path}"'.format(
                          id=step.definition.name, path=path))
        return ERROR_STATUS

    if not force and not step.is_dirty():
        environ.log('[{}]: Nothing to update'.format(step.definition.name))
        return SKIP_STATUS

    return OK_STATUS
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 echo_steps(response: Response, project: Project):
    """
    :param response:
    :param project:
    :return:
    """

    if len(project.steps) < 1:
        response.update(steps=[]).notify(
            kind='SUCCESS', code='ECHO_STEPS',
            message='No steps in project').console("""
            [NONE]: This project does not have any steps yet. To add a new
                step use the command:

                steps add [YOUR_STEP_NAME]

                and a new step will be created in this project.
            """,
                                                   whitespace=1)
        return

    response.update(steps=[ps.kernel_serialize()
                           for ps in project.steps]).notify(
                               kind='SUCCESS',
                               code='ECHO_STEPS').console_header(
                                   'Project Steps',
                                   level=3).console('\n'.join([
                                       '* {}'.format(ps.definition.name)
                                       for ps in project.steps
                                   ]),
                                                    indent_by=2,
                                                    whitespace_bottom=1)
Example #4
0
def fetch_recent(response: Response) -> str:
    """

    :return:
    """

    recent_paths = environ.configs.fetch('recent_paths', [])

    if len(recent_paths) < 1:
        response.fail(
            code='NO_RECENT_PROJECTS',
            message='There are no recent projects available'
        ).console()
        return None

    index, path = query.choice(
        'Recently Opened Projects',
        'Choose a project',
        recent_paths + ['Cancel'],
        0
    )
    if index == len(recent_paths):
        return None

    return path
Example #5
0
def synchronize_step_names(
        project: 'projects.Project',
        insert_index: int = None
) -> Response:
    """
    :param project:
    :param insert_index:
    """

    response = Response()
    response.returned = dict()

    if not project.naming_scheme:
        return response

    create_mapper_func = functools.partial(
        create_rename_entry,
        insertion_index=insert_index
    )

    step_renames = list([create_mapper_func(s) for s in project.steps])
    step_renames = list(filter(lambda sr: (sr is not None), step_renames))

    if not step_renames:
        return response

    try:
        backup_path = create_backup(project)
    except Exception as err:
        return response.fail(
            code='RENAME_BACKUP_ERROR',
            message='Unable to create backup name',
            error=err
        ).response

    try:
        step_renames = list([stash_source(sr) for sr in step_renames])
        step_renames = list([unstash_source(sr) for sr in step_renames])
    except Exception as err:
        return response.fail(
            code='RENAME_FILE_ERROR',
            message='Unable to rename files',
            error=err
        ).response

    response.returned = update_steps(project, step_renames)
    project.save()

    try:
        os.remove(backup_path)
    except PermissionError:
        pass

    return response
Example #6
0
def fetch_last(response: Response) -> typing.Union[str, None]:
    """ Returns the last opened project path if such a path exists """

    recent_paths = environ.configs.fetch('recent_paths', [])

    if len(recent_paths) < 1:
        response.fail(
            code='NO_RECENT_PROJECTS',
            message='No projects have been opened recently').console()
        return None

    return recent_paths[0]
Example #7
0
def fetch_last(response: Response) -> typing.Union[str, None]:
    """ Returns the last opened project path if such a path exists """

    recent_paths = environ.configs.fetch('recent_paths', [])

    if len(recent_paths) < 1:
        response.fail(
            code='NO_RECENT_PROJECTS',
            message='No projects have been opened recently'
        ).console()
        return None

    return recent_paths[0]
Example #8
0
def get_parser(
        target_module,
        raw_args: typing.List[str],
        assigned_args: dict
) -> typing.Tuple[ArgumentParser, Response]:
    """

    :param target_module:
    :param raw_args:
    :param assigned_args:
    :return:
    """

    response = Response()

    description = None
    if hasattr(target_module, 'DESCRIPTION'):
        description = getattr(target_module, 'DESCRIPTION')

    parser = ArgumentParser(
        prog=target_module.NAME,
        add_help=False,
        description=description
    )

    parser.add_argument(
        '-h', '--help',
        dest='show_help',
        action='store_true',
        default=False,
        help=cli.reformat(
            """
            Print this help information instead of running the command
            """
        )
    )

    if not hasattr(target_module, 'populate'):
        return parser, response

    try:
        getattr(target_module, 'populate')(parser, raw_args, assigned_args)
    except Exception as err:
        response.fail(
            code='ARGS_PARSE_ERROR',
            message='Unable to parse command arguments',
            error=err,
            name=target_module.NAME
        ).console(whitespace=1)

    return parser, response
Example #9
0
def remove_step(response: Response,
                project: 'projects.Project',
                name: str,
                keep_file: bool = False) -> Response:
    """

    :param response:
    :param project:
    :param name:
    :param keep_file:
    :return:
    """

    step = project.remove_step(name)
    if not step:
        return response.fail(
            code='NO_SUCH_STEP',
            message='Step "{}" not found. Unable to remove.'.format(
                name)).kernel(name=name).console(whitespace=1).response

    project.save()
    project.write()

    if not keep_file:
        os.remove(step.source_path)

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

    step_renames = res.returned

    removed_name = 'REMOVED--{}'.format(uuid.uuid4())
    step_renames[name] = dict(name=removed_name, title='')

    step_changes = [
        dict(name=removed_name, filename=step.filename, action='removed')
    ]

    return response.update(
        project=project.kernel_serialize(),
        step_changes=step_changes,
        step_renames=step_renames).notify(
            kind='SUCCESS',
            code='STEP_REMOVED',
            message='Removed "{}" step from project'.format(name)).console(
                whitespace=1).response
Example #10
0
    def test_initialize_results(self, initialize_results_path: MagicMock):
        """Should initialize paths for project"""

        response = Response()
        project = MagicMock()
        result = opener.initialize_results(response, project)
        self.assertTrue(result)
        self.assertEqual(1, initialize_results_path.call_count)
Example #11
0
def get_project(response: Response):
    """

    :return:
    """

    project = cauldron.project.internal_project

    if not project:
        response.fail(code='NO_OPEN_PROJECT',
                      message='No project opened').console("""
            [ERROR]: No project has been opened. Use the "open" command to
                open a project, or the "create" command to create a new one.
            """,
                                                           whitespace=1)
        return None

    return project
Example #12
0
    def test_initialize_results_abort(self,
                                      initialize_results_path: MagicMock):
        """Should abort initializing project has no results path"""

        response = Response()
        project = MagicMock()
        project.results_path = None
        result = opener.initialize_results(response, project)
        self.assertTrue(result)
        self.assertEqual(0, initialize_results_path.call_count)
Example #13
0
    def test_initialize_results_error(self,
                                      initialize_results_path: MagicMock):
        """Should fail to initialize paths for project"""

        initialize_results_path.side_effect = ValueError('FAKE')
        response = Response()
        project = MagicMock()
        result = opener.initialize_results(response, project)
        self.assertFalse(result)
        self.assertTrue(response.failed)
        self.assertEqual(1, initialize_results_path.call_count)
Example #14
0
def fetch_recent(response: Response) -> str:
    """

    :return:
    """

    recent_paths = environ.configs.fetch('recent_paths', [])

    if len(recent_paths) < 1:
        response.fail(
            code='NO_RECENT_PROJECTS',
            message='There are no recent projects available').console()
        return None

    index, path = query.choice('Recently Opened Projects', 'Choose a project',
                               recent_paths + ['Cancel'], 0)
    if index == len(recent_paths):
        return None

    return path
Example #15
0
def get_parser(module, raw_args: typing.List[str],
               assigned_args: dict) -> typing.Tuple[ArgumentParser, Response]:
    """

    :param module:
    :param raw_args:
    :param assigned_args:
    :return:
    """

    response = Response()

    description = None
    if hasattr(module, 'DESCRIPTION'):
        description = getattr(module, 'DESCRIPTION')

    parser = ArgumentParser(prog=module.NAME,
                            add_help=False,
                            description=description)

    parser.add_argument('-h',
                        '--help',
                        dest='show_help',
                        action='store_true',
                        default=False,
                        help=cli.reformat("""
            Print this help information instead of running the command
            """))

    if not hasattr(module, 'populate'):
        return parser, response

    try:
        getattr(module, 'populate')(parser, raw_args, assigned_args)
    except Exception as err:
        response.fail(code='ARGS_PARSE_ERROR',
                      message='Unable to parse command arguments',
                      error=err,
                      name=module.NAME).console(whitespace=1)

    return parser, response
Example #16
0
def check_status(
        response: Response,
        project: Project,
        step: ProjectStep,
        force: bool = False
) -> str:
    """

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

    path = step.source_path

    if step.is_muted:
        environ.log('[{}]: Muted (skipped)'.format(step.definition.name))
        return SKIP_STATUS

    if not os.path.exists(path):
        response.fail(
            code='MISSING_SOURCE_FILE',
            message='Source file not found "{}"'.format(path),
            id=step.definition.name,
            path=path
        ).console(
            '[{id}]: Not found "{path}"'.format(
                id=step.definition.name,
                path=path
            )
        )
        return ERROR_STATUS

    if not force and not step.is_dirty():
        environ.log('[{}]: Nothing to update'.format(step.definition.name))
        return SKIP_STATUS

    return OK_STATUS
Example #17
0
def echo_steps(response: Response, project: Project):
    """
    :param response:
    :param project:
    :return:
    """

    if len(project.steps) < 1:
        response.update(
            steps=[]
        ).notify(
            kind='SUCCESS',
            code='ECHO_STEPS',
            message='No steps in project'
        ).console(
            """
            [NONE]: This project does not have any steps yet. To add a new
                step use the command:

                steps add [YOUR_STEP_NAME]

                and a new step will be created in this project.
            """,
            whitespace=1
        )
        return

    response.update(
        steps=[ps.kernel_serialize() for ps in project.steps]
    ).notify(
        kind='SUCCESS',
        code='ECHO_STEPS'
    ).console_header(
        'Project Steps',
        level=3
    ).console(
        '\n'.join(['* {}'.format(ps.definition.name) for ps in project.steps]),
        indent_by=2,
        whitespace_bottom=1
    )
Example #18
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 #19
0
def get_project(response: Response):
    """

    :return:
    """

    project = cauldron.project.internal_project

    if not project:
        response.fail(
            code='NO_OPEN_PROJECT',
            message='No project opened'
        ).console(
            """
            [ERROR]: No project has been opened. Use the "open" command to
                open a project, or the "create" command to create a new one.
            """,
            whitespace=1
        )
        return None

    return project
Example #20
0
    def test_explode_line(self):
        """
        """

        r = Response('TEST').update(test_info='More test information')

        m = messages.Message('Some-Message',
                             'This is a test',
                             'Message that will be turned into a string',
                             response=r)

        m = str(m)
        self.assertGreater(
            len(m), 0,
            messages.Message('Message to String',
                             'Unable to convert message to string'))
Example #21
0
def synchronize_step_names(project: 'projects.Project',
                           insert_index: int = None) -> Response:
    """
    :param project:
    :param insert_index:
    """

    response = Response()
    response.returned = dict()

    if not project.naming_scheme:
        return response

    create_mapper_func = functools.partial(create_rename_entry,
                                           insertion_index=insert_index)

    step_renames = list([create_mapper_func(s) for s in project.steps])
    step_renames = list(filter(lambda sr: (sr is not None), step_renames))

    if not step_renames:
        return response

    try:
        backup_path = create_backup(project)
    except Exception as err:
        return response.fail(code='RENAME_BACKUP_ERROR',
                             message='Unable to create backup name',
                             error=err).response

    try:
        step_renames = list([stash_source(sr) for sr in step_renames])
        step_renames = list([unstash_source(sr) for sr in step_renames])
    except Exception as err:
        return response.fail(code='RENAME_FILE_ERROR',
                             message='Unable to rename files',
                             error=err).response

    response.returned = update_steps(project, step_renames)
    project.save()

    try:
        os.remove(backup_path)
    except PermissionError:
        pass

    return response
Example #22
0
def check_connection(url: str, force: bool) -> Response:
    """ """

    response = Response()
    if force:
        return response

    ping = '{}/ping'.format(url)

    response.notify(
        kind='STARTING',
        code='CONNECTING',
        message='Establishing connection to: {}'.format(url)
    ).console(
        whitespace_top=1
    )

    try:
        result = requests.get(ping)

        if result.status_code != 200:
            raise request_exceptions.ConnectionError()
    except request_exceptions.InvalidURL as error:
        return response.fail(
            code='INVALID_URL',
            message='Invalid connection URL. Unable to establish connection',
            error=error
        ).console(
            whitespace=1
        ).response
    except request_exceptions.ConnectionError as error:
        return response.fail(
            code='CONNECTION_ERROR',
            message='Unable to connect to remote cauldron host',
            error=error
        ).console(
            whitespace=1
        ).response
    except Exception as error:
        return response.fail(
            code='CONNECT_COMMAND_ERROR',
            message='Failed to connect to the remote cauldron host',
            error=error
        ).console(
            whitespace=1
        ).response
Example #23
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 #24
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 #25
0
def open_project(
        path: str,
        forget: bool = False,
        results_path: str = None
) -> Response:
    """

    :param path:
    :param forget:
    :param results_path:
    :return:
    """

    response = Response()

    try:
        # Try to close any open projects before opening a new one.
        runner.close()
    except Exception:  # pragma: no cover
        pass

    path = environ.paths.clean(path)

    if not project_exists(response, path):
        return response.fail(
            code='PROJECT_NOT_FOUND',
            message='No project found at: "{}"'.format(path)
        ).console(whitespace=1).response

    if not load_project(response, path):
        return response.fail(
            code='PROJECT_NOT_LOADED',
            message='Unable to load project data'
        ).console(whitespace=1).response

    if not forget and not update_recent_paths(response, path):
        return response.fail(
            code='PROJECT_STATUS_FAILURE',
            message='Unable to update loaded project status'
        ).console(whitespace=1).response

    project = cauldron.project.get_internal_project()
    if results_path:
        project.results_path = results_path

    # 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

    if not initialize_results(response, project):
        return response.fail(
            code='PROJECT_INIT_FAILURE',
            message='Unable to initialize loaded project'
        ).console(whitespace=1).response

    if not write_results(response, project):
        return response.fail(
            code='PROJECT_WRITE_FAILURE',
            message='Unable to write project notebook data'
        ).console(whitespace=1).response

    # Should no longer be needed because the source directory is included
    # in the library directories as of v0.4.7
    # runner.add_library_path(project.source_directory)
    runner.reload_libraries(project.library_directories)

    return response.update(
        project=project.kernel_serialize()
    ).notify(
        kind='SUCCESS',
        code='PROJECT_OPENED',
        message='Opened project: {}'.format(path)
    ).console_header(
        project.title,
        level=2
    ).console(
        """
        PATH: {path}
         URL: {url}
        """.format(path=path, url=project.baked_url),
        whitespace=1
    ).response
Example #26
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
Example #27
0
 def test_load_project_bad_2(self, runner_initialize, *args):
     runner_initialize.side_effect = ValueError()
     response = Response()
     self.assertFalse(opener.load_project(response, INVALID_PATH))
     self.assertTrue(response.errors[0].code == 'PROJECT_INIT_FAILURE')
Example #28
0
    def test_load_project_bad(self, runner_initialize, *args):
        runner_initialize.side_effect = FileNotFoundError()

        response = Response()
        self.assertFalse(opener.load_project(response, INVALID_PATH))
        self.assertTrue(response.errors[0].code == 'PROJECT_NOT_FOUND')
Example #29
0
 def test_not_exists_load(self, *args):
     """ should return False when the project does not exist """
     self.assertFalse(opener.load_project(Response(), INVALID_PATH))
Example #30
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 #31
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 #32
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 #33
0
    def test_not_exists(self):
        """ should return False when the project does not exist """

        self.assertFalse(opener.project_exists(Response(), INVALID_PATH))
Example #34
0
def remove_step(
        response: Response,
        project: 'projects.Project',
        name: str,
        keep_file: bool = False
) -> Response:
    """

    :param response:
    :param project:
    :param name:
    :param keep_file:
    :return:
    """

    step = project.remove_step(name)
    if not step:
        return response.fail(
            code='NO_SUCH_STEP',
            message='Step "{}" not found. Unable to remove.'.format(name)
        ).kernel(
            name=name
        ).console(
            whitespace=1
        ).response

    project.save()
    project.write()

    if not keep_file:
        os.remove(step.source_path)

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

    step_renames = res.returned

    removed_name = 'REMOVED--{}'.format(uuid.uuid4())
    step_renames[name] = dict(
        name=removed_name,
        title=''
    )

    step_changes = [dict(
        name=removed_name,
        filename=step.filename,
        action='removed'
    )]

    return response.update(
        project=project.kernel_serialize(),
        step_changes=step_changes,
        step_renames=step_renames
    ).notify(
        kind='SUCCESS',
        code='STEP_REMOVED',
        message='Removed "{}" step from project'.format(name)
    ).console(
        whitespace=1
    ).response
Example #35
0
def open_project(
        path: str,
        forget: bool = False,
        results_path: str = None
) -> Response:
    """

    :param path:
    :param forget:
    :param results_path:
    :return:
    """

    response = Response()

    try:
        runner.close()
    except Exception:
        pass

    path = environ.paths.clean(path)

    if not project_exists(response, path):
        return response.fail(
            code='PROJECT_NOT_FOUND',
            message='No project found at: "{}"'.format(path)
        ).console(whitespace=1).response

    if not load_project(response, path):
        return response.fail(
            code='PROJECT_NOT_LOADED',
            message='Unable to load project data'
        ).console(whitespace=1).response

    if not forget and not update_recent_paths(response, path):
        return response.fail(
            code='PROJECT_STATUS_FAILURE',
            message='Unable to update loaded project status'
        ).console(whitespace=1).response

    project = cauldron.project.internal_project
    if results_path:
        project.results_path = results_path

    # 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

    if not initialize_results(response, project):
        return response.fail(
            code='PROJECT_INIT_FAILURE',
            message='Unable to initialize loaded project'
        ).console(whitespace=1).response

    if not write_results(response, project):
        return response.fail(
            code='PROJECT_WRITE_FAILURE',
            message='Unable to write project notebook data'
        ).console(whitespace=1).response

    runner.add_library_path(project.source_directory)

    return response.update(
        project=project.kernel_serialize()
    ).notify(
        kind='SUCCESS',
        code='PROJECT_OPENED',
        message='Opened project: {}'.format(path)
    ).console_header(
        project.title,
        level=2
    ).console(
        """
        PATH: {path}
         URL: {url}
        """.format(path=path, url=project.baked_url),
        whitespace=1
    ).response