gcis_client.py 21.4 KB
Newer Older
1
import json
2
3
import os
from os.path import basename, expanduser
abuddenberg's avatar
abuddenberg committed
4
import re
5
import requests
6
7
import yaml
import getpass
8

abuddenberg's avatar
abuddenberg committed
9
from domain import Figure, Image, Dataset, Activity, Person, Organization
10
11
12
13
14
15


def check_image(fn):
    def wrapped(*args, **kwargs):
        # if len(args) < 1 or not isinstance(args[0], Image):
        #     raise Exception('Invalid Image')
16
        if args[1].identifier in (None, ''):
17
            raise Exception('Invalid identifier', args[0].identifier)
18
        return fn(*args, **kwargs)
19
20
21
22

    return wrapped


23
24
25
26
27
28
29
30
31
32
33
34
def exists(fn):
    def wrapped(*args, **kwargs):
        resp = fn(*args, **kwargs)
        if resp.status_code == 200:
            return True
        elif resp.status_code == 404:
            return False
        else:
            raise Exception(resp.text)
    return wrapped


abuddenberg's avatar
abuddenberg committed
35
36
37
38
39
40
41
42
43
44
def http_resp(fn):
    def wrapped(*args, **kwargs):
        resp = fn(*args, **kwargs)
        if resp.status_code == 200:
            return resp
        else:
            raise Exception(resp.text)
    return wrapped


45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
def get_credentials(url):
    #First check our magic enviroment variables (GCIS_USER and GCIS_KEY)
    from gcis_clients import gcis_auth
    env_user, env_key = gcis_auth

    if env_user is not None and env_key is not None:
        return env_user, env_key

    #Next, see if we can find Gcis.conf somewhere
    conf_possibilities = [expanduser(f) for f in ['~/etc/Gcis.conf', '~/.gcis-py-client/Gcis.conf'] if
                          os.path.exists(expanduser(f))]

    for gcis_config in conf_possibilities:
        print 'Using {gc} for credentials...'.format(gc=gcis_config)
        all_creds = yaml.load(open(gcis_config, 'r'))
        instance_creds = [c for c in all_creds if c['url'] == url][0]

        return instance_creds['userinfo'].split(':')[0], instance_creds['key']

    #Else prompt for credentials
    #Wow, I managed to use it
    else:
        username = raw_input('Username: ')
        api_key = getpass.getpass('API key: ')

        return username, api_key


73
74
75
76
77
78
79
80
class AssociationException(Exception):
    def __init__(self, value):
        self.value = value

    def __str__(self):
        return repr(self.value)


81
class GcisClient(object):
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
    def __init__(self, *args, **kwargs):
        username = None
        api_key = None
        
        #Handle varargs
        #User only specifies url
        if len(args) == 1:
            self.base_url = args[0]
        #User provides url, username, and key
        elif len(args) == 3:
            self.base_url = args[0]
            username, api_key == args[1:3]
        #User provides none or inconsistent args
        else:
            print 'Using http://data.globalchange.gov'
            self.base_url = 'http://data.globalchange.gov'
98

99
        #If credentials were not provided, obtain them 
100
        if username is None or api_key is None:
101
            username, api_key = get_credentials(self.base_url)
102

103
        self.s = requests.Session()
104
        self.s.auth = (username, api_key)
105
        self.s.headers.update({'Accept': 'application/json'})
106

107
    @http_resp
108
109
    def create_figure(self, report_id, chapter_id, figure, skip_images=False):
        if figure.identifier in (None, ''):
110
111
112
113
114
            raise Exception('Invalid figure identifier', figure.identifier)

        #Is GCIS not inferring this from the url parameter?
        if figure.chapter_identifier in (None, ''):
            figure.chapter_identifier = chapter_id
115

116
117
        url = '{b}/report/{rpt}/chapter/{chp}/figure/'.format(
            b=self.base_url, rpt=report_id, chp=chapter_id
118
119
        )

120
        resp = self.s.post(url, data=figure.as_json(), verify=False)
121
122

        if skip_images is False:
123
            for image in figure.images:
124
125
                self.create_image(image),
                self.associate_image_with_figure(image.identifier, report_id, figure.identifier)
126

127
128
129
        for p in figure.parents:
            self.associate_figure_with_parent(report_id, figure.identifier, p)

