Example #1
0
def cli(ctx, apiroot, config, organization):
    """A command line tool for working with Transcriptic."""
    if ctx.invoked_subcommand in [
            'login', 'compile', 'preview', 'summarize', 'init'
    ]:
        # For login/local commands, initialize empty connection
        ctx.obj = ContextObject()
        ctx.obj.api = Connection(use_environ=False)
    else:
        try:
            ctx.obj = ContextObject()
            ctx.obj.api = Connection.from_file(config)
            if organization is not None:
                ctx.obj.api.organization_id = organization
            if apiroot is not None:
                ctx.obj.api.api_root = apiroot
        except:
            click.echo(
                "Welcome to TxPy! It seems like your `.transcriptic` config file is missing or out of date"
            )
            analytics = click.confirm(
                "Send TxPy CLI usage information to improve the CLI user "
                "experience?",
                default=True)
            ctx.obj.api = Connection(
                use_environ=False)  # Initialize empty connection
            ctx.invoke(login, analytics=analytics)
    if ctx.obj.api.analytics:
        try:
            ctx.obj.api._post_analytics(event_action=ctx.invoked_subcommand,
                                        event_category="cli")
        except:
            pass
Example #2
0
    def format_commands(self, ctx, formatter):
        """Custom formatter to control whether a command is displayed
        Note: This is only called when formatting the help message.
        """
        ctx.obj = ContextObject()
        try:
            ctx.obj.api = Connection.from_file('~/.transcriptic')
        except (FileNotFoundError, OSError):
            # This defaults to feature_groups = []
            ctx.obj.api = Connection()

        rows = []
        for subcommand in self.list_commands(ctx):
            cmd = self.get_command(ctx, subcommand)
            if cmd is None:
                continue
            try:
                if cmd.feature is not None and \
                        cmd.feature in ctx.obj.api.feature_groups:
                    help = cmd.short_help or ''
                    rows.append((subcommand, help))
                else:
                    continue
            except AttributeError:
                help = cmd.short_help or ''
                rows.append((subcommand, help))

        if rows:
            with formatter.section('Commands'):
                formatter.write_dl(rows)
Example #3
0
def cli(ctx, apiroot, config, organization):
    """A command line tool for working with Transcriptic."""
    if ctx.invoked_subcommand in ['login', 'compile', 'preview', 'summarize', 'init']:
        # For login/local commands, initialize empty connection
        ctx.obj = ContextObject()
        ctx.obj.api = Connection(use_environ=False)
    else:
        try:
            ctx.obj = ContextObject()
            ctx.obj.api = Connection.from_file(config)
            if organization is not None:
                ctx.obj.api.organization_id = organization
            if apiroot is not None:
                ctx.obj.api.api_root = apiroot
        except:
            click.echo("Welcome to TxPy! It seems like your `.transcriptic` config file is missing or out of date")
            analytics = click.confirm("Send TxPy CLI usage information to improve the CLI user "
                                      "experience?", default=True)
            ctx.obj.api = Connection(use_environ=False)  # Initialize empty connection
            ctx.invoke(login, analytics=analytics)
    if ctx.obj.api.analytics:
        try:
            ctx.obj.api._post_analytics(event_action=ctx.invoked_subcommand, event_category="cli")
        except:
            pass
