xnat_search.py 7.49 KB
Newer Older
1
HOST = None
2

Franziska Koehn's avatar
Franziska Koehn committed
3
4
5
from threading import Lock
requests_lock = Lock()

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def main(host):
    import distutils.core

    root_element = 'xnat:mrScanData'
    constraints = [
        ('xnat:mrScanData/TYPE', 'LIKE', '%t1%'),
        ('xnat:mrScanData/PARAMETERS_FLIP', '>=', '10'),
        'AND',
        [('xnat:mrScanData/PARAMETERS_TE', '>', '2.0'),
         ('xnat:mrScanData/PARAMETERS_TE', '<', '2.0'),
         'OR'
         ]
        ]



    results = search_for(host, root_element, constraints)

    print "Search results (%s):" % len(results)
    print results

    while True:
        try:
            is_downl = distutils.util.strtobool(raw_input("download? "))
            break
        except ValueError:
            print "invalid character. Please use y or n."

    if is_downl:
        print "downloading files..."
        download_all(results, host)
        print "finished"
    else:
        print "download canceled"

41
42
43
44
45
46
47
48
49
50
def read_json_from_file():
    pass

def get_data_types():
    from os import listdir
    from os.path import isfile, join
    import json

    DIR = 'datatypes/'
    files = [ f for f in listdir(DIR) if isfile(join(DIR,f)) ]
51
    result = []
52
53
54
    for file in files:
        with open(join(DIR,file)) as f:
            try:
55
56
                data=json.loads(f.read())['root-type']
                result.append(data)
57
            except ValueError:
58
59
60
61
62
63
64
65
66
67
                pass
    return result

def get_field_labels_of_type(type):
    result = []
    fields = get_fields_of_type(type)
    if fields is not None:
        for f in fields:
            result.append(f['label'])
    return result
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

def get_fields_of_type(type):
    from os import listdir
    from os.path import isfile, join
    import json

    DIR = 'datatypes/'
    files = [ f for f in listdir(DIR) if isfile(join(DIR,f)) ]
    for file in files:
        with open(join(DIR,file)) as f:
            try:
                t = json.loads(f.read())
                if t['root-type'] == type:
                    return t['fields']
            except ValueError:
                pass

Franziska Koehn's avatar
Franziska Koehn committed
85
86
87
88
89
90
91
92
93
94
95
def get_fields_from_labels(labels, type):
    result=[]
    fields = get_fields_of_type(type)
    if fields is not None:
        for l in labels:
            for f in fields:
                if f['label'] == l:
                    result.append(f['field'])
                    break
    return result

96
def get_search_fields():
97
98
99
100
101
102
103
104
105
106
107
108
    import json
    return json.loads("""[{"label":"type", "field":"xnat:mrScanData/TYPE"},
            {"label":"fov_x", "field":"xnat:mrScanData/PARAMETERS_FOV_X"},
            {"label":"fov_y", "field":"xnat:mrScanData/PARAMETERS_FOV_Y"},
            {"label":"tr", "field": "xnat:mrScanData/PARAMETERS_TR"},
            {"label":"te", "field": "xnat:mrScanData/PARAMETERS_TE"},
            {"label":"ti", "field": "xnat:mrScanData/PARAMETERS_TI"},
            {"label":"flip", "field": "xnat:mrScanData/PARAMETERS_FLIP"},
            {"label":"voxel_res_x", "field":"xnat:mrScanData/PARAMETERS_VOXELRES_X"},
            {"label":"voxel_res_y", "field":"xnat:mrScanData/PARAMETERS_VOXELRES_Y"},
            {"label":"voxel_res_z", "field":"xnat:mrScanData/PARAMETERS_VOXELRES_Z"}
            ]""")
109
110
111
112
113
114
115

def get_query_methods():
    return ["AND", "OR"]

def get_operators():
    return ["LIKE", ">", "<", "<=", ">=", "="]

116
def download_all(results, host=None):
117
    for r in results:
118
119
        download(r, host)

120
def download_async(result, dest_folder='', host=None, cb=(lambda *_: None), cb_args=()):
Franziska Koehn's avatar
Franziska Koehn committed
121
    from threading import Thread
122
    download_thread = Thread(target=download, args=(result, dest_folder, host, cb, cb_args))