130
        return resp
131

132
    @http_resp
133
    def update_figure(self, report_id, chapter_id, figure, skip_images=False, old_id=None):
134
135
        if figure.identifier in (None, ''):
            raise Exception('Invalid identifier', figure.identifier)
136
137
138
139
140

        #Is GCIS not inferring this from the url parameter?
        if figure.chapter_identifier in (None, ''):
            figure.chapter_identifier = chapter_id

141
142
        url = '{b}/report/{rpt}/chapter/{chp}/figure/{fig}'.format(
            b=self.base_url, rpt=report_id, chp=chapter_id, fig=old_id or figure.identifier
143
144
        )

145
        resp = self.s.post(url, data=figure.as_json(), verify=False)
146

147
        if skip_images is False:
148
            for image in figure.images:
149
150
                self.update_image(image)

151
152
153
        for c in figure.contributors:
            self.associate_contributor_with_figure(c, report_id, chapter_id, figure.identifier)

154
155
156
        for p in figure.parents:
            self.associate_figure_with_parent(report_id, figure.identifier, p)

157
        return resp
158

159
    @http_resp
160
161
    def delete_figure(self, report_id, figure_id):
        url = '{b}/report/{rpt}/figure/{fig}'.format(b=self.base_url, rpt=report_id, fig=figure_id)
162
        return self.s.delete(url, verify=False)
163

164
    @check_image
165
    def create_image(self, image, report_id=None, figure_id=None):
abuddenberg's avatar
abuddenberg committed
166
        url = '{b}/image/'.format(b=self.base_url)
167
        resp = self.s.post(url, data=image.as_json(), verify=False)
168
169
170
171
172
173
        
        if image.local_path is not None:
            self.upload_image_file(image.identifier, image.local_path)
        if figure_id and report_id:
            self.associate_image_with_figure(image.identifier, report_id, figure_id)
        for dataset in image.datasets:
abuddenberg's avatar
abuddenberg committed
174
175
            if not self.dataset_exists(dataset.identifier):
                self.create_dataset(dataset)
176
177
178
            # if not self.activity_exists(dataset.activity.identifier):
            #     self.create_activity(dataset.activity))
            self.create_or_update_activity(dataset.activity)
179
180
            self.associate_dataset_with_image(dataset.identifier, image.identifier,
                                              activity_id=dataset.activity.identifier)
abuddenberg's avatar
abuddenberg committed
181
        return resp
182

183
    @check_image
184
185
    def update_image(self, image, old_id=None):
        url = '{b}/image/{img}'.format(b=self.base_url, img=old_id or image.identifier)
186
        for dataset in image.datasets:
187
188
            # self.update_activity(dataset.activity)
            self.create_or_update_activity(dataset.activity)
abuddenberg's avatar
abuddenberg committed
189
190
            self.associate_dataset_with_image(dataset.identifier, image.identifier,
                                              activity_id=dataset.activity.identifier)
191
192
        for c in image.contributors:
            self.associate_contributor_with_image(c, image.identifier)
193

194
        return self.s.post(url, data=image.as_json(), verify=False)
195

196
    @check_image
197
    @http_resp
198
199
    def delete_image(self, image):
        delete_url = '{b}/image/{img}'.format(b=self.base_url, img=image.identifier)
200
        return self.s.delete(delete_url, verify=False)
201

202
    @http_resp
203
204
    def associate_image_with_figure(self, image_id, report_id, figure_id):
        url = '{b}/report/{rpt}/figure/rel/{fig}'.format(b=self.base_url, rpt=report_id, fig=figure_id)
205
        return self.s.post(url, data=json.dumps({'add_image_identifier': image_id}), verify=False)
206

207
    @http_resp
208
    def upload_image_file(self, image_id, local_path):
209
        url = '{b}/image/files/{id}/{fn}'.format(b=self.base_url, id=image_id, fn=basename(local_path))
210
        # For future multi-part encoding support
211
        # return self.s.put(url, headers=headers, files={'file': (filename, open(filepath, 'rb'))})
212
        if not os.path.exists(local_path):
213
214
            raise Exception('File not found: ' + local_path)

215
        return self.s.put(url, data=open(local_path, 'rb'), verify=False)
216

