gcis_client.py 13.1 KB
Newer Older
1
from base64 import b64encode
2
3
import urllib
import json
4
from os.path import exists, basename
5
6

import requests
abuddenberg's avatar
abuddenberg committed
7
from requests.exceptions import ConnectionError
8

abuddenberg's avatar
abuddenberg committed
9
from domain import Figure, Image, Dataset, Activity
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
class AssociationException(Exception):
    def __init__(self, value):
        self.value = value

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


53
class GcisClient(object):
54
    def __init__(self, url, username, password):
55
56
57
58
        self.headers = {
            'Accept': 'application/json'
        }

59
60
        self.base_url = url
        self.headers['Authorization'] = 'Basic ' + b64encode(username + ':' + password)
61

62
    @http_resp
63
64
    def create_figure(self, report_id, chapter_id, figure, skip_images=False):
        if figure.identifier in (None, ''):
65
66
67
68
69
            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
70

71
72
        url = '{b}/report/{rpt}/chapter/{chp}/figure/'.format(
            b=self.base_url, rpt=report_id, chp=chapter_id
73
74
        )

75
        resp = requests.post(url, data=figure.as_json(), headers=self.headers, verify=False)
76
77

        if skip_images is False:
78
            for image in figure.images:
79
80
                self.create_image(image),
                self.associate_image_with_figure(image.identifier, report_id, figure.identifier)
81

82
        return resp
83

84
    @http_resp
85
    def update_figure(self, report_id, chapter_id, figure, skip_images=False):
86
87
        if figure.identifier in (None, ''):
            raise Exception('Invalid identifier', figure.identifier)
88
89
90
91
92

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

93
        update_url = '{b}/report/{rpt}/chapter/{chp}/figure/{fig}'.format(
94
            b=self.base_url, rpt=report_id, chp=chapter_id, fig=figure.identifier
95
96
        )

abuddenberg's avatar
abuddenberg committed
97
        resp = requests.post(update_url, data=figure.as_json(), headers=self.headers, verify=False)
98

99
        if skip_images is False:
100
            for image in figure.images:
101
102
103
                self.update_image(image)

        return resp
104

105
    @http_resp
106
107
    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)
108
        return requests.delete(url, headers=self.headers, verify=False)
109

110
    @check_image
111
    def create_image(self, image, report_id=None, figure_id=None):
abuddenberg's avatar
abuddenberg committed
112
113
        url = '{b}/image/'.format(b=self.base_url)
        resp = requests.post(url, data=image.as_json(), headers=self.headers, verify=False)
114
115
116
117
118
119
120
121
122
123
        
        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:
            self.create_or_update_dataset(dataset)
            self.create_or_update_activity(dataset.activity)
            self.associate_dataset_with_image(dataset.identifier, image.identifier,
                                              activity_id=dataset.activity.identifier)
abuddenberg's avatar
abuddenberg committed
124
        return resp
125

126
127
128
    @check_image
    def update_image(self, image):
        update_url = '{b}/image/{img}'.format(b=self.base_url, img=image.identifier)
129
        for dataset in image.datasets:
abuddenberg's avatar
abuddenberg committed
130
131
132
            self.update_activity(dataset.activity)
            self.associate_dataset_with_image(dataset.identifier, image.identifier,
                                              activity_id=dataset.activity.identifier)
133

abuddenberg's avatar
abuddenberg committed
134
        return requests.post(update_url, data=image.as_json(), headers=self.headers, verify=False)
135

136
    @check_image
137
    @http_resp
138
139
    def delete_image(self, image):
        delete_url = '{b}/image/{img}'.format(b=self.base_url, img=image.identifier)
140
        return requests.delete(delete_url, headers=self.headers, verify=False)
141

142
    @http_resp
143
144
    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)
abuddenberg's avatar
abuddenberg committed
145
        return requests.post(url, data=json.dumps({'add_image_identifier': image_id}), headers=self.headers, verify=False)
146

147
    @http_resp
148
    def upload_image_file(self, image_id, local_path):
149
        url = '{b}/image/files/{id}/{fn}'.format(b=self.base_url, id=image_id, fn=basename(local_path))
150
151
        # For future multi-part encoding support
        # return requests.put(url, headers=headers, files={'file': (filename, open(filepath, 'rb'))})
152
153
154
        if not exists(local_path):
            raise Exception('File not found: ' + local_path)

155
        return requests.put(url, data=open(local_path, 'rb'), headers=self.headers, verify=False)
156

157
158
159
    #Full listing
    def get_figure_listing(self, report_id, chapter_id=None):
        chapter_filter = '/chapter/' + chapter_id if chapter_id else ''
160

161
162
163
        url = '{b}/report/{rpt}{chap}/figure?{p}'.format(
            b=self.base_url, rpt=report_id, chap=chapter_filter, p=urllib.urlencode({'all': '1'})
        )
164
        resp = requests.get(url, headers=self.headers, verify=False)
165

166
167
168
169
        try:
            return [Figure(figure) for figure in resp.json()]
        except ValueError:
            raise Exception('Add a better exception string here')
170

171
172
    def get_figure(self, report_id, figure_id, chapter_id=None):
        chapter_filter = '/chapter/' + chapter_id if chapter_id else ''
173

174
175
176
        url = '{b}/report/{rpt}{chap}/figure/{fig}?{p}'.format(
            b=self.base_url, rpt=report_id, chap=chapter_filter, fig=figure_id, p=urllib.urlencode({'all': '1'})
        )
177
        resp = requests.get(url, headers=self.headers, verify=False)
178

