def echo_known_projects(response: Response) -> dict: """ :return: """ def print_path_group(header, paths): if not paths: return environ.log_header(header, level=6, indent_by=2) entries = [] for p in paths: parts = p.rstrip(os.sep).split(os.sep) name = parts[-1] if name.startswith('@'): name = name.split(':', 1)[-1] entries.append( '* "{name}" -> {path}'.format(name=name, path=p) ) environ.log('\n'.join(entries), indent_by=4) def project_paths_at(root_path): glob_path = os.path.join(root_path, '**', 'cauldron.json') return [ os.path.dirname(x)[(len(root_path) + 1):] for x in glob.iglob(glob_path, recursive=True) ] environ.log_header('Existing Projects') print_path_group( 'Recently Opened', environ.configs.fetch('recent_paths', []) ) environ.configs.load() aliases = dict( home={ 'path': environ.paths.clean(os.path.join('~', 'cauldron')) }, examples={ 'path': environ.paths.package('resources', 'examples') } ) aliases.update(environ.configs.fetch('folder_aliases', {})) aliases = [(k, p) for k, p in aliases.items()] aliases.sort(key=lambda x: x[0]) for key, data in aliases: print_path_group( key.capitalize(), [ '@{}:{}'.format(key, x) for x in project_paths_at(data['path']) ] ) environ.log_blanks()
def show_help(command_name: str = None, raw_args: str = '') -> Response: """ Prints the basic command help to the console """ response = Response() cmds = fetch() if command_name and command_name in cmds: parser, result = parse.get_parser(cmds[command_name], parse.explode_line(raw_args), dict()) if parser is not None: out = parser.format_help() return response.notify( kind='INFO', code='COMMAND_DESCRIPTION').kernel(commands=out).console( out, whitespace=1).response environ.log_header('Available Commands') response.consume(print_module_help()) return response.fail(code='NO_SUCH_COMMAND', message='Failed to show command help for "{}"'.format( command_name)).console( """ For more information on the various commands, enter help on the specific command: help [COMMAND] """, whitespace_bottom=1).response
def execute( context: cli.CommandContext, force: bool = False, all_projects: bool = False ) -> Response: """ :param context: :param force: :param all_projects: """ response = context.response environ.log_header('REMOVE RESULTS', level=2) environ.log( ALL_MESSAGE if all_projects else PROJECT_MESSAGE, whitespace=1 ) do_it = force if not force: do_it = query.confirm( 'Are you sure you want to continue', default=False ) if not do_it: return response.notify( kind='ABORTED', code='NO_PURGE', message='No files were deleted' ).console( whitespace=1 ).response if context.remote_connection.active: return remote_purge(context) path = environ.configs.fetch('results_directory') path = path if path else environ.paths.user('results') if environ.systems.remove(path): response.notify( kind='SUCCESS', code='RESULTS_PURGED', message='All results have been removed' ).console( whitespace=1 ) else: response.fail( code='PURGE_FAILURE', message='Failed to remove results' ).console( whitespace=1 ) return response
def print_path_group(header, paths): if not paths: return environ.log_header(header, level=6, indent_by=2) entries = [] for p in paths: parts = p.rstrip(os.sep).split(os.sep) name = parts[-1] if name.startswith('@'): name = name.split(':', 1)[-1] entries.append('* "{name}" -> {path}'.format(name=name, path=p)) environ.log('\n'.join(entries), indent_by=4)
def print_path_group(header, paths): if not paths: return environ.log_header(header, level=6, indent_by=2) entries = [] for p in paths: parts = p.rstrip(os.sep).split(os.sep) name = parts[-1] if name.startswith('@'): name = name.split(':', 1)[-1] entries.append( '* "{name}" -> {path}'.format(name=name, path=p) ) environ.log('\n'.join(entries), indent_by=4)
def echo_known_projects(response: Response) -> dict: """ :return: """ def print_path_group(header, paths): if not paths: return environ.log_header(header, level=6, indent_by=2) entries = [] for p in paths: parts = p.rstrip(os.sep).split(os.sep) name = parts[-1] if name.startswith('@'): name = name.split(':', 1)[-1] entries.append('* "{name}" -> {path}'.format(name=name, path=p)) environ.log('\n'.join(entries), indent_by=4) def project_paths_at(root_path): glob_path = os.path.join(root_path, '**', 'cauldron.json') return [ os.path.dirname(x)[(len(root_path) + 1):] for x in glob.iglob(glob_path, recursive=True) ] environ.log_header('Existing Projects') print_path_group('Recently Opened', environ.configs.fetch('recent_paths', [])) environ.configs.load() aliases = dict( home={'path': environ.paths.clean(os.path.join('~', 'cauldron'))}, examples={'path': environ.paths.package('resources', 'examples')}) aliases.update(environ.configs.fetch('folder_aliases', {})) aliases = [(k, p) for k, p in aliases.items()] aliases.sort(key=lambda x: x[0]) for key, data in aliases: print_path_group( key.capitalize(), ['@{}:{}'.format(key, x) for x in project_paths_at(data['path'])]) environ.log_blanks()
def list_snapshots(project: Project): """ :param project: :return: """ snapshots = get_snapshot_listing(project) if not snapshots: environ.log('No snapshots found') return None entries = [] for item in snapshots: entries.append('* {}'.format(item['name'])) environ.log_header('EXISTING SNAPSHOTS') environ.log(entries, whitespace_bottom=1, indent_by=3)
def on_progress(message: ResponseMessage): if message.kind == 'SKIP': return if len(synchronized) < 1: environ.log_header( text='SYNCHRONIZING', level=2, whitespace=1 ) if message.code == 'STARTED': synchronized.append(message) chunk_count = message.data.get('chunk_count', 0) if message.code == 'DONE' and chunk_count < 2: return message.console()
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)
def execute( context: cli.CommandContext, action: str, arguments: list, no_show: bool = False ) -> Response: """ :param context: :param action: :param arguments: :param no_show: :return: """ show = not no_show if not action: return context.response.fail( code='NO_ACTION_ARG', message='An action is required for the snapshot command' ).console( whitespace=1 ).response action = action.strip().lower() project = cauldron.project.internal_project if not project: return context.response.fail( code='NO_OPEN_PROJECT', message='No open project' ).console( '[ERROR]: No open project. Use the "open" command to open one.' ) if action == 'remove': return actions.remove_snapshot(project, *arguments) if action == 'add': return actions.create_snapshot(project, *arguments, show=show) if action == 'list': return actions.list_snapshots(project) if action == 'open': name = arguments[0] result = actions.open_snapshot(project, name) if result is None: environ.log('[ERROR]: No snapshot found named "{}"'.format(name)) return context.response environ.log_header('SNAPSHOT: {}'.format(name)) environ.log( """ URL: {url} LAST MODIFIED: {modified} """.format( url=result['url'], modified=datetime.fromtimestamp( result['last_modified'] ).strftime('%H:%M %b %d, %Y') ), whitespace=1 ) if show: webbrowser.open(result['url'])
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
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
def execute( context: cli.CommandContext, command: str = None, name: str = None, path: str = None, temporary: bool = False ): """ :return: """ response = context.response if name: name = name.replace(' ', '_').strip('"').strip() if path: path = environ.paths.clean(path.strip('"')) if not os.path.isdir(path): path = os.path.dirname(path) environ.log( """ [WARNING]: The specified path was not a directory. Aliases must be directories, so the directory containing the specified file will be used instead: {} """.format(path), whitespace=1 ) environ.configs.load() temporary_aliases = environ.configs.session.get('folder_aliases', {}) persistent_aliases = environ.configs.persistent.get('folder_aliases', {}) if not name and command in ['add', 'remove']: return response.fail( code='MISSING_ARG', message='You need to specify the name of the alias' ).console( whitespace=1 ).response if command == 'list': items = [] aliases = dict( list(temporary_aliases.items()) + list(persistent_aliases.items()) ) for k, v in aliases.items(): items.append('{}\n {}'.format(k, v['path'])) environ.log_header('EXISTING ALIASES') environ.log(items) return response aliases = temporary_aliases if temporary else persistent_aliases if command == 'add': aliases[name] = dict( path=path ) environ.configs.put( persists=not bool(temporary), folder_aliases=aliases ) return response.notify( kind='ADDED', code='ALIAS_ADDED', message='The alias "{}" has been saved'.format(name) ).console( whitespace=1 ).response if command == 'remove': if name in aliases: del aliases[name] environ.configs.put( persists=not bool(temporary), folder_aliases=aliases ) return response.notify( kind='REMOVED', code='ALIAS_REMOVED', message='The alias "{}" has been removed'.format(name) ).console( whitespace=1 ).response return response.fail( code='UNKNOWN_COMMAND', message='Unrecognized alias command "{}"'.format(command) ).console( whitespace=1 ).response