Skip to content
queries.py 7.19 KiB
Newer Older
Franziska Koehn's avatar
Franziska Koehn committed
"""
:Author: Franziska Koehn
:Created: 2015/01/13

This module includes functions around sending and defining queries.
Franziska Koehn's avatar
Franziska Koehn committed

to query the xnat server define:

one string including one Root-Type like:

.. sourcecode:: python

   'xnat:mrScanData'

one list including the search-constraints like:

.. code-block:: python

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

a list of search-fields like:

.. code-block:: python

    [ 'xnat:mrScanData/TYPE','xnat:mrScanData/ID']

Franziska Koehn's avatar
Franziska Koehn committed
"""

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

Franziska Koehn's avatar
Franziska Koehn committed
    """Returns all applicable methods for creating a query."""
Franziska Koehn's avatar
Franziska Koehn committed
    """Returns all applicable Operators for creating a query."""
Franziska Koehn's avatar
Franziska Koehn committed

def download_async(result, host, creds, rest, dest_folder='', cb=(lambda *_: None), cb_args=()):
Franziska Koehn's avatar
Franziska Koehn committed
    """
Franziska Koehn's avatar
Franziska Koehn committed
    Downloads file by using threads (So the GUI will not be blocked while downloading).
Franziska Koehn's avatar
Franziska Koehn committed

    **Parameters**
Franziska Koehn's avatar
Franziska Koehn committed
        :result: dict, resultset from which will be downloaded
        :host: str, address of host
        :creds: tuple (username, userpassword), credentials (including user-name and -password)
        :rest: str, REST-API definition
        :dest_folder: str, folder where the downloaded files will be saved
        :cb: function, that will be called, when download is finished with the arguments from cb_args
Franziska Koehn's avatar
Franziska Koehn committed
        :cb_args: args for function cb
    """
Franziska Koehn's avatar
Franziska Koehn committed
    from threading import Thread
    download_thread = Thread(target=download, args=(result, host, creds, rest, dest_folder, cb, cb_args))
Franziska Koehn's avatar
Franziska Koehn committed
    download_thread.start()
    return download_thread
Franziska Koehn's avatar
Franziska Koehn committed

def download(result, host, creds, rest, dest_folder='', cb=(lambda *_: None), cb_args=()):
Franziska Koehn's avatar
Franziska Koehn committed
    """
Franziska Koehn's avatar
Franziska Koehn committed
    Downloads a Result.
Franziska Koehn's avatar
Franziska Koehn committed

    **Parameters**
Franziska Koehn's avatar
Franziska Koehn committed
        :result: dict, resultset from which will be downloaded
        :host: str, address of host
        :creds: tuple (username, userpassword), credentials (including user-name and -password)
        :rest: str, REST-API definition
        :dest_folder: str, folder where the downloaded files will be saved
        :cb: function, that will be called, when download is finished with the arguments from cb_args
Franziska Koehn's avatar
Franziska Koehn committed
        :cb_args: args for function cb
    """

Franziska Koehn's avatar
Franziska Koehn committed
    requests_lock.acquire()

    import re
    import os

    names=[]
    def subfunc(f):
        key = f.group(0).strip("{}")
        r = result[key]
        names.append(r)
        return r
    ret = re.sub('\{\w+\}', subfunc, rest)

    path = os.path.join(dest_folder, '-'.join(names))
    url = "%s%s" % (host, ret)

    download_file(url, creds, path)
Franziska Koehn's avatar
Franziska Koehn committed
    requests_lock.release()
    cb(*cb_args)