Franziska Koehn's avatar
Franziska Koehn committed
123
124
    download_thread.start()
    return download_thread
Franziska Koehn's avatar
Franziska Koehn committed
125

126
def download(result, dest_folder='', host=None, cb=(lambda *_: None), cb_args=()):
127
128
    if not host:
        host = HOST
Franziska Koehn's avatar
Franziska Koehn committed
129
    requests_lock.acquire()
130
131
132
133
134
    print(result)
    subject = result['xnat_mrsessiondata_subject_id']
    experiment = result['xnat_mrsessiondata_session_id']
    project = result['xnat_mrsessiondata_project']
    scan = result['id']
135
    file_name = "%s/%s-%s-%s-%s" % (dest_folder, project, subject, experiment, scan)
136
    download_file(host, project, subject, experiment, scan, file_name)
Franziska Koehn's avatar
Franziska Koehn committed
137
138
    requests_lock.release()
    cb(*cb_args)
139

140
def search_for(host, root_element, constraints, result_fields, user, passw):
141
    from pyxnat import Interface
142
    from pyxnat.core import errors
143
    from tempfile import mkdtemp
144
    from httplib2 import ServerNotFoundError
145

146
147
    get_credentials(username=user, password=passw, force=True)

148
149
    user, passw = get_credentials()
    tmp_dir=mkdtemp()
150
151
152
153
154
155
156
157

    try:
        central = Interface(server="%s" % host,
                  user=user,
                  password=passw,
                  cachedir=tmp_dir)
    except IndexError:
        return "ServerNotFoundError"    #TODO Eigene Fehlerklasse
158

159
    main_fields = [
160
161
162
163
164
165
166
        'xnat:mrSessionData/PROJECT',       #project id
        'xnat:mrSessionData/SUBJECT_ID',    #subject id
        'xnat:mrSessionData/SESSION_ID',    #experiment id
        'xnat:mrScanData/ID',               #scan id
        'xnat:mrScanData/TYPE',             #scan type
        ]

167
168
    search_fields = main_fields+result_fields

169
170
171
172
173
174
175
176
    result = []

    try:
        result =  central.select(root_element,search_fields).where(constraints)
    except errors.DatabaseError:
        return "DatabaseError"   #TODO Eigene Fehlerklasse
    except ServerNotFoundError:
        return "ServerNotFoundError"    #TODO Eigene Fehlerklasse
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244

    try:
        central.disonnect()
    except AttributeError:
        print "can\'t close connection (wrong pyxnat version?)"

    try:
        from shutil import rmtree
        rmtree(tmp_dir)
    except:
        pass
    return result


def get_credentials(force=False,username="", password="" ):
    if get_credentials.cache and not force:
        return get_credentials.cache
    from getpass import getpass
    while not len(username):
        username = raw_input("Username: ").strip()
    while not len(password):
        password = getpass().strip()
    get_credentials.cache = username, password
    return get_credentials()
get_credentials.cache = None

def download_file(host, project, subject, experiment, scan, file_name):
    import requests
    from base64 import b64encode
    from requests.auth import HTTPBasicAuth

    if not file_name.endswith(".zip"):
        file_name += ".zip"

    url= "%s/data/archive/projects/%s/subjects/%s/experiments/%s/scans/%s/resources/DICOM/files?format=zip" % (host, project, subject, experiment, scan)

    user, passw = get_credentials()

    try:
        with open(file_name, 'wb') as handle:
            response = requests.get(url, stream=True, auth=HTTPBasicAuth(user, passw))
            if not response.ok:
                raise ValueError(response.status_code)
            for block in response.iter_content(1024):
                if not block:
                    break
                handle.write(block)
    except IOError as e:
        print "Error writing file %s, %s" % (file_name, e)
    except ValueError as e:
        print "Error downloading file %s, Status Code %s" % (url, e)

if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(description='search and download')
    parser.add_argument('--host', type=str, help='hostname or ip-address (including port).', default="localhost:8080")
    parser.add_argument('--user', type=str, help='user:passw', default=":")

    #TODO force download (boolean)
    #TODO read constraints und root_element from file
    #TODO GUI

    args = parser.parse_args()

    u,p = args.user.split(':')
    get_credentials(username=u, password=p)
    main(args.host)