gcis_client.py 27.5 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

9
from domain import Figure, Image, Dataset, Activity, Person, Organization, Article, Webpage, Report
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, ''):
abuddenberg's avatar
abuddenberg committed
17
            raise Exception('Invalid identifier', args[1].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
def http_resp(fn):
    def wrapped(*args, **kwargs):
        resp = fn(*args, **kwargs)
        if resp.status_code == 200:
            return resp
        else:
41
            raise Exception('{url}\n{stat} {txt}'.format(url=resp.url, stat=resp.status_code, txt=resp.text))
abuddenberg's avatar
abuddenberg committed
42
43
44
    return wrapped


45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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]

62
        return instance_creds['userinfo'].split(':')
63
64
65
66
67
68
69
70
71
72

    #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
    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]
93
            username, api_key = args[1:3]
94
95
96
        #User provides none or inconsistent args
        else:
            self.base_url = 'http://data.globalchange.gov'
97

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

102
103
104
105
        #Squash trailing slash in base_url, if given
        if self.base_url.endswith('/'):
            self.base_url = self.base_url[:-1]

106
        self.s = requests.Session()
107
        self.s.auth = (username, api_key)
108
        self.s.headers.update({'Accept': 'application/json'})
109

110
    @http_resp
111
    def create_figure(self, report_id, chapter_id, figure, skip_images=False, skip_upload=False):
112
        if figure.identifier in (None, ''):
113
114
115
116
117
            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
118

119
120
        url = '{b}/report/{rpt}/chapter/{chp}/figure/'.format(
            b=self.base_url, rpt=report_id, chp=chapter_id
121
122
        )

123
        resp = self.s.post(url, data=figure.as_json(), verify=False)
abuddenberg's avatar
abuddenberg committed
124
125
126
        if resp.status_code != 200:
            return resp

127
        if not skip_upload and figure.local_path is not None:
abuddenberg's avatar
abuddenberg committed
128
            self.upload_figure_file(report_id, chapter_id, figure.identifier, figure.local_path)
129
130

        if skip_images is False:
131
            for image in figure.images:
132
133
                self.create_image(image),
                self.associate_image_with_figure(image.identifier, report_id, figure.identifier)
134

135
136
137
        for c in figure.contributors:
            self.associate_contributor_with_figure(c, report_id, chapter_id, figure.identifier)

138
        for p in figure.parents:
139
140
141
142
            if p.activity:
                self.create_or_update_activity(p.activity)
            activity_id = p.activity.identifier if p.activity else None
            self.associate_figure_with_parent(report_id, figure.identifier, p, activity_id=activity_id)
143

144
        return resp
145

146
    @http_resp
147
    def update_figure(self, report_id, chapter_id, figure, skip_images=False, old_id=None):
148
149
        if figure.identifier in (None, ''):
            raise Exception('Invalid identifier', figure.identifier)
150
151
152
153
154

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

155
156
        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
157
158
        )

159
        resp = self.s.post(url, data=figure.as_json(), verify=False)
160

161
        if skip_images is False:
162
            for image in figure.images:
163
164
                self.update_image(image)

165
166
167
        for c in figure.contributors:
            self.associate_contributor_with_figure(c, report_id, chapter_id, figure.identifier)

168
        for p in figure.parents:
169
170
171
172
            if p.activity:
                self.create_or_update_activity(p.activity)
            activity_id = p.activity.identifier if p.activity else None
            self.associate_figure_with_parent(report_id, figure.identifier, p, activity_id=activity_id)
173

174
        return resp
175

176
    @http_resp
177
178
    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)
179
        return self.s.delete(url, verify=False)
180

abuddenberg's avatar
abuddenberg committed
181
182
183
184
185
186
187
188
189
190
    @http_resp
    def upload_figure_file(self, report_id, chapter_id, figure_id, local_path):
        url = '{b}/report/{rpt}/chapter/{chp}/figure/files/{id}/{fn}'.format(b=self.base_url, rpt=report_id, chp=chapter_id, id=figure_id, fn=basename(local_path))
        # For future multi-part encoding support
        # return self.s.put(url, headers=headers, files={'file': (filename, open(filepath, 'rb'))})
        if not os.path.exists(local_path):
            raise Exception('File not found: ' + local_path)

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

191
    @check_image
192
    def create_image(self, image, report_id=None, figure_id=None):