def download_file(url, creds, path):
Franziska Koehn's avatar
Franziska Koehn committed
    """
Franziska Koehn's avatar
Franziska Koehn committed
    Downloads a file from given 'url' and saves it to 'path'.

    Returns True if download was finished without problems
    Returns False if download raised an exception.
Franziska Koehn's avatar
Franziska Koehn committed

    **Parameters**
Franziska Koehn's avatar
Franziska Koehn committed
        :url: str, host/REST-API with filled values
        :creds: tuple(user-name, user-password), credentials
        :path: str, folder where the downloaded files will be saved
Franziska Koehn's avatar
Franziska Koehn committed
    """

    import requests
    from base64 import b64encode
    from requests.auth import HTTPBasicAuth

    if not path.endswith(".zip"):
        path += ".zip"
        with open(path, '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)
Franziska Koehn's avatar
Franziska Koehn committed
        return True
    except IOError as e:
        print "Error writing file %s, %s" % (path, e)
    except ValueError as e:
        print "Error downloading file %s, Status Code %s" % (url, e)
Franziska Koehn's avatar
Franziska Koehn committed
    return False
def get_xnat_server_connection(force=False, host="", user="", passw=""):
    from pyxnat import Interface
    from tempfile import mkdtemp
    import xsa.errors

    if get_xnat_server_connection.cache and not force:
        return get_xnat_server_connection.cache
    elif force and host=="":
        raise xsa.errors.ServerNotFoundError("The host was not defined.")
    elif force and (user=="" or passw==""):
        raise xsa.errors.UnauthorizedError("The user-name and/or the user-password were not defined.")

    disconnect_xnat_server()
    try:
        get_xnat_server_connection.cache = Interface(server=host, user=user, password=passw, cachedir=mkdtemp())
    except IndexError as e:
        raise xsa.errors.ServerNotFoundError("Server not found, check your host-address.")
    return get_xnat_server_connection()
get_xnat_server_connection.cache = None

def disconnect_xnat_server():
    import xsa.errors
    try:
        get_xnat_server_connection.cache.disconnect()
        get_xnat_server_connection.cache = None
    except:
       return

def query_for_additional_data(rest, result, host, user, passw):

    import re
    def subfunc(f):
        key = f.group(0).strip("{}")
Franziska Koehn's avatar
Franziska Koehn committed
        return result[key]
    ret = re.sub('\{\w+\}', subfunc, rest)

    central = get_xnat_server_connection(host=host, user=user, passw=passw)
    exp = central.select(ret)

    new_fields = exp.xpath("//xnat:field")
    new_result = result.copy()
    for f in new_fields:
        fieldname = f.get("name")
        value = exp.xpath("//xnat:field[@name='"+fieldname+"']/child::text()")
        new_result[fieldname] = ''.join(value).replace("\n","")
    return new_result


def search_for(host, root_element, constraints, search_fields, user, passw):
Franziska Koehn's avatar
Franziska Koehn committed
    """
    Does a search for given values. raises xsa -Exceptions

    **Parameters**
Franziska Koehn's avatar
Franziska Koehn committed
        :host: str, host-address
        :root_element: str, Root-Type of search
        :constraints: list, constraints of query
        :search_fields: list of str, fields which will be returned from server
        :user: str, user-name
        :passw: str, user-password

    **Raises**
        :xsa.errors.ServerNotFoundError:
        :xsa.errors.UnauthorizedError:
        :xsa.errors.ResponseNotReady:
        :xsa.errors.QueryError:

Franziska Koehn's avatar
Franziska Koehn committed
    """
Franziska Koehn's avatar
Franziska Koehn committed
    import xsa.errors
    from pyxnat.core import errors
    from httplib2 import ServerNotFoundError
    from httplib import ResponseNotReady

    from tempfile import mkdtemp
    central = get_xnat_server_connection(host=host, user=user, passw=passw)
    result = []

    try:
        result =  central.select(root_element,search_fields).where(constraints)
    except errors.DatabaseError as e:
        if '401' in str(e):
Franziska Koehn's avatar
Franziska Koehn committed
            raise xsa.errors.UnauthorizedError("Unauthorizied attempt. Check your User and Password")
        else:
            raise e
    except ServerNotFoundError:
Franziska Koehn's avatar
Franziska Koehn committed
        raise xsa.errors.ServerNotFoundError("Server not found, check your host-address.")
    except ResponseNotReady:
Franziska Koehn's avatar
Franziska Koehn committed
        raise xsa.errors.ResponseNotReady("Please check your Host-Address")

    if result == []:
Franziska Koehn's avatar
Franziska Koehn committed
        raise xsa.errors.QueryError("Please check your query.")