Example #4
0
def summarize(ctx, file, tree, lookup, runtime):
    """Summarize Autoprotocol as a list of plain English steps, as well as a
    visualized Job Tree contingent upon desired runtime allowance (in seconds).
    A Job Tree refers to a structure of protocol based on container dependency,
    where each node, and its corresponding number, represents an instruction of
    the protocol. More specifically, the tree structure contains process branches,
    in which the x-axis refers to the dependency depth in a given branch, while
    the y-axis refers to the traversal of branches themselves.

    Example usage is as follows:

    python my_script.py | transcriptic summarize --tree

    python my_script.py | transcriptic summarize --tree --runtime 20
    """
    with click.open_file(file, 'r') as f:
        try:
            protocol = json.loads(f.read())
        except ValueError:
            click.echo(
                "The autoprotocol you're trying to summarize is invalid.")
            return

    if lookup:
        try:
            config = '~/.transcriptic'
            ctx.obj = ContextObject()
            ctx.obj.api = Connection.from_file(config)
            parser = AutoprotocolParser(protocol, ctx=ctx)
        except:
            click.echo(
                "Connection with Transcriptic failed. "
                "Summarizing without lookup.",
                err=True)
            parser = AutoprotocolParser(protocol)
    else:
        parser = AutoprotocolParser(protocol)

    if tree:
        import multiprocessing

        print("\nGenerating Job Tree...")
        p = multiprocessing.Process(target=parser.job_tree)
        p.start()

        # Wait for <runtime_allowance> seconds or until process finishes
        p.join(runtime)

        # If thread is still active
        if p.is_alive():
            print("Still running... Aborting tree construction.")
            print(
                "Please allow for more runtime allowance, or opt for no tree construction.\n"
            )

            # Terminate
            p.terminate()
            p.join()
        else:
            print("\nYour Job Tree is complete!\n")
Example #5
0
def summarize_cmd(ctx, file, html, tree, lookup, runtime):
    """Summarize Autoprotocol as a list of plain English steps, as well as a
    visualized Job Tree contingent upon desired runtime allowance (in seconds).
    A Job Tree refers to a structure of protocol based on container dependency,
    where each node, and its corresponding number, represents an instruction of
    the protocol. More specifically, the tree structure contains process branches,
    in which the x-axis refers to the dependency depth in a given branch, while
    the y-axis refers to the traversal of branches themselves.

    Example usage is as follows:

    python my_script.py | transcriptic summarize --tree

    python my_script.py | transcriptic summarize --tree --runtime 20

    python my_script.py | transcriptic summarize --html
    """
    if lookup or html:
        try:
            config      = '~/.transcriptic'
            ctx.obj     = ContextObject()
            ctx.obj.api = Connection.from_file(config)
        except:
            click.echo("Connection with Transcriptic failed. "
                       "Summarizing without lookup.", err=True)

    api = ctx.obj.api
    commands.summarize(api, file, html, tree, lookup, runtime)
Example #6
0
def test_api(monkeypatch):
    from transcriptic.config import Connection
    from .helpers.mockAPI import _req_call as mockCall
    api = Connection(email="*****@*****.**",
                     organization_id="mock",
                     api_root="mock-api")
    monkeypatch.setattr(api, '_req_call', mockCall)
    return api
Example #7
0
def connect(transcriptic_path="~/.transcriptic"):
    #TODO: Mirror login code from CLI
    try:
        api = Connection.from_file(transcriptic_path)
    except:
        print(
            "Unable to find .transcriptic file, please ensure the right path is provided"
        )
Example #8
0
def summarize(ctx, file, tree, lookup, runtime):
    """Summarize Autoprotocol as a list of plain English steps, as well as a
    visualized Job Tree contingent upon desired runtime allowance (in seconds).
    A Job Tree refers to a structure of protocol based on container dependency,
    where each node, and its corresponding number, represents an instruction of
    the protocol. More specifically, the tree structure contains process branches,
    in which the x-axis refers to the dependency depth in a given branch, while
    the y-axis refers to the traversal of branches themselves.

    Example usage is as follows:

    python my_script.py | transcriptic summarize --tree

    python my_script.py | transcriptic summarize --tree --runtime 20
    """
    with click.open_file(file, 'r') as f:
        try:
            protocol = json.loads(f.read())
        except ValueError:
            click.echo(
                "The autoprotocol you're trying to summarize is invalid.")
            return

    if lookup:
        try:
            config = '~/.transcriptic'
            ctx.obj = ContextObject()
            ctx.obj.api = Connection.from_file(config)
            parser = AutoprotocolParser(protocol, ctx=ctx)
        except:
            click.echo("Connection with Transcriptic failed. "
                       "Summarizing without lookup.", err=True)
            parser = AutoprotocolParser(protocol)
    else:
        parser = AutoprotocolParser(protocol)

    if tree:
        import multiprocessing

        print("\nGenerating Job Tree...")
        p = multiprocessing.Process(target=parser.job_tree)
        p.start()

        # Wait for <runtime_allowance> seconds or until process finishes
        p.join(runtime)

        # If thread is still active
        if p.is_alive():
            print("Still running... Aborting tree construction.")
            print(
                "Please allow for more runtime allowance, or opt for no tree construction.\n")

            # Terminate
            p.terminate()
            p.join()
        else:
            print("\nYour Job Tree is complete!\n")
