base64.encodestring needs a bytearray in python3
[htsworkflow.git] / htsworkflow / util / api.py
1 """Common functions for accessing the HTS Workflow REST API
2 """
3 from __future__ import unicode_literals
4
5 import base64
6 from six.moves import configparser
7 from six import int2byte
8 import random
9 import logging
10
11 # try to deal with python <2.6
12 try:
13   import json
14 except ImportError:
15   import simplejson as json
16
17 import os
18 from optparse import OptionGroup
19 from six.moves import urllib
20
21 LOGGER = logging.getLogger(__name__)
22
23 def add_auth_options(parser):
24     """Add options OptParser configure authentication options
25     """
26     # Load defaults from the config files
27     config = configparser.SafeConfigParser()
28     config.read([os.path.expanduser('~/.htsworkflow.ini'),
29                  '/etc/htsworkflow.ini'
30                  ])
31
32     sequence_archive = None
33     apiid = None
34     apikey = None
35     apihost = None
36     SECTION = 'sequence_archive'
37     if config.has_section(SECTION):
38         sequence_archive = config.get(SECTION, 'sequence_archive',sequence_archive)
39         sequence_archive = os.path.expanduser(sequence_archive)
40         apiid = config.get(SECTION, 'apiid', apiid)
41         apikey = config.get(SECTION, 'apikey', apikey)
42         apihost = config.get(SECTION, 'host', apihost)
43
44     # configuration options
45     group = OptionGroup(parser, "htsw api authentication")
46     group.add_option('--apiid', default=apiid, help="Specify API ID")
47     group.add_option('--apikey', default=apikey, help="Specify API KEY")
48     group.add_option('--host',  default=apihost,
49                      help="specify HTSWorkflow host",)
50     group.add_option('--sequence', default=sequence_archive,
51                      help="sequence repository")
52     parser.add_option_group(group)
53     return parser
54
55 def make_auth_from_opts(opts, parser=None):
56     """Create htsw auth info dictionary from optparse info
57     """
58     if opts.host is None or opts.apiid is None or opts.apikey is None:
59         if parser is not None:
60             parser.error("Please specify host url, apiid, apikey")
61         else:
62             raise RuntimeError("Need host, api id api key")
63
64     return {'apiid': opts.apiid, 'apikey': opts.apikey }
65
66
67 def library_url(root_url, library_id):
68     """
69     Return the url for retrieving information about a specific library.
70
71     Args:
72       library_id (str): the library id of interest
73       root_url (str): the root portion of the url, e.g. http://localhost
74
75     Returns:
76       str. The url to use for this REST api.
77
78     >>> print library_url('http://localhost', '12345')
79     http://localhost/samples/library/12345/json
80
81     """
82     url_fragment = '/samples/library/%s/json' % (library_id,)
83     url = urllib.parse.urljoin(root_url, url_fragment)
84
85     return url
86
87
88 def flowcell_url(root_url, flowcell_id):
89     """
90     Return the url for retrieving information about a specific flowcell.
91
92     Args:
93       root_url (str): the root portion of the url, e.g. http://localhost
94       flowcell_id (str): the flowcell id of interest
95
96     Returns:
97       str. The url to use for this REST api.
98
99     >>> print flowcell_url('http://localhost', '1234AAXX')
100     http://localhost/experiments/config/1234AAXX/json
101     """
102     url_fragment = '/experiments/config/%s/json' % (flowcell_id,)
103     url = urllib.parse.urljoin(root_url, url_fragment)
104
105     return url
106
107
108 def lanes_for_user_url(root_url, username):
109     """
110     Return the url for returning all the lanes associated with a username
111
112     Args:
113       username (str): a username in your target filesystem
114       root_url (str): the root portion of the url, e.g. http://localhost
115
116     Returns:
117       str. The url to use for this REST api.
118
119     >>> print lanes_for_user_url('http://localhost', 'diane')
120     http://localhost/lanes_for/diane/json
121
122     """
123     url_fragment = '/lanes_for/%s/json' % (username,)
124     url = urllib.parse.urljoin(root_url, url_fragment)
125
126     return url
127
128 def retrieve_info(url, apidata):
129     """
130     Return a dictionary from the HTSworkflow API
131     """
132     try:
133         apipayload = urllib.parse.urlencode(apidata)
134         web = urllib.request.urlopen(url, apipayload)
135     except urllib.request.URLError as e:
136         if hasattr(e, 'code') and e.code == 404:
137             LOGGER.info("%s was not found" % (url,))
138             return None
139         else:
140             errmsg = 'URLError: %s' % (str(e))
141             raise IOError(errmsg)
142
143     contents = web.read()
144     headers = web.info()
145
146     return json.loads(contents)
147
148 class HtswApi(object):
149     def __init__(self, root_url, authdata):
150         self.root_url = root_url
151         self.authdata = authdata
152
153     def get_flowcell(self, flowcellId):
154         url = flowcell_url(self.root_url, flowcellId)
155         return retrieve_info(url, self.authdata)
156
157     def get_library(self, libraryId):
158         url = library_url(self.root_url, libraryId)
159         return retrieve_info(url, self.authdata)
160
161     def get_lanes_for_user(self, user):
162         url = lanes_for_user(self.root_url, user)
163         return retrieve_info(url, self.authdata)
164
165     def get_url(self, url):
166         return retrieve_info(url, self.authdata)
167
168 def make_django_secret_key(size=216):
169     """return key suitable for use as secret key"""
170     try:
171         source = random.SystemRandom()
172     except AttributeError as e:
173         source = random.random()
174     bits = source.getrandbits(size)
175     chars = []
176     while bits > 0:
177         byte = bits & 0xff
178         chars.append(int2byte(byte))
179         bits >>= 8
180     return base64.encodestring(b"".join(chars)).strip()
181
182 if __name__ == "__main__":
183     from optparse import OptionParser
184     from pprint import pprint
185     parser = OptionParser()
186     parser = add_auth_options(parser)
187     parser.add_option('--flowcell', default=None)
188     parser.add_option('--library', default=None)
189
190     opts, args = parser.parse_args()
191     apidata =  make_auth_from_opts(opts)
192
193     api = HtswApi(opts.host, apidata)
194
195     if opts.flowcell is not None:
196         pprint(api.get_flowcell(opts.flowcell))
197     if opts.library is not None:
198         pprint(api.get_library(opts.library))
199