Commit 82a1a868 authored by Franziska Koehn's avatar Franziska Koehn
Browse files

final refactor

parent fed770d6
......@@ -3,14 +3,30 @@ ______________
This module contains all logical functions.
xsa.datatypereader
------------------
xsa.queries
-----------
Module: :mod:`xsa.queries`
--------------------------
xsa.chart
---------
.. automodule:: xsa.queries
:members:
xsa.errors
----------
Module: :mod:`xsa.chart`
------------------------
.. automodule:: xsa.chart
:members:
Module: :mod:`xsa.errors`
-------------------------
.. automodule:: xsa.errors
:members:
Module: :mod:`xsa.datatypereader`
---------------------------------
.. automodule:: xsa.datatypereader
:members:
......@@ -39,7 +39,6 @@ Module: :mod:`xsagtk.main_controller`
:members:
:undoc-members:
:show-inheritance:
:private-members:
:special-members:
:exclude-members: __module__, __dict__
......
......@@ -3,40 +3,42 @@
:Author: Franziska Koehn
:Created: 2015/01/13
This module houses functions relating to creating the chart.
This module houses functions relating to the chart-creation.
"""
def count_substrings(substrings, strings):
"""
Returns a dictionary including the counts of its keys. This keys are the given list of substrings
Returns a dictionary including the counts of its keys.
This keys are the given list of substrings.
**Parameters**
:substrings: list of substrings to be counted
:strings: strings in which the counts of substrings will be determined
:substrings: list of str, list of substrings to be counted
:strings: list of str, strings in which the counts of substrings will be determined
"""
from collections import defaultdict
results = defaultdict(int)
from itertools import product
if strings == []:
return []
for sub in substrings:
results[sub] = 0
for string in strings:
if sub.lower() in string.lower():
results[sub] += 1
results = defaultdict(int)
for sub,string in product(substrings, strings):
results[sub] # create key
if sub.lower() in string.lower():
results[sub] += 1
return list(results.items())
def create(file, data, size, y_max, x_max):
"""
creates diagram for given 'data' and max-values ('y_max', 'x_max') and renders it as a image, saved as 'file' in given 'size'
creates diagram for given 'data' and max-values ('y_max', 'x_max') and
renders it as an image, saved as 'file' in given 'size'
**Parameters**
:file: destination/filename where the rendered image will be saved
:data: dictionary, the key will be shown on the x-axis. the value on the y-axis. if a two-dimensional list will be pass, the values on the first dimension will drawn on the x-axis, the second on the y-axis
:size: size of rendered image
:size: tuple (int,int), size of rendered image
:y_max: max-value for y-axis
:x_max: max-value for x-axis (= count of keys)
"""
......
......@@ -16,10 +16,10 @@ DIR = 'datatypes/'
def get_all(force=False):
"""
Reads all json-files in the given directory "DIR". Ignores corrupted files.
If cache is not None and force is not True, it will return the saved values.
Data maybe retreived from a cache.
**Parameters**
:force: if True, the functions reeds the types new from file and sets saves the new values in the cache.
:force: bool, if True, the cache is ignored and renewed.
"""
if get_all.cache and not force:
return get_all.cache
......@@ -40,71 +40,59 @@ def get_all(force=False):
get_all.cache = None
#TODO rename type to root_type
def get_rest(type):
"""
Returns REST-API-Interface of a given root-type, defined in its json-file. If no REST-API-Interface was defined it will raise a xsa.errors.NoRestApiError.
def get_rest(root_type):
"""Returns REST-API-Interface of a given Root-Type, defined in its json-file.
If no REST-API-Interface was defined it will raise a xsa.errors.NoRestApiError.
**Parameters**
:root_type: str, Root-Type of returned REST-API-Definition
"""
for data in get_all():
if data['root-type'] == type:
if data['root-type'] == root_type:
if not 'REST-API' in data or not data['REST-API']:
raise NoRestApiError("No REST-Inteface was defined for this datatype")
else:
return data['REST-API']
# TODO rename data_types to root_types
def get_data_types():
"""
Returns all Root-Types.
"""
result = []
for data in get_all():
result.append(data['root-type'])
return result
def get_root_types():
"""Returns all Root-Types."""
return list(data['root-type'] for data in get_all())
# TODO rename type to root_types
def get_fields(type):
"""
Returns all fields of a given Root-Type.
def get_fields(root_type):
"""Returns all fields of a given Root-Type.
**Parameters**
:type: Root-Type of returned fields
:root_type: str, Root-Type of returned fields
"""
for data in get_all():
if data['root-type'] == type:
if data['root-type'] == root_type:
return data['fields']
# TODO rename type to root_types
def get_labels(type):
"""
returns a list of labels of fields of a given root_type.
def get_labels(root_type):
"""Returns a list of field-labels of a given root_type.
**Parameters**
:type: Root-Type of fields, their labels will be returned
:root_type: str, Root-Type of returned field-labels
"""
result = []
fields = get_fields(type)
fields = get_fields(root_type)
if fields is not None:
for f in fields:
result.append(f['label'])
return result
return list(f['label'] for f in fields)
return []
# TODO rename type to root_type
def get_field_label_by_key(type, key):
"""
Returns the label of the given key.
def get_field_label_by_key(root_type, field_key):
"""Returns the label of the given field-key.
**Parameters**
:type: Root-Type of given key
:key: key of field, of which the label will be returned
:root_type: str, Root-Type of given key
:field_key: str, field-Key, for which the label will be returned
"""
fields = get_fields(type)
fields = get_fields(root_type)
for f in fields:
if "key" in f and f["key"] == key:
if "key" in f and f["key"] == field_key:
return f["label"]
return key
......@@ -114,8 +102,8 @@ def get_field_label_by_field(root_type, field):
Returns the label of the given field.
**Parameters**
:root_type: Root-Type of given field
:field: field, of which the label will be returned
:root_type: str, Root-Type of given field
:field: str, field for which the label will be returned
"""
fields = get_fields(root_type)
for f in fields:
......@@ -123,39 +111,31 @@ def get_field_label_by_field(root_type, field):
return f['label']
return field
# TODO rename type to field
# TODO a tuple or would be a dictionary better?
def get_fields_required(type):
"""
Returns a list, containing a tuple in which the first value is the label of a field and the second the boolean, if its required or not.
def get_fields_required(root_type):
"""Returns a list, containing a tuple in which the first value is the label of a field and the second the boolean, if its required or not.
**Parameters**
:type: Root-Type of fields
:root_type: Root-Type of fields
"""
result = []
fields = get_fields(type)
fields = get_fields(root_type)
if fields is not None:
for f in fields:
result.append((f['label'],f['required']))
return result
return list((f['label'],f['required']) for f in fields)
return []
# TODO rename type to root_types
def get_fields_from_labels(labels, type):
"""
Returns a list of fields of the labels in the given list labels.
def get_fields_from_labels(labels, root_type):
"""Returns a list, including one field for each label.
**Parameters**
:labels:
:type:
:labels: list of str, list of labels
:root_type: str, Root-Type of fields
"""
result=[]
fields = get_fields(type)
fields = get_fields(root_type)
if fields is not None:
for l in labels:
for f in fields:
if f['label'] == l:
result.append(f['field'])
break
break # if there is more than one label with this name, return just the first one
return result
......@@ -2,13 +2,12 @@
:Author: Franziska Koehn
:Created: 2015/01/13
This module houses all classes of Exceptions of xsa. Some Exceptions do already exist in modules like httplib2 (...), the where here implemented for let some classes not know this libaries just for the exception.
This module houses all classes of Exceptions of xsa.
Some are defined here just to hide exceptions from different libraries.
"""
# TODO rename to xsa-exception ???
class Error(Exception):
"""A new class Error to discrimminate between xsa and non-xsa Exceptions"""
"""A new Exception-class, to differentiate between xsa and non-xsa Exceptions"""
pass
class ServerNotFoundError(Error):
......@@ -31,7 +30,6 @@ class EmptyResultset(Error):
"""Should be raised when the resultset of the given search is empty."""
pass
# TODO QueryError == CorruptedQueryError???
class QueryError(Error):
"""Should be raised when the query-definition is incompatible."""
pass
......@@ -40,7 +38,6 @@ class NoRestApiError(Error):
"""Should be raised when no REST-API-Interface was defined in the json-file of a root-type"""
pass
# TODO QueryError == CorruptedQueryError???
class CorruptedQueryError(Error):
"""Should be raised when the query-definition is incompatible."""
"""Should be raised when the query-definition can not be loaded from file."""
pass
......@@ -3,6 +3,35 @@
:Created: 2015/01/13
This module includes functions around sending and defining queries.
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']
"""
from threading import Lock
......@@ -16,19 +45,18 @@ def get_operators():
"""Returns all applicable Operators for creating a query."""
return ["LIKE", ">", "<", "<=", ">=", "="]
#TODO download_async and download as one function???
def download_async(result, host, creds, rest, dest_folder='', cb=(lambda *_: None), cb_args=()):
"""
Downloads file by using threads (So the GUI will not be bocked while downloading).
Downloads file by using threads (So the GUI will not be blocked while downloading).
**Parameters**
:result: resultset, from which will be downloaded
:host: address of host
:creds: credentials (including user-name and -password)
:rest: REST-API definition
:dest_folder: folder where the downloaded files will be saved
:cb: function for moving spinners
: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
:cb_args: args for function cb
"""
from threading import Thread
......@@ -38,15 +66,15 @@ def download_async(result, host, creds, rest, dest_folder='', cb=(lambda *_: Non
def download(result, host, creds, rest, dest_folder='', cb=(lambda *_: None), cb_args=()):
"""
Downloads a file.
Downloads a Result.
**Parameters**
:result: resultset, from which will be downloaded
:host: address of host
:creds: credentials (including user-name and -password)
:rest: REST-API definition
:dest_folder: folder where the downloaded files will be saved
:cb: function for moving spinners
: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
:cb_args: args for function cb
"""
......@@ -72,12 +100,15 @@ def download(result, host, creds, rest, dest_folder='', cb=(lambda *_: None), cb
def download_file(url, creds, path):
"""
Downloads a file.
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.
**Parameters**
:url: host/REST-API with filled values
:creds: credentials (including user-name and -password)
:path: folder where the downloaded files will be saved
: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
"""
import requests
......@@ -98,10 +129,12 @@ def download_file(url, creds, path):
if not block:
break
handle.write(block)
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)
return False
def search_for(host, root_element, constraints, search_fields, user, passw):
......@@ -109,14 +142,22 @@ def search_for(host, root_element, constraints, search_fields, user, passw):
Does a search for given values. raises xsa -Exceptions
**Parameters**
:host: host-address
:root_element: root-element of search
:constraints: constraints of query
:search_fields: fields which will be returned from server
:user: user-name
:passw:user-password
: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:
"""
import xsa.errors as xsa_errors
import xsa.errors
from pyxnat.core import errors
from httplib2 import ServerNotFoundError
......@@ -132,7 +173,7 @@ def search_for(host, root_element, constraints, search_fields, user, passw):
password=passw,
cachedir=tmp_dir)
except IndexError as e:
raise xsa_errors.ServerNotFoundError("Server not found, check your host-address.")
raise xsa.errors.ServerNotFoundError("Server not found, check your host-address.")
result = []
......@@ -140,14 +181,16 @@ def search_for(host, root_element, constraints, search_fields, user, passw):
result = central.select(root_element,search_fields).where(constraints)
except errors.DatabaseError as e:
if '401' in str(e):
raise xsa_errors.UnauthorizedError("Unauthorizied attempt. Check your User and Password")
raise xsa.errors.UnauthorizedError("Unauthorizied attempt. Check your User and Password")
else:
raise e
except ServerNotFoundError:
raise xsa_errors.ServerNotFoundError("Server not found, check your host-address.")
raise xsa.errors.ServerNotFoundError("Server not found, check your host-address.")
except ResponseNotReady:
raise xsa_errors.ResponseNotReady("Please check your Host-Address")
raise xsa.errors.ResponseNotReady("Please check your Host-Address")
if result == []:
raise xsa_errors.QueryError("Please check your query.")
raise xsa.errors.QueryError("Please check your query.")
try:
central.disonnect()
......
......@@ -4,6 +4,8 @@ import xsa.queries as queries
def main(host, user, passw):
import distutils.core
#TODO Read query from json-file
root_element = 'xnat:mrScanData'
constraints = [
('xnat:mrScanData/TYPE', 'LIKE', '%t2%'),
......
......@@ -10,20 +10,17 @@ This module houses classes relating to the GUI-representation of the chart.
import gtk
class ChartView(gtk.HPaned):
"""
contains all widgets for working with the chart.
"""
"""Container with all widgets for working with the chart."""
results = []
def create_new_chart(self, field, allocation):
"""
Reads values from TreeViewChartValues and calls the function for creating the chart.
Creates the chart from all values from TreeViewChartValues
**Parameters**
:field: the field, for which the chart will be drawn
:allocation: the size of the chart
:field: str, the field for which the chart will be drawn
:allocation: gtk.Allocation, the size of the chart
"""
import xsa.chart as chart
import tempfile
......@@ -44,12 +41,12 @@ class ChartView(gtk.HPaned):
def update_chart_view(self, data, root_type):
"""
updates chart and combobox for a new search.
Updates chart and combobox of types for given Root-Type and results.
**Parameters**
:data: results of search
:root_type: root_type of search, for showing the fields of this type in the combobox
:data: JsonTable, results of search
:root_type: str, Root-Type of search
"""
self.results = data
self.combobox.show_data(root_type, data)
......@@ -59,10 +56,7 @@ class ChartView(gtk.HPaned):
def __init__(self):
"""
creates chart-area.
"""
"""Creates chart-area."""
super(ChartView, self).__init__()
......@@ -125,23 +119,23 @@ class ChartView(gtk.HPaned):
self.image.connect("size-allocate",callback_char_size_allocate)
class ComboBoxRootType(gtk.ComboBox):
"""
ComboBox for choosing Type.
"""
"""ComboBox for choosing field."""
store = []
def __init__(self, *args, **kwargs):
"""
Calls parent-constructor. Creates store and set it as model.
"""Creates store and set it as model.
**Parameters**
:\*args: arguments passed to gtk.ComboBox-constructor
:\*\*kwargs: keyword-arguments passed to gtk.ComboBox-constructor
"""
super(ComboBoxRootType, self).__init__(*args, **kwargs)
self.store = gtk.ListStore(str, str)
self.set_model(self.store)
def get_selected_item(self):
"""
returns string of selected item.
"""
"""Returns string of selected item or None if no item is selected."""
model = self.get_model()
index = self.get_active()
if index > -1:
......@@ -151,12 +145,13 @@ class ComboBoxRootType(gtk.ComboBox):
def show_data(self, root_type, data):
"""
clears store, tests for emtpy data (= no results of search) and, if data is not empty, sets list of fields of given root-type in combobox-store.
clears store, tests for emtpy data (= no results of search) and, if data is not empty,
sets list of fields of given root-type in combobox-store.
**Parameters**
:root_type: root_type of search
:data: results of search, if empty the store will just cleared
:root_type: str, Root-Type of search
:data: JsonTable, Results of search, if empty the store will just cleared
"""
import xsa.datatypereader as type_reader
......@@ -169,18 +164,16 @@ class ComboBoxRootType(gtk.ComboBox):
class TreeViewChartValues(gtk.TreeView):
"""
For creating the strings, shown in the chart (by the user).
"""
"""TreeView for creating the Search-Strings, shown in the chart."""
inital_value = "..."
"""Start-string for new inserted row."""
tooltip = 'Delete by using right click'
"""Tooltip that will be shown for each row and each column"""
def __init__(self):
"""
calls parent-constructor, calls funktions for creating the model and creating the column.
"""
"""Creats the model and column."""
super(TreeViewChartValues, self).__init__()
self.create_model()
......@@ -208,10 +201,7 @@ class TreeViewChartValues(gtk.TreeView):
def create_column(self):
"""
Creates column and its edited-callback.
"""
"""Creates column and its edited-callback."""
def cell_edited_callback(cellrenderertext, path_string, new_text, *_):
it = self.store.get_iter_from_string(path_string)
......@@ -231,9 +221,7 @@ class TreeViewChartValues(gtk.TreeView):
self.append_column(column)
def create_model(self):
"""
creates store and set it as the model.
"""
self.store = gtk.ListStore(str, str)
"""creates store and set it as model."""
self.store = gtk.ListStore(str, str)