Example #9
0
def cli(ctx, apiroot, config, organization):
  '''A command line tool for working with Transcriptic.'''
  if ctx.invoked_subcommand not in ['login', 'preview', 'run']:
    try:
      ctx.obj = Connection.from_file(config)
      if organization is not None:
        ctx.obj.organization_id = organization
      if apiroot is not None:
        ctx.obj.api_root = apiroot
    except IOError:
      click.echo("Error reading config file, running "
                 "`transcriptic login` ...")
      ctx.invoke(login)
Example #10
0
def cli(ctx, apiroot, config, organization):
    '''A command line tool for working with Transcriptic.'''
    if ctx.invoked_subcommand not in ['login', 'preview', 'run']:
        try:
            ctx.obj = Connection.from_file(config)
            if organization is not None:
                ctx.obj.organization_id = organization
            if apiroot is not None:
                ctx.obj.api_root = apiroot
        except IOError:
            click.echo("Error reading config file, running "
                       "`transcriptic login` ...")
            ctx.invoke(login)
Example #11
0
def protocol_response(method, protocol_path=None, response_path=None, **kwargs):
    """
    Helper function for getting protocol response and dumping json to response path
    Caveat Emptor: Does not do any additional checks on the response object, just dumps to json if possible
    """
    from transcriptic import api
    if not api:
        from transcriptic.config import Connection
        api = Connection.from_file("~/.transcriptic")
    protocol = json.loads(open(protocol_path).read())

    response = requests.post(api.get_route(method), headers=api.headers, data=json.dumps({'protocol': protocol}))
    with open(response_path, 'w') as out_file:
        json.dump(response.json(), out_file, indent=2)
Example #12
0
def login(ctx, api_root):
    '''Authenticate to your Transcriptic account.'''
    email = click.prompt('Email')
    password = click.prompt('Password', hide_input=True)
    r = requests.post("%s/users/sign_in" % api_root,
                      data=json.dumps({
                          'user': {
                              'email': email,
                              'password': password,
                          },
                      }),
                      headers={
                          'Accept': 'application/json',
                          'Content-Type': 'application/json',
                      })
    if r.status_code != 200:
        click.echo("Error logging into Transcriptic: %s" % r.json()['error'])
        sys.exit(1)
    user = r.json()
    token = (user.get('authentication_token')
             or user['test_mode_authentication_token'])
    if len(user['organizations']) < 1:
        click.echo(
            "Error: You don't appear to belong to any organizations. \nVisit %s "
            "and create an organization." % api_root)
        sys.exit(1)
    if len(user['organizations']) == 1:
        organization = user['organizations'][0]['subdomain']
    else:
        click.echo("You belong to %s organizations:" %
                   len(user['organizations']))
        for o in user['organizations']:
            click.echo("  %s (%s)" % (o['name'], o['subdomain']))
        organization = click.prompt(
            'Which would you like to login as',
            default=user['organizations'][0]['subdomain'],
            prompt_suffix='? ')
    r = requests.get('%s/%s' % (api_root, organization),
                     headers={
                         'X-User-Email': email,
                         'X-User-Token': token,
                         'Accept': 'application/json',
                     })
    if r.status_code != 200:
        click.echo("Error accessing organization: %s" % r.text)
        sys.exit(1)
    ctx.obj = Connection(email, token, organization, api_root=api_root)
    ctx.obj.save(ctx.parent.params['config'])
    click.echo('Logged in as %s (%s)' % (user['email'], organization))
