From 8eb846254185801282630ddc53f0d3f5264ac631 Mon Sep 17 00:00:00 2001 From: Antony Harfield Date: Mon, 13 Jan 2020 08:54:08 +0700 Subject: [PATCH] Update docs and version to 0.0.5 --- README.md | 7 +- docs/client.html | 274 ++++++++++++++++++++++++++++++++++++++++++----- setup.py | 2 +- 3 files changed, 252 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 8886efc..d4eb272 100644 --- a/README.md +++ b/README.md @@ -34,10 +34,13 @@ First time only: Increment the version in `setup.py`. -To create a source distribution: +To create a distribution (source and wheel): -`python setup.py sdist` +`python setup.py sdist bdist_wheel` +Alternatively through docker: + +`docker run -it --rm -v "$PWD":/usr/src/app rfcx-sdk-python python setup.py sdist bdist_wheel` ### Uploading diff --git a/docs/client.html b/docs/client.html index b4dfffe..d2dfecb 100644 --- a/docs/client.html +++ b/docs/client.html @@ -26,6 +26,7 @@

Module rfcx.client

import getpass
 import datetime
+from os import path
 import rfcx._pkce as pkce
 import rfcx._api_rfcx as api_rfcx
 import rfcx._api_auth as api_auth
@@ -38,8 +39,9 @@ 

Module rfcx.client

self.credentials = None self.default_site = None self.accessible_sites = None + self.persisted_credentials_path = '.rfcx_credentials' - def authenticate(self): + def authenticate(self, persist=True): """Authenticate an RFCx user to obtain a token Returns: @@ -50,6 +52,18 @@

Module rfcx.client

print('Already authenticated') return + access_token = None + + # Attempt to load the credentials from disk + if path.exists(self.persisted_credentials_path): + with open(self.persisted_credentials_path, 'r') as f: + lines = f.read().splitlines() + if len(lines) == 5 and lines[0] == 'version 1': + token_expiry = datetime.datetime.strptime(lines[3], "%Y-%m-%dT%H:%M:%S.%fZ") + self._setup_credentials(lines[1], lines[2], token_expiry, lines[4]) + print('Using persisted authenticatation') + return + # Create a Code Verifier & Challenge code_verifier = pkce.code_verifier() code_challenge = pkce.code_challenge(code_verifier) @@ -66,23 +80,68 @@

Module rfcx.client

# Perform the exchange access_token, refresh_token, token_expiry, id_token = api_auth.authcode_exchange(code.strip(), code_verifier, client_id, scope) + self._setup_credentials(access_token, refresh_token, token_expiry, id_token) + + print('Successfully authenticated') + print('Default site:', self.default_site) + print('Accessible sites:', self.accessible_sites) + + # Write token to disk + if persist: + with open(self.persisted_credentials_path, 'w') as f: + f.write('version 1\n') + f.write(access_token + '\n' + (refresh_token if refresh_token != None else '') + '\n' + token_expiry.isoformat() + 'Z\n' + id_token + '\n') - # Store the result in credentials + def _setup_credentials(self, access_token, token_expiry, refresh_token, id_token): self.credentials = Credentials(access_token, token_expiry, refresh_token, id_token) - print('Successfully authenticated') app_meta = self.credentials.id_object['https://rfcx.org/app_metadata'] if app_meta: self.accessible_sites = app_meta['accessibleSites'] self.default_site = app_meta['defaultSite'] - print('Default site:', self.default_site) - print('Accessible sites:', self.accessible_sites) + def guardians(self, sites=None): + """Retrieve a list of guardians from a site (TO BE DEPRECATED - use streams in future) + + Args: + sites: List of site shortnames (e.g. cerroblanco). Default (None) gets all your accessible sites. + + Returns: + List of guardians""" + + if sites == None: + sites = self.accessible_sites + + return api_rfcx.guardians(self.credentials.id_token, sites) + + def guardianAudio(self, guardianId=None, start=None, end=None, limit=50, descending=True): + """Retrieve audio information about a specific guardian (TO BE DEPRECATED - use streams in future) + + Args: + guardianId: (Required) The guid of a guardian + start: Minimum timestamp of the audio. If None then defaults to exactly 30 days ago. + end: Maximum timestamp of the audio. If None then defaults to now. + limit: Maximum results to return. Defaults to 50. (TODO check if there is an upper limit on the API) + descending: Order by newest results first. Defaults to True. + + Returns: + List of audio files (meta data showing audio id and recorded timestamp) + """ + if self.credentials == None: + print('Not authenticated') + return + + if start == None: + start = (datetime.datetime.utcnow() - datetime.timedelta(days=30)).replace(microsecond=0).isoformat() + 'Z' + if end == None: + end = datetime.datetime.utcnow().replace(microsecond=0).isoformat() + 'Z' + + return api_rfcx.guardianAudio(self.credentials.id_token, guardianId, start, end, limit, descending) def tags(self, type, labels=None, start=None, end=None, sites=None, limit=1000): """Retrieve tags (annotations or confirmed/rejected reviews) from the RFCx API Args: - type: (Required) Type of tag. Must be either: annotation, inference, inference:confirned, or inference:rejected + type: (Required) Type of tag. Must be either: annotation, inference, inference:confirmed, or inference:rejected labels: List of labels. If None then returns tags of any label. start: Minimum timestamp of the annotations to be returned. If None then defaults to exactly 30 days ago. end: Maximum timestamp of the annotations. If None then defaults to now. @@ -96,7 +155,7 @@