abuddenberg's avatar
abuddenberg committed
193
        url = '{b}/image/'.format(b=self.base_url)
194
        resp = self.s.post(url, data=image.as_json(), verify=False)
195

abuddenberg's avatar
abuddenberg committed
196
197
        if resp.status_code != 200:
            return resp
198
199
200
201
202
        
        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)
203
204
205
206
207
208
209
210
211
212
213
214
215
216
        # for dataset in image.datasets:
        #     if not self.dataset_exists(dataset.identifier):
        #         self.create_dataset(dataset)
        #     # if not self.activity_exists(dataset.activity.identifier):
        #     #     self.create_activity(dataset.activity))
        #     self.create_or_update_activity(dataset.activity)
        #     self.associate_image_with_parent(dataset.identifier, image.identifier,
        #                                       activity_id=dataset.activity.identifier)
        for p in image.parents:
            if p.activity:
                self.create_or_update_activity(p.activity)

            activity_id = p.activity.identifier if p.activity else None
            self.associate_image_with_parent(image.identifier, p, activity_id=activity_id)
abuddenberg's avatar
abuddenberg committed
217
        return resp
218

219
    @check_image
220
221
    def update_image(self, image, old_id=None):
        url = '{b}/image/{img}'.format(b=self.base_url, img=old_id or image.identifier)
222
223
        for c in image.contributors:
            self.associate_contributor_with_image(c, image.identifier)
224

225
226
227
228
229
230
        for p in image.parents:
            if p.activity:
                self.create_or_update_activity(p.activity)
            activity_id = p.activity.identifier if p.activity else None
            self.associate_image_with_parent(image.identifier, p, activity_id=activity_id)

231
        return self.s.post(url, data=image.as_json(), verify=False)
232

233
    @check_image
234
    @http_resp
235
236
    def delete_image(self, image):
        delete_url = '{b}/image/{img}'.format(b=self.base_url, img=image.identifier)
237
        return self.s.delete(delete_url, verify=False)
238

239
    @http_resp
240
241
    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)
242
        return self.s.post(url, data=json.dumps({'add_image_identifier': image_id}), verify=False)
243

244
    @http_resp
245
    def upload_image_file(self, image_id, local_path):
246
        url = '{b}/image/files/{id}/{fn}'.format(b=self.base_url, id=image_id, fn=basename(local_path))
247
        # For future multi-part encoding support
248
        # return self.s.put(url, headers=headers, files={'file': (filename, open(filepath, 'rb'))})
249
        if not os.path.exists(local_path):
250
251
            raise Exception('File not found: ' + local_path)

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

254
255
256
    #Full listing
    def get_figure_listing(self, report_id, chapter_id=None):
        chapter_filter = '/chapter/' + chapter_id if chapter_id else ''
257

258
259
        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)
260

261
262
263
        try:
            return [Figure(figure) for figure in resp.json()]
        except ValueError:
264
            raise Exception(resp.text)
265

266
267
    def get_figure(self, report_id, figure_id, chapter_id=None):
        chapter_filter = '/chapter/' + chapter_id if chapter_id else ''
268

269
270
        url = '{b}/report/{rpt}{chap}/figure/{fig}'.format(
            b=self.base_url, rpt=report_id, chap=chapter_filter, fig=figure_id
271
        )
272
        resp = self.s.get(url, params={'all': '1'}, verify=False)
273

274
275
276
277
        try:
            return Figure(resp.json())
        except ValueError:
            raise Exception(resp.text)
278

279
280
281
282
    @exists
    def figure_exists(self, report_id, figure_id, chapter_id=None):
        chapter_filter = '/chapter/' + chapter_id if chapter_id else ''

283
284
        url = '{b}/report/{rpt}{chap}/figure/{fig}'.format(
            b=self.base_url, rpt=report_id, chap=chapter_filter, fig=figure_id
285
        )
286
        return self.s.head(url, verify=False)
287

288
289
    def get_image(self, image_id):
        url = '{b}/image/{img}'.format(b=self.base_url, img=image_id)
290
        resp = self.s.get(url, verify=False)
291

292
293
294
295
296
297
298
299
        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)
300
        return self.s.head(url, verify=False)
301

302
303
    def has_all_associated_images(self, report_id, figure_id, target_image_ids):
        try:
304
305
306
307
            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()
308

