Skip to content

Commit

Permalink
Add support for passing a token and report_id on command line. (#71)
Browse files Browse the repository at this point in the history
analytics-host now supports two more command line flags:
--token=TOKEN
--web-push-id=REPORT_ID

If --token is specified, it will immediately set the token
for CloudSession instead of prompting for a password.

If --web-push-id is specified then when uploading a report
to iotile cloud, the files will be attached to that existing
report id, rather than creating a new report id.

Closes #70
  • Loading branch information
timburke committed Oct 23, 2018
1 parent ec7f1d3 commit 0a7159f
Show file tree
Hide file tree
Showing 12 changed files with 56 additions and 32 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Expand Up @@ -67,4 +67,5 @@ target/

built_docs
.pytest_cache
notebooks
notebooks
.pytest_cache
5 changes: 5 additions & 0 deletions iotile_analytics_core/RELEASE.md
@@ -1,5 +1,10 @@
# Release Notes

## 0.5.1

- Properly suppress warning if you choose verify=False during testing to disable
certificate verification.

## 0.5.0

- Add incremental envelope calculation functions to handle accumulating a
Expand Down
10 changes: 5 additions & 5 deletions iotile_analytics_core/iotile_analytics/core/session.py
Expand Up @@ -24,12 +24,12 @@
#python2
from urllib2 import urlopen, Request
from urllib import urlencode
import requests.packages.urllib3 as urllib3
import urllib3
except ImportError:
#python3
from urllib.request import urlopen, Request
from urllib.parse import urlencode
import requests.packages.urllib3 as urllib3
import urllib3

from typedargs.exceptions import ArgumentError
from iotile_cloud.api.connection import Api, DOMAIN_NAME
Expand Down Expand Up @@ -107,6 +107,9 @@ def __init__(self, user=None, password=None, domain=DOMAIN_NAME, verify=None, to
else:
self.verify = cache.get('verify', True)

if self.verify is False:
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

if token is not None:
self.token = token
self.token_type = "jwt"
Expand Down Expand Up @@ -149,9 +152,6 @@ def __init__(self, user=None, password=None, domain=DOMAIN_NAME, verify=None, to
prompt_str = prompt_str.encode('utf-8')
password = getpass.getpass(prompt_str)

if self.verify is False:
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

try:
res = self._api.login(email=user, password=password)
except RestHttpBaseException as err:
Expand Down
2 changes: 1 addition & 1 deletion iotile_analytics_core/version.py
@@ -1 +1 @@
version = "0.5.0"
version = "0.5.1"

This file was deleted.

11 changes: 0 additions & 11 deletions iotile_analytics_interactive/.pytest_cache/v/cache/nodeids

This file was deleted.

7 changes: 7 additions & 0 deletions iotile_analytics_interactive/RELEASE.md
@@ -1,5 +1,12 @@
# Release Notes

## 0.5.1

- Add support for passing an iotile.cloud token on the command line using
--token={token}
- Add support for passing a pre-existing report id on the command line using
--web-push-id={id}

## 0.5.0

- Add support for streaming report generation. Now there are FileHandler
Expand Down
Expand Up @@ -12,7 +12,7 @@ class StreamingWebPushHandler(FileHandler):
waits synchronously in the finish() routine until all uploads are done.
"""

def __init__(self, label, slug, domain):
def __init__(self, label, slug, domain, report_id=None):
super(StreamingWebPushHandler, self).__init__()

self._label = label
Expand All @@ -21,15 +21,19 @@ def __init__(self, label, slug, domain):
self._logger = logging.getLogger(__name__)
self._uploader = None
self._last_path = None
self._report_id = None
self._report_id = report_id

def start(self):
"""Perform any necessary actions before the AnalysisTemplate starts."""

self._logger.info("Streaming files to cloud as they are generated")
self._uploader = ReportUploader(self._domain)
self._report_id = self._uploader.create_report(self._label, slug=self._slug)
self._logger.debug("Created report id: %s", self._report_id)

if self._report_id is None:
self._report_id = self._uploader.create_report(self._label, slug=self._slug)
self._logger.debug("Created report id: %s", self._report_id)
else:
self._logger.debug("Using existing report id: %s", self._report_id)

def finish(self, paths):
"""Perform any necessary actions after the AnalysisTemplate has finished.
Expand Down
Expand Up @@ -11,15 +11,16 @@ class WebPushHandler(LocalDiskHandler):
but it will also be pushed to the cloud in the finish() method.
"""

def __init__(self, label, slug, domain):
def __init__(self, label, slug, domain, report_id=None):
super(WebPushHandler, self).__init__()

self._label = label
self._slug = slug
self._domain = domain
self._report_id = report_id

def finish(self, paths):
"""Push this entire report up to the cloud."""

uploader = ReportUploader(self._domain)
uploader.upload_report(self._label, paths, slug=self._slug)
uploader.upload_report(self._label, paths, slug=self._slug, report_id=self._report_id)
Expand Up @@ -116,8 +116,10 @@ def build_args():
parser.add_argument('-b', '--bundle', action="store_true", help="Bundle the rendered output into a zip file")
parser.add_argument('-w', '--web-push', action="store_true", help="Push the resulting report to iotile.cloud.")
parser.add_argument('--unattended', action="store_true", help="Hint that we are running as an unattended script to not print dynamic progress bars")
parser.add_argument('--web-push-id', type=str, default=None, help="Use this report id when uploading rather than creating a new report record")
parser.add_argument('--web-push-label', type=str, default=None, help="Set the label used when pushing a report to iotile.cloud (otherwise you are prompted for it)")
parser.add_argument('--web-push-slug', type=str, default=None, help="Override the source slug given in the analysisgroup and force it to be this")
parser.add_argument('--token', type=str, default=None, help="Token for authentication to iotile cloud (instead of a password)")
parser.add_argument('-d', '--domain', default=DOMAIN_NAME, help="Domain to use for remote queries, defaults to https://iotile.cloud")
parser.add_argument('analysis_group', default=None, nargs='?', help="The slug or path of the object you want to perform analysis on")

Expand Down Expand Up @@ -247,7 +249,7 @@ def find_analysis_group(args):
sys.exit(1)

if is_cloud:
CloudSession(user=args.user, password=args.password, domain=args.domain, verify=not args.no_verify)
CloudSession(user=args.user, password=args.password, token=args.token, domain=args.domain, verify=not args.no_verify)

group_obj = generator(group)

Expand Down Expand Up @@ -315,7 +317,7 @@ def check_output_settings(report_class, output_path, bundle, web_push):
raise UsageError("The chosen AnalysisTemplate produces more than one file, you must specify an output path using -o if you are not pushing diretly to the cloud")


def build_file_handler(output_path, standalone, bundle, web_push, label, group, slug, domain):
def build_file_handler(output_path, standalone, bundle, web_push, label, group, slug, domain, report_id=None):
"""Build the appropriate file handler for the AnalysisTemplate's output."""

if output_path is None and web_push is False:
Expand All @@ -342,9 +344,9 @@ def build_file_handler(output_path, standalone, bundle, web_push, label, group,
raise ArgumentError("The group provided did not have source slug information in its source_info", source_info=group.source_info)

if output_path is None:
return None, StreamingWebPushHandler(label, slug, domain)
return None, StreamingWebPushHandler(label, slug, domain, report_id=report_id)

return output_path, WebPushHandler(label, slug, domain)
return output_path, WebPushHandler(label, slug, domain, report_id=report_id)


def split_args(args):
Expand Down Expand Up @@ -429,9 +431,11 @@ def main(argv=None):
# Make sure we create a cloud session now to capture the user's password
# if they gave us a username and we haven't already logged in
if not logged_in and args.user is not None and args.web_push:
CloudSession(user=args.user, password=args.password, domain=args.domain, verify=not args.no_verify)
CloudSession(user=args.user, password=args.password, token=args.token, domain=args.domain, verify=not args.no_verify)

output_path, handler = build_file_handler(args.output, report_obj.standalone, args.bundle, args.web_push, label=args.web_push_label, group=group, slug=args.web_push_slug, domain=args.domain)
output_path, handler = build_file_handler(args.output, report_obj.standalone, args.bundle, args.web_push,
label=args.web_push_label, group=group, slug=args.web_push_slug,
domain=args.domain, report_id=args.web_push_id)

handler.start()
rendered_paths = perform_analysis(report_obj, group, output_path, handler=handler.handle_file, args=report_args, domain=args.domain)
Expand Down
14 changes: 14 additions & 0 deletions iotile_analytics_interactive/test/test_analytics_host.py
Expand Up @@ -140,6 +140,20 @@ def test_user_pass_handling(water_meter, capsys):
assert out == DEVICE_DATA


def test_user_token_handling(water_meter, capsys):
"""Make sure we can pass username and token on the cmdline."""

domain, _cloud = water_meter
slug = 'd--0000-0000-0000-00d2'

retval = main(['-t', 'basic_info', '-u', 'test@arch-iot.com', '--token', 'JWT_USER', slug, '-d', domain, '--no-verify', '-c'])
assert retval == 0

out, _err = capsys.readouterr()

assert out == DEVICE_DATA


def test_local_files(water_meter, tmpdir):
"""Make sure we can run a report from a local file."""

Expand Down
2 changes: 1 addition & 1 deletion iotile_analytics_interactive/version.py
@@ -1 +1 @@
version = "0.5.0"
version = "0.5.1"

0 comments on commit 0a7159f

Please sign in to comment.