217
218
219
    #Full listing
    def get_figure_listing(self, report_id, chapter_id=None):
        chapter_filter = '/chapter/' + chapter_id if chapter_id else ''
220

221
222
        url = '{b}/report/{rpt}{chap}/figure'.format(b=self.base_url, rpt=report_id, chap=chapter_filter)
        resp = self.s.get(url, params={'all': '1'}, verify=False)
223

224
225
226
        try:
            return [Figure(figure) for figure in resp.json()]
        except ValueError:
227
            raise Exception(resp.text)
228

229
230
    def get_figure(self, report_id, figure_id, chapter_id=None):
        chapter_filter = '/chapter/' + chapter_id if chapter_id else ''
231

232
233
        url = '{b}/report/{rpt}{chap}/figure/{fig}'.format(
            b=self.base_url, rpt=report_id, chap=chapter_filter, fig=figure_id
234
        )
235
        resp = self.s.get(url, params={'all': '1'}, verify=False)
236

237
238
239
240
        try:
            return Figure(resp.json())
        except ValueError:
            raise Exception(resp.text)
241

242
243
244
245
    @exists
    def figure_exists(self, report_id, figure_id, chapter_id=None):
        chapter_filter = '/chapter/' + chapter_id if chapter_id else ''

246
247
        url = '{b}/report/{rpt}{chap}/figure/{fig}'.format(
            b=self.base_url, rpt=report_id, chap=chapter_filter, fig=figure_id
248
        )
249
        return self.s.head(url, verify=False)
250

251
252
    def get_image(self, image_id):
        url = '{b}/image/{img}'.format(b=self.base_url, img=image_id)
253
        resp = self.s.get(url, verify=False)
254

255
256
257
258
259
260
261
262
        try:
            return Image(resp.json())
        except ValueError:
            raise Exception(resp.text)

    @exists
    def image_exists(self, image_id):
        url = '{b}/image/{img}'.format(b=self.base_url, img=image_id)
263
        return self.s.head(url, verify=False)
264

265
266
    def has_all_associated_images(self, report_id, figure_id, target_image_ids):
        try:
267
268
269
270
            figure_image_ids = [i.identifier for i in self.get_figure(report_id, figure_id).images]
        except Exception, e:
            print e.message
            return False, set()
271

272
273
274
275
276
277
        target_set = set(target_image_ids)
        gcis_set = set(figure_image_ids)
        deltas = target_set - gcis_set

        if target_set.issubset(gcis_set):
            return True, deltas
278
        else:
279
            return False, deltas
280

281
282
    def test_login(self):
        url = '{b}/login.json'.format(b=self.base_url)
283
        resp = self.s.get(url, verify=False)
284
285
286
        return resp.status_code, resp.text

    def get_keyword_listing(self):
287
288
        url = '{b}/gcmd_keyword'.format(b=self.base_url)
        resp = self.s.get(url, params={'all': '1'}, verify=False)
289

290
291
292
293
        return resp.json()

    def get_keyword(self, key_id):
        url = '{b}/gcmd_keyword/{k}'.format(b=self.base_url, k=key_id)
294
        return self.s.get(url, verify=False).json()
295
296
297

    def associate_keyword_with_figure(self, keyword_id, report_id, figure_id):
        url = '{b}/report/{rpt}/figure/keywords/{fig}'.format(b=self.base_url, rpt=report_id, fig=figure_id)
298
        return self.s.post(url, data=json.dumps({'identifier': keyword_id}), verify=False)
299
300
301

    def get_dataset(self, dataset_id):
        url = '{b}/dataset/{ds}'.format(b=self.base_url, ds=dataset_id)
302
        resp = self.s.get(url, verify=False)
303
304
305
        try:
            return Dataset(resp.json())
        except ValueError:
abuddenberg's avatar
abuddenberg committed
306
            raise Exception(resp.text)
307

308
309
310
    @exists
    def dataset_exists(self, dataset_id):
        url = '{b}/dataset/{ds}'.format(b=self.base_url, ds=dataset_id)
311
        return self.s.head(url, verify=False)
312

abuddenberg's avatar
abuddenberg committed
313
    @http_resp
abuddenberg's avatar
abuddenberg committed
314
    def create_dataset(self, dataset):
315
        url = '{b}/dataset/'.format(b=self.base_url)
316
        return self.s.post(url, data=dataset.as_json(), verify=False)