309
310
311
312
313
314
        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
315
        else:
316
            return False, deltas
317

318
319
    def test_login(self):
        url = '{b}/login.json'.format(b=self.base_url)
320
        resp = self.s.get(url, verify=False)
321
322
        return resp.status_code, resp.text

323
324
325
326
    def get_report(self, report_id):
        url = '{b}/report/{id}'.format(b=self.base_url, id=report_id)
        resp = self.s.get(url, verify=False)

327
328
329
330
        try:
            return Report(resp.json())
        except ValueError:
            raise Exception(resp.text)
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378

    @exists
    def report_exists(self, report_id):
        url = '{b}/report/{id}'.format(b=self.base_url, id=report_id)
        return self.s.head(url, verify=False)

    @http_resp
    def create_report(self, report):
        url = '{b}/report/'.format(b=self.base_url)
        return self.s.post(url, data=report.as_json(), verify=False)

    @http_resp
    def update_report(self, report, old_id=None):
        url = '{b}/report/{id}'.format(b=self.base_url, id=old_id or report.identifier)
        return self.s.post(url, data=report.as_json(), verify=False)

    @http_resp
    def delete_report(self, report):
        url = '{b}/report/{ds}'.format(b=self.base_url, ds=report.identifier)
        return self.s.delete(url, verify=False)

    @http_resp
    def get_chapter(self, chapter_id):
        url = '{b}/chapter/{id}'.format(b=self.base_url, id=chapter_id)
        resp = self.s.get(url, verify=False)

        return resp.json()

    @exists
    def chapter_exists(self, report_id, chapter_id):
        url = '{b}/report/{rpt}/chapter/{id}'.format(b=self.base_url, rpt=report_id, id=chapter_id)
        return self.s.head(url, verify=False)

    @http_resp
    def create_chapter(self, report_id, chapter):
        url = '{b}/report/{rpt}/chapter/'.format(b=self.base_url, rpt=report_id)
        return self.s.post(url, data=chapter.as_json(), verify=False)

    @http_resp
    def update_chapter(self, chapter, old_id=None):
        url = '{b}/chapter/{id}'.format(b=self.base_url, id=old_id or chapter.identifier)
        return self.s.post(url, data=chapter.as_json(), verify=False)

    @http_resp
    def delete_chapter(self, chapter):
        url = '{b}/chapter/{ds}'.format(b=self.base_url, ds=chapter.identifier)
        return self.s.delete(url, verify=False)

379
    def get_keyword_listing(self):
380
381
        url = '{b}/gcmd_keyword'.format(b=self.base_url)
        resp = self.s.get(url, params={'all': '1'}, verify=False)
382

383
384
385
386
        return resp.json()

    def get_keyword(self, key_id):
        url = '{b}/gcmd_keyword/{k}'.format(b=self.base_url, k=key_id)
387
        return self.s.get(url, verify=False).json()
388
389
390

    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)
391
        return self.s.post(url, data=json.dumps({'identifier': keyword_id}), verify=False)
392
393
394

    def get_dataset(self, dataset_id):
        url = '{b}/dataset/{ds}'.format(b=self.base_url, ds=dataset_id)
395
        resp = self.s.get(url, verify=False)
396
397
398
        try:
            return Dataset(resp.json())
        except ValueError:
abuddenberg's avatar
abuddenberg committed
399
            raise Exception(resp.text)
400

401
402
403
    @exists
    def dataset_exists(self, dataset_id):
        url = '{b}/dataset/{ds}'.format(b=self.base_url, ds=dataset_id)
404
        return self.s.head(url, verify=False)
405

abuddenberg's avatar
abuddenberg committed
406
    @http_resp
abuddenberg's avatar
abuddenberg committed
407
    def create_dataset(self, dataset):
408
        url = '{b}/dataset/'.format(b=self.base_url)
409
        return self.s.post(url, data=dataset.as_json(), verify=False)
410

abuddenberg's avatar
abuddenberg committed
411
    @http_resp
412
413
    def update_dataset(self, dataset, old_id=None):
        url = '{b}/dataset/{ds}'.format(b=self.base_url, ds=old_id or dataset.identifier)
414
        return self.s.post(url, data=dataset.as_json(), verify=False)
415

abuddenberg's avatar
abuddenberg committed
416
    @http_resp