Module rfcx.client

print('Not authenticated') return - if type not in ['annotation', 'inference', 'inference:confirned', 'inference:rejected']: + if type not in ['annotation', 'inference', 'inference:confirmed', 'inference:rejected']: print('Unrecognized type') return @@ -133,8 +192,9 @@

Classes

self.credentials = None self.default_site = None self.accessible_sites = None + self.persisted_credentials_path = '.rfcx_credentials' - def authenticate(self): + def authenticate(self, persist=True): """Authenticate an RFCx user to obtain a token Returns: @@ -145,6 +205,18 @@

Classes

print('Already authenticated') return + access_token = None + + # Attempt to load the credentials from disk + if path.exists(self.persisted_credentials_path): + with open(self.persisted_credentials_path, 'r') as f: + lines = f.read().splitlines() + if len(lines) == 5 and lines[0] == 'version 1': + token_expiry = datetime.datetime.strptime(lines[3], "%Y-%m-%dT%H:%M:%S.%fZ") + self._setup_credentials(lines[1], lines[2], token_expiry, lines[4]) + print('Using persisted authenticatation') + return + # Create a Code Verifier & Challenge code_verifier = pkce.code_verifier() code_challenge = pkce.code_challenge(code_verifier) @@ -161,23 +233,68 @@

Classes

# Perform the exchange access_token, refresh_token, token_expiry, id_token = api_auth.authcode_exchange(code.strip(), code_verifier, client_id, scope) + self._setup_credentials(access_token, refresh_token, token_expiry, id_token) + + print('Successfully authenticated') + print('Default site:', self.default_site) + print('Accessible sites:', self.accessible_sites) - # Store the result in credentials + # Write token to disk + if persist: + with open(self.persisted_credentials_path, 'w') as f: + f.write('version 1\n') + f.write(access_token + '\n' + (refresh_token if refresh_token != None else '') + '\n' + token_expiry.isoformat() + 'Z\n' + id_token + '\n') + + def _setup_credentials(self, access_token, token_expiry, refresh_token, id_token): self.credentials = Credentials(access_token, token_expiry, refresh_token, id_token) - print('Successfully authenticated') app_meta = self.credentials.id_object['https://rfcx.org/app_metadata'] if app_meta: self.accessible_sites = app_meta['accessibleSites'] self.default_site = app_meta['defaultSite'] - print('Default site:', self.default_site) - print('Accessible sites:', self.accessible_sites) + def guardians(self, sites=None): + """Retrieve a list of guardians from a site (TO BE DEPRECATED - use streams in future) + + Args: + sites: List of site shortnames (e.g. cerroblanco). Default (None) gets all your accessible sites. + + Returns: + List of guardians""" + + if sites == None: + sites = self.accessible_sites + + return api_rfcx.guardians(self.credentials.id_token, sites) + + def guardianAudio(self, guardianId=None, start=None, end=None, limit=50, descending=True): + """Retrieve audio information about a specific guardian (TO BE DEPRECATED - use streams in future) + + Args: + guardianId: (Required) The guid of a guardian + start: Minimum timestamp of the audio. If None then defaults to exactly 30 days ago. + end: Maximum timestamp of the audio. If None then defaults to now. + limit: Maximum results to return. Defaults to 50. (TODO check if there is an upper limit on the API) + descending: Order by newest results first. Defaults to True. + + Returns: + List of audio files (meta data showing audio id and recorded timestamp) + """ + if self.credentials == None: + print('Not authenticated') + return + + if start == None: + start = (datetime.datetime.utcnow() - datetime.timedelta(days=30)).replace(microsecond=0).isoformat() + 'Z' + if end == None: + end = datetime.datetime.utcnow().replace(microsecond=0).isoformat() + 'Z' + + return api_rfcx.guardianAudio(self.credentials.id_token, guardianId, start, end, limit, descending) def tags(self, type, labels=None, start=None, end=None, sites=None, limit=1000): """Retrieve tags (annotations or confirmed/rejected reviews) from the RFCx API Args: - type: (Required) Type of tag. Must be either: annotation, inference, inference:confirned, or inference:rejected + type: (Required) Type of tag. Must be either: annotation, inference, inference:confirmed, or inference:rejected labels: List of labels. If None then returns tags of any label. start: Minimum timestamp of the annotations to be returned. If None then defaults to exactly 30 days ago. end: Maximum timestamp of the annotations. If None then defaults to now. @@ -191,7 +308,7 @@