Example #13
0
def protocol_response(method,
                      protocol_path=None,
                      response_path=None,
                      **kwargs):
    """
    Helper function for getting protocol response and dumping json to response path
    Caveat Emptor: Does not do any additional checks on the response object, just dumps to json if possible
    """
    from transcriptic import api
    if not api:
        from transcriptic.config import Connection
        api = Connection.from_file("~/.transcriptic")
    protocol = json.loads(open(protocol_path).read())

    response = requests.post(api.get_route(method),
                             headers=api.session.headers,
                             data=json.dumps({'protocol': protocol}))
    with open(response_path, 'w') as out_file:
        json.dump(response.json(), out_file, indent=2)
Example #14
0
def connect(transcriptic_path="~/.transcriptic"):
    #TODO: Mirror login code from CLI
    try:
        api = Connection.from_file(transcriptic_path)
    except:
        print ("Unable to find .transcriptic file, please ensure the right path is provided")
Example #15
0
def login(ctx, api_root, analytics=True):
    """Authenticate to your Transcriptic account."""
    email = click.prompt('Email')
    password = click.prompt('Password', hide_input=True)
    r = ctx.obj.api.post(routes.login(api_root=api_root),
                         data=json.dumps({
                             'user': {
                                 'email': email,
                                 'password': password,
                             },
                         }),
                         headers={
                             'Accept': 'application/json',
                             'Content-Type': 'application/json',
                         },
                         status_response={
                             '200': lambda resp: resp,
                             'default': lambda resp: resp
                         },
                         custom_request=False)
    if r.status_code != 200:
        click.echo("Error logging into Transcriptic: %s" % r.json()['error'])
        sys.exit(1)
    user = r.json()
    token = (user.get('authentication_token')
             or user['test_mode_authentication_token'])
    user_id = user.get("id")
    if len(user['organizations']) < 1:
        click.echo("Error: You don't appear to belong to any organizations. \n"
                   "Visit %s and create an organization." % api_root)
        sys.exit(1)
    if len(user['organizations']) == 1:
        organization = user['organizations'][0]['subdomain']
    else:
        click.echo("You belong to %s organizations:" %
                   len(user['organizations']))
        for indx, o in enumerate(user['organizations']):
            click.echo("%s.  %s (%s)" % (indx + 1, o['name'], o['subdomain']))

        def parse_valid_org(indx):
            from click.exceptions import BadParameter
            try:
                return user['organizations'][int(indx) - 1]['subdomain']
            except:
                raise BadParameter("Please enter an integer between 1 and %s" %
                                   (len(user['organizations'])),
                                   ctx=ctx)

        organization = click.prompt(
            'Which organization would you like to log in as',
            default=1,
            prompt_suffix='? ',
            type=int,
            value_proc=lambda x: parse_valid_org(x))

    r = ctx.obj.api.get(routes.get_organization(api_root=api_root,
                                                org_id=organization),
                        headers={
                            'X-User-Email': email,
                            'X-User-Token': token,
                            'Accept': 'application/json',
                        },
                        status_response={
                            '200': lambda resp: resp,
                            'default': lambda resp: resp
                        },
                        custom_request=True)
    if r.status_code != 200:
        click.echo("Error accessing organization: %s" % r.text)
        sys.exit(1)
    ctx.obj.api = Connection(email=email,
                             token=token,
                             organization_id=organization,
                             api_root=api_root,
                             user_id=user_id,
                             analytics=analytics)
    ctx.obj.api.save(ctx.parent.params['config'])
    click.echo('Logged in as %s (%s)' % (user['email'], organization))