317

abuddenberg's avatar
abuddenberg committed
318
    @http_resp
319
320
    def update_dataset(self, dataset, old_id=None):
        url = '{b}/dataset/{ds}'.format(b=self.base_url, ds=old_id or dataset.identifier)
321
        return self.s.post(url, data=dataset.as_json(), verify=False)
322

abuddenberg's avatar
abuddenberg committed
323
    @http_resp
324
325
    def delete_dataset(self, dataset):
        url = '{b}/dataset/{ds}'.format(b=self.base_url, ds=dataset.identifier)
326
        return self.s.delete(url, verify=False)
327

abuddenberg's avatar
abuddenberg committed
328
329
330
331
332
    @http_resp
    def get_dataset_list(self):
        url = '{b}/dataset/'.format(b=self.base_url)
        return self.s.get(url, params={'all': 1}, verify=False)

abuddenberg's avatar
abuddenberg committed
333
    def associate_dataset_with_image(self, dataset_id, image_id, activity_id=None):
334
        url = '{b}/image/prov/{img}'.format(b=self.base_url, img=image_id)
abuddenberg's avatar
abuddenberg committed
335

336
337
338
339
        data = {
            'parent_uri': '/dataset/' + dataset_id,
            'parent_rel': 'prov:wasDerivedFrom'
        }
abuddenberg's avatar
abuddenberg committed
340
341
342
        if activity_id:
            data['activity'] = activity_id

343
344
345
346
347
        try:
            self.delete_dataset_image_assoc(dataset_id, image_id)
        except AssociationException as e:
            print e.value

348
        resp = self.s.post(url, data=json.dumps(data), verify=False)
349
350
351
352

        if resp.status_code == 200:
            return resp
        else:
353
            raise Exception('Dataset association failed:\n{url}\n{resp}'.format(url=url, resp=resp.text))
354

abuddenberg's avatar
abuddenberg committed
355
356
357
358
359
360
361
362
363
    def delete_dataset_image_assoc(self, dataset_id, image_id):
        url = '{b}/image/prov/{img}'.format(b=self.base_url, img=image_id)

        data = {
            'delete': {
                'parent_uri': '/dataset/' + dataset_id,
                'parent_rel': 'prov:wasDerivedFrom'
            }
        }
364
        resp = self.s.post(url, data=json.dumps(data), verify=False)
abuddenberg's avatar
abuddenberg committed
365
366
367
368

        if resp.status_code == 200:
            return resp
        else:
369
370
371
372
373
374
            raise AssociationException(
                'Dataset dissociation failed:\n{url}\n{resp}\n{d}'.format(url=url, resp=resp.text, d=data))

    def create_or_update_dataset(self, dataset):
        if self.dataset_exists(dataset.identifier):
            print 'Updating dataset: ' + dataset.identifier
375
            self.update_dataset(dataset)
376
377
        else:
            print 'Creating dataset: ' + dataset.identifier
378
            self.create_dataset(dataset)
379

abuddenberg's avatar
abuddenberg committed
380
381
382
    # @exists
    def activity_exists(self, activity_id):
        url = '{b}/activity/{act}'.format(b=self.base_url, act=activity_id)
383
        resp = self.s.head(url, verify=False)
abuddenberg's avatar
abuddenberg committed
384
385
386
387
388
389
390
        if resp.status_code == 200:
            return True
        else:
            return False

    def get_activity(self, activity_id):
        url = '{b}/activity/{act}'.format(b=self.base_url, act=activity_id)
391
        resp = self.s.get(url, verify=False)
abuddenberg's avatar
abuddenberg committed
392
393
394
        try:
            return Activity(resp.json())
        except ValueError:
abuddenberg's avatar
abuddenberg committed
395
            raise Exception(resp.text)
abuddenberg's avatar
abuddenberg committed
396
397
398
399

    @http_resp
    def create_activity(self, activity):
        url = '{b}/activity/'.format(b=self.base_url)
400
        return self.s.post(url, data=activity.as_json(), verify=False)
abuddenberg's avatar
abuddenberg committed
401
402

    @http_resp
403
404
    def update_activity(self, activity, old_id=None):
        url = '{b}/activity/{act}'.format(b=self.base_url, act=old_id or activity.identifier)