179
180
181
182
        try:
            return Figure(resp.json())
        except ValueError:
            raise Exception(resp.text)
183

184
185
186
187
188
189
190
    @exists
    def figure_exists(self, report_id, figure_id, chapter_id=None):
        chapter_filter = '/chapter/' + chapter_id if chapter_id else ''

        url = '{b}/report/{rpt}{chap}/figure/{fig}?{p}'.format(
            b=self.base_url, rpt=report_id, chap=chapter_filter, fig=figure_id, p=urllib.urlencode({'all': '1'})
        )
191
        return requests.head(url, headers=self.headers, verify=False)
192

193
194
    def get_image(self, image_id):
        url = '{b}/image/{img}'.format(b=self.base_url, img=image_id)
195
        resp = requests.get(url, headers=self.headers, verify=False)
196

197
198
199
200
201
202
203
204
        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)
205
        return requests.head(url, headers=self.headers, verify=False)
206

207
208
    def has_all_associated_images(self, report_id, figure_id, target_image_ids):
        try:
209
210
211
212
            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()
213

214
215
216
217
218
219
        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
220
        else:
221
            return False, deltas
222

223
224
    def test_login(self):
        url = '{b}/login.json'.format(b=self.base_url)
225
        resp = requests.get(url, headers=self.headers, verify=False)
226
227
228
229
        return resp.status_code, resp.text

    def get_keyword_listing(self):
        url = '{b}/gcmd_keyword?{p}'.format(b=self.base_url, p=urllib.urlencode({'all': '1'}))
230
        resp = requests.get(url, headers=self.headers, verify=False)
231

232
233
234
235
        return resp.json()

    def get_keyword(self, key_id):
        url = '{b}/gcmd_keyword/{k}'.format(b=self.base_url, k=key_id)
236
        return requests.get(url, headers=self.headers, verify=False).json()
237
238
239

    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)
240
        return requests.post(url, data=json.dumps({'identifier': keyword_id}), headers=self.headers, verify=False)
241
242
243

    def get_dataset(self, dataset_id):
        url = '{b}/dataset/{ds}'.format(b=self.base_url, ds=dataset_id)
244
        resp = requests.get(url, headers=self.headers, verify=False)
245
246
247
248
249
        try:
            return Dataset(resp.json())
        except ValueError:
            raise Exception(resp.text())

250
251
252
    @exists
    def dataset_exists(self, dataset_id):
        url = '{b}/dataset/{ds}'.format(b=self.base_url, ds=dataset_id)
253
        return requests.head(url, headers=self.headers, verify=False)
254

abuddenberg's avatar
abuddenberg committed
255
    def create_dataset(self, dataset):
256
        url = '{b}/dataset/'.format(b=self.base_url)
257
        return requests.post(url, data=dataset.as_json(), headers=self.headers, verify=False)
258
259
260

    def update_dataset(self, dataset):
        url = '{b}/dataset/{ds}'.format(b=self.base_url, ds=dataset.identifier)
261
        return requests.post(url, data=dataset.as_json(), headers=self.headers, verify=False)
262
263
264

    def delete_dataset(self, dataset):
        url = '{b}/dataset/{ds}'.format(b=self.base_url, ds=dataset.identifier)
265
        return requests.delete(url, headers=self.headers, verify=False)
266

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

270
271
272
273
        data = {
            'parent_uri': '/dataset/' + dataset_id,
            'parent_rel': 'prov:wasDerivedFrom'
        }
abuddenberg's avatar
abuddenberg committed
274
275
276
        if activity_id:
            data['activity'] = activity_id

277
278
279
280
281
282
        try:
            self.delete_dataset_image_assoc(dataset_id, image_id)
        except AssociationException as e:
            print e.value

        resp = requests.post(url, data=json.dumps(data), headers=self.headers, verify=False)
283
284
285
286

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

abuddenberg's avatar
abuddenberg committed
289
290
291
292
293
294
295
296
297
    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'
            }
        }
298
        resp = requests.post(url, data=json.dumps(data), headers=self.headers, verify=False)
abuddenberg's avatar
abuddenberg committed
299
300
301
302

        if resp.status_code == 200:
            return resp
        else:
303
304
305
306
307
308
            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
309
            self.update_dataset(dataset)
310
311
        else:
            print 'Creating dataset: ' + dataset.identifier
312
            self.create_dataset(dataset)
313

abuddenberg's avatar
abuddenberg committed
314
315
316
317

    # @exists
    def activity_exists(self, activity_id):
        url = '{b}/activity/{act}'.format(b=self.base_url, act=activity_id)
318
        resp = requests.head(url, headers=self.headers, verify=False)
abuddenberg's avatar
abuddenberg committed
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
        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)
        resp = requests.get(url, headers=self.headers, verify=False)
        try:
            return Activity(resp.json())
        except ValueError:
            raise Exception(resp.text())

    @http_resp
    def create_activity(self, activity):
        url = '{b}/activity/'.format(b=self.base_url)
335
        return requests.post(url, data=activity.as_json(), headers=self.headers, verify=False)
abuddenberg's avatar
abuddenberg committed
336
337
338
339

    @http_resp
    def update_activity(self, activity):
        url = '{b}/activity/{act}'.format(b=self.base_url, act=activity.identifier)
340
        return requests.post(url, data=activity.as_json(), headers=self.headers, verify=False)
abuddenberg's avatar
abuddenberg committed
341
342
343
344

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

347
348
349
350
351
    def create_or_update_activity(self, activity):
        if self.activity_exists(activity.identifier):
            self.update_activity(activity)
        else:
            self.create_activity(activity)