417
418
    def delete_dataset(self, dataset):
        url = '{b}/dataset/{ds}'.format(b=self.base_url, ds=dataset.identifier)
419
        return self.s.delete(url, verify=False)
420

abuddenberg's avatar
abuddenberg committed
421
422
423
424
425
    @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)

426
427
428
    def create_or_update_dataset(self, dataset):
        if self.dataset_exists(dataset.identifier):
            print 'Updating dataset: ' + dataset.identifier
429
            self.update_dataset(dataset)
430
431
        else:
            print 'Creating dataset: ' + dataset.identifier
432
            self.create_dataset(dataset)
433

abuddenberg's avatar
abuddenberg committed
434
435
436
    # @exists
    def activity_exists(self, activity_id):
        url = '{b}/activity/{act}'.format(b=self.base_url, act=activity_id)
437
        resp = self.s.head(url, verify=False)
abuddenberg's avatar
abuddenberg committed
438
439
440
441
442
443
444
        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)
445
        resp = self.s.get(url, verify=False)
abuddenberg's avatar
abuddenberg committed
446
447
448
        try:
            return Activity(resp.json())
        except ValueError:
abuddenberg's avatar
abuddenberg committed
449
            raise Exception(resp.text)
abuddenberg's avatar
abuddenberg committed
450
451
452
453

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

    @http_resp
457
458
    def update_activity(self, activity, old_id=None):
        url = '{b}/activity/{act}'.format(b=self.base_url, act=old_id or activity.identifier)
459
        return self.s.post(url, data=activity.as_json(), verify=False)
abuddenberg's avatar
abuddenberg committed
460
461
462
463

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

466
467
468
469
470
    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
471

472
473
474
475
476
    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
477
478
479
    @exists
    def person_exists(self, person_id):
        url = '{b}/person/{pid}'.format(b=self.base_url, pid=person_id)
480
        return self.s.head(url, verfiy=False)
abuddenberg's avatar
abuddenberg committed
481
482
483

    def get_person(self, person_id):
        url = '{b}/person/{pid}'.format(b=self.base_url, pid=person_id)
484
        resp = self.s.get(url, verify=False)
abuddenberg's avatar
abuddenberg committed
485
486
487
488
489
490
491
        try:
            return Person(resp.json())
        except ValueError:
            raise Exception(resp.text)

    def lookup_person(self, name):
        url = '{b}/autocomplete'.format(b=self.base_url)
492
        resp = self.s.get(url, params={'q': name, 'items': 15, 'type': 'person'}, verify=False)
abuddenberg's avatar
abuddenberg committed
493
494
495
496
497
498
499
500
501

        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)
502
        return self.s.post(url, data=person.as_json(), verify=False)
abuddenberg's avatar
abuddenberg committed
503
504

    @http_resp
505
506
    def update_person(self, person, old_id=None):
        url = '{b}/person/{pid}'.format(b=self.base_url, pid=old_id or person.identifier)
507
        return self.s.post(url, data=person.as_json(), verify=False)
abuddenberg's avatar
abuddenberg committed
508
509
510
511

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

514
515
516
517
518
519
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
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
    @exists
    def article_exists(self, article_id):
        url = '{b}/article/{aid}'.format(b=self.base_url, aid=article_id)
        return self.s.head(url, verify=False)

    def get_article(self, article_id):
        url = '{b}/article/{aid}'.format(b=self.base_url, aid=article_id)
        resp = self.s.get(url, verify=False)
        try:
            return Article(resp.json())
        except ValueError:
            raise Exception(resp.text)

    @http_resp
    def create_article(self, article):
        url = '{b}/article/'.format(b=self.base_url)
        return self.s.post(url, data=article.as_json(), verify=False)

    @http_resp
    def update_article(self, article):
        url = '{b}/article/{aid}'.format(b=self.base_url, aid=article.identifier)
        return self.s.post(url, data=article.as_json(), verify=False)

    @http_resp
    def delete_article(self, article):
        url = '{b}/article/{aid}'.format(b=self.base_url, aid=article.identifier)
        return self.s.delete(url, verify=False)

    @exists
    def webpage_exists(self, webpage_id):
        url = '{b}/webpage/{id}'.format(b=self.base_url, id=webpage_id)
        return self.s.head(url, verify=False)

    def get_webpage(self, webpage_id):
        url = '{b}/webpage/{id}'.format(b=self.base_url, id=webpage_id)
        resp = self.s.get(url, verify=False)
        try:
            return Webpage(resp.json())
        except ValueError:
            raise Exception(resp.text)

    @http_resp
    def create_webpage(self, webpage):
        url = '{b}/webpage/'.format(b=self.base_url)
        return self.s.post(url, data=webpage.as_json(), verify=False)

    @http_resp
    def update_webpage(self, webpage):
        url = '{b}/webpage/{aid}'.format(b=self.base_url, aid=webpage.identifier)
        return self.s.post(url, data=webpage.as_json(), verify=False)

    @http_resp
    def delete_webpage(self, webpage):
        url = '{b}/webpage/{aid}'.format(b=self.base_url, aid=webpage.identifier)
        return self.s.delete(url, verify=False)