Example #16
0
def cli(ctx, api_root, email, token, organization, config):
    """A command line tool for working with Transcriptic.

    Note: This is the main entry point of the CLI. If specifying credentials,
    note that the order of preference is: --flag, environment then config file.

    Example: `transcriptic --organization "my_org" projects` >>
    `export USER_ORGANIZATION="my_org"` >> `"organization_id": "my_org" in
     ~/.transcriptic
    """
    # Initialize ContextObject to be used for storing api object
    ctx.obj = ContextObject()

    if ctx.invoked_subcommand in ['compile', 'preview', 'summarize', 'init']:
        # For local commands, initialize empty connection
        ctx.obj.api = Connection()
    elif ctx.invoked_subcommand == 'login':
        # Load analytics option from existing dotfile if present, else prompt
        try:
            api = Connection.from_file(config)
            api.api_root = (
                api_root or os.environ.get('BASE_URL', None) or api.api_root
            )
            ctx.obj.api = api
        except (OSError, IOError):
            ctx.obj.api = Connection()
        # Echo a warning if other options are defined for login
        if organization or email or token:
            click.echo("Only the `--api-root` option is applicable for the "
                       "`login` command. All other options are ignored.")
    else:
        try:
            api = Connection.from_file(config)
            api.api_root = (
                api_root or os.environ.get('BASE_URL', None) or api.api_root
            )
            api.organization_id = (
                organization or os.environ.get('USER_ORGANIZATION', None) or
                api.organization_id
            )
            api.email = (
                email or os.environ.get('USER_EMAIL', None) or api.email
            )
            api.token = (
                token or os.environ.get('USER_TOKEN', None) or api.token
            )
            ctx.obj.api = api
        except (OSError, IOError):
            click.echo("Welcome to TxPy! It seems like your `.transcriptic` "
                       "config file is missing or out of date")
            analytics = click.confirm("Send TxPy CLI usage information to "
                                      "improve the CLI user "
                                      "experience?", default=True)
            ctx.obj.api = Connection()  # Initialize empty connection
            ctx.invoke(login_cmd, api_root=api_root, analytics=analytics)
    if ctx.obj.api.analytics:
        try:
            ctx.obj.api._post_analytics(event_action=ctx.invoked_subcommand,
                                        event_category="cli")
        except requests.exceptions.RequestException:
            pass
Example #17
0
def login(api, config, api_root=None, analytics=True):
    """Authenticate to your Transcriptic account."""
    if api_root is None:
        # Always default to the pre-defined api-root if possible, else use
        # the secure.transcriptic.com domain
        try:
            api_root = api.api_root
        except ValueError:
            api_root = "https://secure.transcriptic.com"

    email = click.prompt('Email')
    password = click.prompt('Password', hide_input=True)
    try:
        r = api.post(
            routes.login(api_root=api_root),
            data=json.dumps({
                'user': {
                    'email': email,
                    'password': password,
                },
            }),
            headers={
                'Accept': 'application/json',
                'Content-Type': 'application/json',
            },
            status_response={
                '200': lambda resp: resp,
                '401': lambda resp: resp,
                'default': lambda resp: resp
            }
        )

    except requests.exceptions.RequestException:
        click.echo("Error logging into specified host: {}. Please check your "
                   "internet connection and host name".format(api_root))
        sys.exit(1)

    if r.status_code != 200:
        click.echo("Error logging into Transcriptic: %s" % r.json()['error'])
        sys.exit(1)
    user = r.json()
    token = (user.get('authentication_token') or
             user['test_mode_authentication_token'])
    user_id = user.get("id")
    feature_groups = user.get('feature_groups')
    organization = org_prompt(user['organizations'])

    r = api.get(
        routes.get_organization(api_root=api_root, org_id=organization),
        headers={
            'X-User-Email': email,
            'X-User-Token': token,
            'Accept': 'application/json'},
        status_response={
            '200': lambda resp: resp,
            'default': lambda resp: resp}
    )

    if r.status_code != 200:
        click.echo("Error accessing organization: %s" % r.text)
        sys.exit(1)
    api = Connection(email=email, token=token,
                     organization_id=organization, api_root=api_root,
                     user_id=user_id, analytics=analytics,
                     feature_groups=feature_groups)
    api.save(config)
    click.echo('Logged in as %s (%s)' % (user['email'], organization))
 def _req_call(method, route, **kwargs):
     
     if 'verify' not in kwargs:
         kwargs['verify'] = False
         
     return Connection._req_call(method, route, **kwargs)