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

import requests

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


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

    return wrapped


22
23
24
25
26
27
28
29
30
31
32
33
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
34
35
36
37
38
39
40
41
42
43
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


44
class GcisClient(object):
45
    def __init__(self, url, username, password):
46
47
48
49
        self.headers = {
            'Accept': 'application/json'
        }

50
51
        self.base_url = url
        self.headers['Authorization'] = 'Basic ' + b64encode(username + ':' + password)
52

53
54
    def create_figure(self, report_id, chapter_id, figure, skip_images=False):
        if figure.identifier in (None, ''):
55
56
57
58
59
            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
60

61
62
        url = '{b}/report/{rpt}/chapter/{chp}/figure/'.format(
            b=self.base_url, rpt=report_id, chp=chapter_id
63
64
        )

65
66
67
68
69
70
        resp = requests.post(url, data=figure.as_json(), headers=self.headers)

        if resp.status_code != 200:
            raise Exception(resp.text)

        if skip_images is False:
71
            for image in figure.images:
72
73
                self.create_image(image),
                self.associate_image_with_figure(image.identifier, report_id, figure.identifier)
74

75
        return resp
76

77
    def update_figure(self, report_id, chapter_id, figure, skip_images=False):
78
79
        if figure.identifier in (None, ''):
            raise Exception('Invalid identifier', figure.identifier)
80
81
82
83
84

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

85
        update_url = '{b}/report/{rpt}/chapter/{chp}/figure/{fig}'.format(
86
            b=self.base_url, rpt=report_id, chp=chapter_id, fig=figure.identifier
87
88
        )

89
90
91
92
        resp = requests.post(update_url, figure.as_json(), headers=self.headers)

        if resp.status_code != 200:
            raise Exception(resp.text)
93

94
        if skip_images is False:
95
            for image in figure.images:
96
97
98
                self.update_image(image)

        return resp
99
100
101
102

    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)
        return requests.delete(url, headers=self.headers)
103

104
    @check_image
105
    def create_image(self, image, report_id=None, figure_id=None):
106
        url = '{b}/image/'.format(b=self.base_url, img=image.identifier)
abuddenberg's avatar
abuddenberg committed
107
        resp = requests.post(url, image.as_json(), headers=self.headers)
108
        if image.local_path is not None:
abuddenberg's avatar
abuddenberg committed
109
            self.upload_image_file(image.identifier, image.local_path)
110
        if figure_id and report_id:
abuddenberg's avatar
abuddenberg committed
111
            self.associate_image_with_figure(image.identifier, report_id, figure_id)
112
        for dataset in image.datasets:
abuddenberg's avatar
abuddenberg committed
113
114
115
            self.create_activity(dataset.activity)
            self.associate_dataset_with_image(dataset.identifier, image.identifier,
                                              activity_id=dataset.activity.identifier)
116

abuddenberg's avatar
abuddenberg committed
117
        return resp
118

119
120
121
    @check_image
    def update_image(self, image):
        update_url = '{b}/image/{img}'.format(b=self.base_url, img=image.identifier)
122
        for dataset in image.datasets:
abuddenberg's avatar
abuddenberg committed
123
124
125
            self.update_activity(dataset.activity)
            self.associate_dataset_with_image(dataset.identifier, image.identifier,
                                              activity_id=dataset.activity.identifier)
126

127
        return requests.post(update_url, image.as_json(), headers=self.headers)
128

129
130
131
132
    @check_image
    def delete_image(self, image):
        delete_url = '{b}/image/{img}'.format(b=self.base_url, img=image.identifier)
        return requests.delete(delete_url, headers=self.headers)
133

134
135
136
    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)
        return requests.post(url, json.dumps({'add_image_identifier': image_id}), headers=self.headers)
137

138
    def upload_image_file(self, image_id, local_path):
139
        url = '{b}/image/files/{id}/{fn}'.format(b=self.base_url, id=image_id, fn=basename(local_path))
140
141
        # For future multi-part encoding support
        # return requests.put(url, headers=headers, files={'file': (filename, open(filepath, 'rb'))})
142
143
144
145
        if not exists(local_path):
            raise Exception('File not found: ' + local_path)

        return requests.put(url, data=open(local_path, 'rb'), headers=self.headers)