405
        return self.s.post(url, data=activity.as_json(), verify=False)
abuddenberg's avatar
abuddenberg committed
406
407
408
409

    @http_resp
    def delete_activity(self, activity):
        url = '{b}/activity/{act}'.format(b=self.base_url, act=activity.identifier)
410
        return self.s.delete(url, verify=False)
abuddenberg's avatar
abuddenberg committed
411

412
413
414
415
416
    def create_or_update_activity(self, activity):
        if self.activity_exists(activity.identifier):
            self.update_activity(activity)
        else:
            self.create_activity(activity)
abuddenberg's avatar
abuddenberg committed
417

418
419
420
421
422
    def get_activity_list(self):
        url = '{b}/activity'.format(b=self.base_url)

        return self.s.get(url, params={'all': 1}, verify=False).json()

abuddenberg's avatar
abuddenberg committed
423
424
425
    @exists
    def person_exists(self, person_id):
        url = '{b}/person/{pid}'.format(b=self.base_url, pid=person_id)
426
        return self.s.head(url, verfiy=False)
abuddenberg's avatar
abuddenberg committed
427
428
429

    def get_person(self, person_id):
        url = '{b}/person/{pid}'.format(b=self.base_url, pid=person_id)
430
        resp = self.s.get(url, verify=False)
abuddenberg's avatar
abuddenberg committed
431
432
433
434
435
436
437
        try:
            return Person(resp.json())
        except ValueError:
            raise Exception(resp.text)

    def lookup_person(self, name):
        url = '{b}/autocomplete'.format(b=self.base_url)
438
        resp = self.s.get(url, params={'q': name, 'items': 15, 'type': 'person'}, verify=False)
abuddenberg's avatar
abuddenberg committed
439
440
441
442
443
444
445
446
447

        if resp.status_code == 200:
            return [re.match(r'\[person\] \{(\d+)\} (.*)', r).groups() for r in resp.json()]
        else:
            raise Exception(resp.text)

    @http_resp
    def create_person(self, person):
        url = '{b}/person/'.format(b=self.base_url)
448
        return self.s.post(url, data=person.as_json(), verify=False)
abuddenberg's avatar
abuddenberg committed
449
450

    @http_resp
451
452
    def update_person(self, person, old_id=None):
        url = '{b}/person/{pid}'.format(b=self.base_url, pid=old_id or person.identifier)
453
        return self.s.post(url, data=person.as_json(), verify=False)
abuddenberg's avatar
abuddenberg committed
454
455
456
457

    @http_resp
    def delete_person(self, person):
        url = '{b}/person/{pid}'.format(b=self.base_url, pid=person.identifier)
458
        return self.s.delete(url, verify=False)
abuddenberg's avatar
abuddenberg committed
459
460
461
462

    @exists
    def organization_exists(self, org_id):
        url = '{b}/organization/{org_id)'.format(b=self.base_url, org_id=org_id)
463
        return self.s.head(url, verify=False)
abuddenberg's avatar
abuddenberg committed
464
465

    def get_organization(self, org_id):
466
467
        url = '{b}/organization/{org_id}'.format(b=self.base_url, org_id=org_id)
        resp = self.s.get(url, verify=False)
abuddenberg's avatar
abuddenberg committed
468
469
470
471
472
473
474

        try:
            return Organization(resp.json())
        except ValueError:
            raise Exception(resp.text)

    def lookup_organization(self, name):
475
        url = '{b}/autocomplete'.format(b=self.base_url)
476
        resp = self.s.get(url, params={'q': name, 'items': 15, 'type': 'organization'}, verify=False)
477
478
479
480
481
        
        if resp.status_code == 200:
            return [re.match(r'\[organization\] \{(.*)\} (.*)', r).groups() for r in resp.json()]
        else:
            raise Exception(resp.text)
abuddenberg's avatar
abuddenberg committed
482
483
484
485

    @http_resp
    def create_organization(self, org):
        url = '{b}/organization/'.format(b=self.base_url)
486
        return self.s.post(url, data=org.as_json(), verify=False)
abuddenberg's avatar
abuddenberg committed
487
488

    @http_resp
489
490
    def update_organization(self, org, old_id=None):
        url = '{b}/organization/{org_id}'.format(b=self.base_url, org_id=old_id or org.identifier)