abuddenberg's avatar
abuddenberg committed
570
571
572
    @exists
    def organization_exists(self, org_id):
        url = '{b}/organization/{org_id)'.format(b=self.base_url, org_id=org_id)
573
        return self.s.head(url, verify=False)
abuddenberg's avatar
abuddenberg committed
574
575

    def get_organization(self, org_id):
576
577
        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
578
579
580
581
582
583
584

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

    def lookup_organization(self, name):
585
        url = '{b}/autocomplete'.format(b=self.base_url)
586
        resp = self.s.get(url, params={'q': name, 'items': 15, 'type': 'organization'}, verify=False)
587
588
589
590
591
        
        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
592
593
594
595

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

    @http_resp
599
600
    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)
601
        return self.s.post(url, data=org.as_json(), verify=False)
abuddenberg's avatar
abuddenberg committed
602
603
604
605

    @http_resp
    def delete_organization(self, org):
        url = '{b}/organization/{org_id}'.format(b=self.base_url, org_id=org.identifier)
606
        return self.s.delete(url, verify=False)
607
608
609
610
611

    @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)

abuddenberg's avatar
abuddenberg committed
612
613
614
615
616
617
618
        try:
            data = {
                'role': contrib.role.type_id,
            }
        except AttributeError as e:
            print 'Contributor {c} missing role'.format(c=contrib)
            raise e
619
620
621
622
623
624
625
626
627
628
629
630
631
632

        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 = {
633
            'delete_contributor': contrib.id
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
        }

        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
            }
        }
663

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

abuddenberg's avatar
abuddenberg committed
666
    @http_resp
667
    def associate_figure_with_parent(self, report_id, figure_id, parent, activity_id=None):
abuddenberg's avatar
abuddenberg committed
668
669
670
        url = '{b}/report/{rpt}/figure/prov/{fig}'.format(b=self.base_url, rpt=report_id, fig=figure_id)

        data = {
671
672
            'parent_uri': parent.url,
            'parent_rel': parent.relationship
abuddenberg's avatar
abuddenberg committed
673
        }
674
675
        if activity_id:
            data['activity'] = activity_id
abuddenberg's avatar
abuddenberg committed
676

677
        try:
678
            self.delete_figure_parent_assoc(report_id, figure_id, parent)
679
680
681
682
683
684
        except AssociationException as e:
            print e.value

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

685
    def delete_figure_parent_assoc(self, report_id, figure_id, parent):
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
        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
701

702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
    @http_resp
    def associate_image_with_parent(self, image_id, parent, activity_id=None):
        url = '{b}/image/prov/{img}'.format(b=self.base_url, img=image_id)

        data = {
            'parent_uri': parent.url,
            'parent_rel': parent.relationship
        }
        if activity_id:
            data['activity'] = activity_id

        try:
            self.delete_dataset_image_assoc(image_id, parent)
        except AssociationException as e:
            print e.value

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

    def delete_dataset_image_assoc(self, image_id, parent):
        url = '{b}/image/prov/{img}'.format(b=self.base_url, img=image_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
738
739
740
741
742
743
    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()]
744
            # return resp.json()
abuddenberg's avatar
abuddenberg committed
745
        else:
746
747
748
            raise Exception('Lookup failed:\nQuery:{q}\nType:{t}\nResponse:\n{r}'.format(q=name, t=pub_type, r=resp.text))


749
750
751
752
753
    @http_resp
    def delete_file(self, file):
        url = '{b}/file/{id}'.format(b=self.base_url, id=file.identifier)
        return self.s.delete(url, verify=False)

754