146

147
148
149
    #Full listing
    def get_figure_listing(self, report_id, chapter_id=None):
        chapter_filter = '/chapter/' + chapter_id if chapter_id else ''
150

151
152
153
154
        url = '{b}/report/{rpt}{chap}/figure?{p}'.format(
            b=self.base_url, rpt=report_id, chap=chapter_filter, p=urllib.urlencode({'all': '1'})
        )
        resp = requests.get(url, headers=self.headers)
155

156
157
158
159
        try:
            return [Figure(figure) for figure in resp.json()]
        except ValueError:
            raise Exception('Add a better exception string here')
160

161
162
    def get_figure(self, report_id, figure_id, chapter_id=None):
        chapter_filter = '/chapter/' + chapter_id if chapter_id else ''
163

164
165
166
167
        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'})
        )
        resp = requests.get(url, headers=self.headers)
168

169
170
171
172
        try:
            return Figure(resp.json())
        except ValueError:
            raise Exception(resp.text)
173

174
175
176
177
178
179
180
181
182
    @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'})
        )
        return requests.head(url, headers=self.headers)

183
184
    def get_image(self, image_id):
        url = '{b}/image/{img}'.format(b=self.base_url, img=image_id)
185
        resp = requests.get(url, headers=self.headers)
186

187
188
189
190
191
192
193
194
195
        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)
        return requests.head(url, headers=self.headers)
196

197
198
    def has_all_associated_images(self, report_id, figure_id, target_image_ids):
        try:
199
200
201
202
            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()
203

204
205
206
207
208
209
        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
210
        else:
211
            return False, deltas
212

213
214
    def test_login(self):
        url = '{b}/login.json'.format(b=self.base_url)
215
        resp = requests.get(url, headers=self.headers, verify=False)
216
217
218
219
220
        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'}))
        resp = requests.get(url, headers=self.headers)
221

222
223
224
225
226
227
228
229
        return resp.json()

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

    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)
230
231
232
233
        return requests.post(url, data=json.dumps({'identifier': keyword_id}), headers=self.headers)

    def get_dataset(self, dataset_id):
        url = '{b}/dataset/{ds}'.format(b=self.base_url, ds=dataset_id)
234
        resp = requests.get(url, headers=self.headers, verify=False)
235
236
237
238
239
        try:
            return Dataset(resp.json())
        except ValueError:
            raise Exception(resp.text())

240
241
242
243
244
    @exists
    def dataset_exists(self, dataset_id):
        url = '{b}/dataset/{ds}'.format(b=self.base_url, ds=dataset_id)
        return requests.head(url, headers=self.headers)

abuddenberg's avatar
abuddenberg committed
245
    def create_dataset(self, dataset):
246
247
248
249
250
251
252
253
254
255
        url = '{b}/dataset/'.format(b=self.base_url)
        return requests.post(url, data=dataset.as_json(), headers=self.headers)

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

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

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

260
261
262
263
        data = {
            'parent_uri': '/dataset/' + dataset_id,
            'parent_rel': 'prov:wasDerivedFrom'
        }
abuddenberg's avatar
abuddenberg committed
264
265
266
267
        if activity_id:
            data['activity'] = activity_id

        self.delete_dataset_image_assoc(dataset_id, image_id)
268
        resp = requests.post(url, data=json.dumps(data), headers=self.headers)
269
270
271
272

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

abuddenberg's avatar
abuddenberg committed
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
    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'
            }
        }
        resp = requests.post(url, data=json.dumps(data), headers=self.headers)

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

    # @exists
    def activity_exists(self, activity_id):
        url = '{b}/activity/{act}'.format(b=self.base_url, act=activity_id)
        resp = requests.head(url, headers=self.headers)
        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)
        return requests.post(url, data=activity.as_json(), headers=self.headers)

    @http_resp
    def update_activity(self, activity):
        url = '{b}/activity/{act}'.format(b=self.base_url, act=activity.identifier)
        return requests.post(url, data=activity.as_json(), headers=self.headers)

    @http_resp
    def delete_activity(self, activity):
        url = '{b}/activity/{act}'.format(b=self.base_url, act=activity.identifier)
        return requests.delete(url, headers=self.headers)