491
        return self.s.post(url, data=org.as_json(), verify=False)
abuddenberg's avatar
abuddenberg committed
492
493
494
495

    @http_resp
    def delete_organization(self, org):
        url = '{b}/organization/{org_id}'.format(b=self.base_url, org_id=org.identifier)
496
        return self.s.delete(url, verify=False)
497
498
499
500
501

    @http_resp
    def associate_contributor_with_figure(self, contrib, report_id, chapter_id, figure_id):
        url = '{b}/report/{rpt}/chapter/{chp}/figure/contributors/{fig}'.format(b=self.base_url, rpt=report_id, chp=chapter_id, fig=figure_id)

502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
        data = {
            'role': contrib.role.type_id,
        }

        if contrib.person is not None and contrib.person.id is not None:
            data['person_id'] = contrib.person.id
        if contrib.organization is not None and contrib.organization.identifier:
            data['organization_identifier'] = contrib.organization.identifier

        resp = self.s.post(url, data=json.dumps(data), verify=False)
        return resp

    @http_resp
    def delete_contributor_figure_assoc(self, contrib, report_id, chapter_id, figure_id):
        url = '{b}/report/{rpt}/chapter/{chp}/figure/contributors/{fig}'.format(b=self.base_url, rpt=report_id, chp=chapter_id, fig=figure_id)

        data = {
519
            'delete_contributor': contrib.id
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
        }

        return self.s.post(url, data=json.dumps(data), verify=False)

    @http_resp
    def associate_contributor_with_image(self, contrib, image_id):
        url = '{b}/image/contributors/{img}'.format(b=self.base_url, img=image_id)

        data = {
            'role': contrib.role.type_id,
        }
        if contrib.person is not None and contrib.person.id is not None:
            data['person_id'] = contrib.person.id
        if contrib.organization is not None and contrib.organization.identifier:
            data['organization_identifier'] = contrib.organization.identifier

        return self.s.post(url, data=json.dumps(data), verify=False)

    @http_resp
    def delete_contributor_image_assoc(self, contrib, image_id):
        url = '{b}/image/contributors/{img}'.format(b=self.base_url, img=image_id)

        data = {
            'delete': {
                'role': contrib.role.type_id,
                'organization_identifier': contrib.organization.identifier,
                'person_id': contrib.person.identifier
            }
        }
549

550
        return self.s.post(url, data=json.dumps(data), verify=False)
551

abuddenberg's avatar
abuddenberg committed
552
    @http_resp
553
    def associate_figure_with_parent(self, report_id, figure_id, parent):
abuddenberg's avatar
abuddenberg committed
554
555
556
        url = '{b}/report/{rpt}/figure/prov/{fig}'.format(b=self.base_url, rpt=report_id, fig=figure_id)

        data = {
557
558
            'parent_uri': parent.url,
            'parent_rel': parent.relationship
abuddenberg's avatar
abuddenberg committed
559
560
        }

561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
        try:
            self.delete_figure_pub_assoc(report_id, figure_id, parent)
        except AssociationException as e:
            print e.value

        resp = self.s.post(url, data=json.dumps(data), verify=False)
        return resp

    def delete_figure_pub_assoc(self, report_id, figure_id, parent):
        url = '{b}/report/{rpt}/figure/prov/{fig}'.format(b=self.base_url, rpt=report_id, fig=figure_id)

        data = {
            'delete': {
                'parent_uri': parent.url,
                'parent_rel': parent.relationship
            }
        }
        resp = self.s.post(url, data=json.dumps(data), verify=False)

        if resp.status_code == 200:
            return resp
        else:
            raise AssociationException(
                'Parent dissociation failed:\n{url}\n{resp}\n{d}'.format(url=url, resp=resp.text, d=data))
abuddenberg's avatar
abuddenberg committed
585
586
587
588
589
590
591

    def lookup_publication(self, pub_type, name):
        url = '{b}/autocomplete'.format(b=self.base_url)
        resp = self.s.get(url, params={'q': name, 'items': 15, 'type': pub_type}, verify=False)

        if resp.status_code == 200:
            return [re.match(r'\[.+\] \{(.+)\} (.*)', r).groups() for r in resp.json()]
592
            # return resp.json()
abuddenberg's avatar
abuddenberg committed
593
        else:
594
            raise Exception(resp.text)