Classes

print('Not authenticated') return - if type not in ['annotation', 'inference', 'inference:confirned', 'inference:rejected']: + if type not in ['annotation', 'inference', 'inference:confirmed', 'inference:rejected']: print('Unrecognized type') return @@ -205,7 +322,7 @@

Classes

Methods

-def authenticate(self) +def authenticate(self, persist=True)

Authenticate an RFCx user to obtain a token

@@ -218,7 +335,7 @@

Returns

Expand source code -
def authenticate(self):
+
def authenticate(self, persist=True):
     """Authenticate an RFCx user to obtain a token
 
     Returns:
@@ -229,6 +346,18 @@ 

Returns

print('Already authenticated') return + access_token = None + + # Attempt to load the credentials from disk + if path.exists(self.persisted_credentials_path): + with open(self.persisted_credentials_path, 'r') as f: + lines = f.read().splitlines() + if len(lines) == 5 and lines[0] == 'version 1': + token_expiry = datetime.datetime.strptime(lines[3], "%Y-%m-%dT%H:%M:%S.%fZ") + self._setup_credentials(lines[1], lines[2], token_expiry, lines[4]) + print('Using persisted authenticatation') + return + # Create a Code Verifier & Challenge code_verifier = pkce.code_verifier() code_challenge = pkce.code_challenge(code_verifier) @@ -245,16 +374,103 @@

Returns

# Perform the exchange access_token, refresh_token, token_expiry, id_token = api_auth.authcode_exchange(code.strip(), code_verifier, client_id, scope) - - # Store the result in credentials - self.credentials = Credentials(access_token, token_expiry, refresh_token, id_token) + self._setup_credentials(access_token, refresh_token, token_expiry, id_token) + print('Successfully authenticated') - app_meta = self.credentials.id_object['https://rfcx.org/app_metadata'] - if app_meta: - self.accessible_sites = app_meta['accessibleSites'] - self.default_site = app_meta['defaultSite'] - print('Default site:', self.default_site) - print('Accessible sites:', self.accessible_sites)
+ print('Default site:', self.default_site) + print('Accessible sites:', self.accessible_sites) + + # Write token to disk + if persist: + with open(self.persisted_credentials_path, 'w') as f: + f.write('version 1\n') + f.write(access_token + '\n' + (refresh_token if refresh_token != None else '') + '\n' + token_expiry.isoformat() + 'Z\n' + id_token + '\n')
+ +
+
+def guardianAudio(self, guardianId=None, start=None, end=None, limit=50, descending=True) +
+
+

Retrieve audio information about a specific guardian (TO BE DEPRECATED - use streams in future)

+

Args

+
+
guardianId
+
(Required) The guid of a guardian
+
start
+
Minimum timestamp of the audio. If None then defaults to exactly 30 days ago.
+
end
+
Maximum timestamp of the audio. If None then defaults to now.
+
limit
+
Maximum results to return. Defaults to 50. (TODO check if there is an upper limit on the API)
+
descending
+
Order by newest results first. Defaults to True.
+
+

Returns

+
+
List of audio files (meta data showing audio id and recorded timestamp)
+
 
+
+
+ +Expand source code + +
def guardianAudio(self, guardianId=None, start=None, end=None, limit=50, descending=True):
+    """Retrieve audio information about a specific guardian (TO BE DEPRECATED - use streams in future)
+    
+    Args:
+        guardianId: (Required) The guid of a guardian
+        start: Minimum timestamp of the audio. If None then defaults to exactly 30 days ago.
+        end: Maximum timestamp of the audio. If None then defaults to now.
+        limit: Maximum results to return. Defaults to 50. (TODO check if there is an upper limit on the API)
+        descending: Order by newest results first. Defaults to True.
+
+    Returns:
+        List of audio files (meta data showing audio id and recorded timestamp)
+    """
+    if self.credentials == None:
+        print('Not authenticated')
+        return
+
+    if start == None:
+        start = (datetime.datetime.utcnow() - datetime.timedelta(days=30)).replace(microsecond=0).isoformat() + 'Z'
+    if end == None:
+        end = datetime.datetime.utcnow().replace(microsecond=0).isoformat() + 'Z'
+    
+    return api_rfcx.guardianAudio(self.credentials.id_token, guardianId, start, end, limit, descending)
+
+
+
+def guardians(self, sites=None) +
+
+

Retrieve a list of guardians from a site (TO BE DEPRECATED - use streams in future)

+

Args

+
+
sites
+
List of site shortnames (e.g. cerroblanco). Default (None) gets all your accessible sites.
+
+

Returns

+
+
List of guardians
+
 
+
+
+ +Expand source code + +
def guardians(self, sites=None):
+    """Retrieve a list of guardians from a site (TO BE DEPRECATED - use streams in future)
+    
+    Args:
+        sites: List of site shortnames (e.g. cerroblanco). Default (None) gets all your accessible sites.
+
+    Returns:
+        List of guardians"""
+
+    if sites == None:
+        sites = self.accessible_sites
+
+    return api_rfcx.guardians(self.credentials.id_token, sites)
@@ -265,7 +481,7 @@

Returns

Args

type
-
(Required) Type of tag. Must be either: annotation, inference, inference:confirned, or inference:rejected
+
(Required) Type of tag. Must be either: annotation, inference, inference:confirmed, or inference:rejected
labels
List of labels. If None then returns tags of any label.
start
@@ -290,7 +506,7 @@

Returns

"""Retrieve tags (annotations or confirmed/rejected reviews) from the RFCx API Args: - type: (Required) Type of tag. Must be either: annotation, inference, inference:confirned, or inference:rejected + type: (Required) Type of tag. Must be either: annotation, inference, inference:confirmed, or inference:rejected labels: List of labels. If None then returns tags of any label. start: Minimum timestamp of the annotations to be returned. If None then defaults to exactly 30 days ago. end: Maximum timestamp of the annotations. If None then defaults to now. @@ -304,7 +520,7 @@

Returns

print('Not authenticated') return - if type not in ['annotation', 'inference', 'inference:confirned', 'inference:rejected']: + if type not in ['annotation', 'inference', 'inference:confirmed', 'inference:rejected']: print('Unrecognized type') return @@ -338,6 +554,8 @@

Index

Client

diff --git a/setup.py b/setup.py index 24c55a0..d2a3d34 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ REQUIRED_PACKAGES = ['pydub', 'librosa'] setup(name='rfcx', - version='0.0.4', + version='0.0.5', url='https://github.com/rfcx/rfcx-sdk-python', license='None', author='Antony Harfield',