gcis_client.py 9.79 KB
Newer Older
1
2
#!/usr/bin/python

3
from base64 import b64encode
4
5
import urllib
import json
abuddenberg's avatar
abuddenberg committed
6
import requests
7
from os.path import exists, basename
8
from domain import Figure, Image, Dataset
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


34
class GcisClient(object):
35
    def __init__(self, url, username, password):
36
37
38
39
        self.headers = {
            'Accept': 'application/json'
        }

40
41
        self.base_url = url
        self.headers['Authorization'] = 'Basic ' + b64encode(username + ':' + password)
42

43
44
    def create_figure(self, report_id, chapter_id, figure, skip_images=False):
        if figure.identifier in (None, ''):
45
46
47
48
49
            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
50

51
52
        url = '{b}/report/{rpt}/chapter/{chp}/figure/'.format(
            b=self.base_url, rpt=report_id, chp=chapter_id
53
54
        )

55
56
57
58
59
60
        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:
61
            for image in figure.images:
62
63
                self.create_image(image),
                self.associate_image_with_figure(image.identifier, report_id, figure.identifier)
64

65
        return resp
66

67
    def update_figure(self, report_id, chapter_id, figure, skip_images=False):
68
69
        if figure.identifier in (None, ''):
            raise Exception('Invalid identifier', figure.identifier)
70
71
72
73
74

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

75
        update_url = '{b}/report/{rpt}/chapter/{chp}/figure/{fig}'.format(
76
            b=self.base_url, rpt=report_id, chp=chapter_id, fig=figure.identifier
77
78
        )

79
80
81
82
        resp = requests.post(update_url, figure.as_json(), headers=self.headers)

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

84
        if skip_images is False:
85
            for image in figure.images:
86
87
88
                self.update_image(image)

        return resp
89
90
91
92

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

94
    @check_image
95
    def create_image(self, image, report_id=None, figure_id=None):
96
97
        url = '{b}/image/'.format(b=self.base_url, img=image.identifier)
        responses = [requests.post(url, image.as_json(), headers=self.headers)]
98
99
        if image.local_path is not None:
            responses.append(self.upload_image_file(image.identifier, image.local_path))
100
101
        if figure_id and report_id:
            responses.append(self.associate_image_with_figure(image.identifier, report_id, figure_id))
102
103
        for dataset in image.datasets:
            self.associate_dataset_with_image(dataset.identifier, image.identifier)
104
105

        return responses
106

107
108
109
    @check_image
    def update_image(self, image):
        update_url = '{b}/image/{img}'.format(b=self.base_url, img=image.identifier)
110
111
112
        for dataset in image.datasets:
            self.associate_dataset_with_image(dataset.identifier, image.identifier)

113
        return requests.post(update_url, image.as_json(), headers=self.headers)
114

115
116
117
118
    @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)
119

120
121
122
    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)
123

124
    def upload_image_file(self, image_id, local_path):
125
        url = '{b}/image/files/{id}/{fn}'.format(b=self.base_url, id=image_id, fn=basename(local_path))
126
127
        # For future multi-part encoding support
        # return requests.put(url, headers=headers, files={'file': (filename, open(filepath, 'rb'))})
128
129
130
131
        if not exists(local_path):
            raise Exception('File not found: ' + local_path)

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

133
134
135
    #Full listing
    def get_figure_listing(self, report_id, chapter_id=None):
        chapter_filter = '/chapter/' + chapter_id if chapter_id else ''
136

137
138
139
140
        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)
141

142
143
144
145
        try:
            return [Figure(figure) for figure in resp.json()]
        except ValueError:
            raise Exception('Add a better exception string here')
146

147
148
    def get_figure(self, report_id, figure_id, chapter_id=None):
        chapter_filter = '/chapter/' + chapter_id if chapter_id else ''
149

150
151
152
153
        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)
154

155
156
157
158
        try:
            return Figure(resp.json())
        except ValueError:
            raise Exception(resp.text)
159

160
161
162
163
164
165
166
167
168
    @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)

169
170
    def get_image(self, image_id):
        url = '{b}/image/{img}'.format(b=self.base_url, img=image_id)
171
        resp = requests.get(url, headers=self.headers)
172

173
174
175
176
177
178
179
180
181
        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)
182

183
184
    def has_all_associated_images(self, report_id, figure_id, target_image_ids):
        try:
185
186
187
188
            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()
189

190
191
192
193
194
195
        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
196
        else:
197
            return False, deltas
198

199
200
    def test_login(self):
        url = '{b}/login.json'.format(b=self.base_url)
201
        resp = requests.get(url, headers=self.headers, verify=False)
202
203
204
205
206
        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)
207

208
209
210
211
212
213
214
215
        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)
216
217
218
219
        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)
220
        resp = requests.get(url, headers=self.headers, verify=False)
221
222
223
224
225
        try:
            return Dataset(resp.json())
        except ValueError:
            raise Exception(resp.text())

226
227
228
229
230
    @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
231
    def create_dataset(self, dataset):
232
233
234
235
236
237
238
239
240
241
        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)
242

243
244
245
246
247
248
249
    def associate_dataset_with_image(self, dataset_id, image_id):
        url = '{b}/image/prov/{img}'.format(b=self.base_url, img=image_id)
        data = {
            'parent_uri': '/dataset/' + dataset_id,
            'parent_rel': 'prov:wasDerivedFrom'
        }
        resp = requests.post(url, data=json.dumps(data), headers=self.headers)
250
251
252
253
254
255
256
257

        if resp.status_code == 200:
            return resp
        #TODO: Change to 409 in next release
        elif resp.status_code == 400:
            print 'Duplicate dataset association {ds} for image: {img}'.format(ds=dataset_id, img=image_id)
            return resp
        else:
258
            raise Exception('Dataset association failed:\n{url}\n{resp}'.format(url=url, resp=resp.text))
259