Commit 07f67d22 authored by Franziska Koehn's avatar Franziska Koehn
Browse files

Merge branch 'feature/cSubAssessment-Extension' into develop

parents 364aa838 47f71165
{
"root-type":"ext:cSubAssessment",
"comparison_data":{"identifier":["expt_id", "project"],"extra-source":"/projects/{project}/experiments/{expt_id}"},
"fields":[
{"label":"Subject-ID", "field":"ext:cSubAssessment/SUBJECT_ID", "required":false, "key": "subject_id"},
{"label":"Experiment-ID", "field":"ext:cSubAssessment/EXPT_ID", "required":true, "key": "expt_id"},
{"label":"Date", "field":"ext:cSubAssessment/DATE", "required":false, "key": "date"},
{"label":"Age", "field":"ext:cSubAssessment/AGE", "required":false, "key": "age"},
{"label":"Projects", "field":"ext:cSubAssessment/PROJECTS", "required":false, "key": "projects"},
{"label":"Project", "field":"ext:cSubAssessment/PROJECT", "required":true, "key": "project"},
{"label":"Label", "field":"ext:cSubAssessment/LABEL", "required":false, "key": "label"},
{"label":"Insert Date", "field":"ext:cSubAssessment/INSERT_DATE", "required":false, "key": "insert_date"},
{"label":"Insert User", "field":"ext:cSubAssessment/INSERT_USER", "required":false, "key": "insert_user"},
{"label":"Datatype", "field":"ext:cSubAssessment/DATATYPE", "required":false, "key": "datatype"},
{"label":"Datatype Category", "field":"ext:cSubAssessment/DATATYPE_CATEGORY", "required":false, "key": "datatype_category"},
{"label":"Import Date", "field":"ext:cSubAssessment/IMPORT_DATE", "required":false, "key": "import_date"},
{"label":"Project Protocol", "field":"ext:cSubAssessment/PROJECT_PROTOCOL", "required":false, "key": "project_protocol"},
{"label":"Validation Method", "field":"ext:cSubAssessment/VALIDATION_METHOD", "required":false, "key": "validation_method"},
{"label":"Validation Status", "field":"ext:cSubAssessment/VALIDATION_STATUS", "required":false, "key": "validation_status"}
]
}
{
"root-type":"xnat:mrScanData",
"comparison_data":{"identifier":["id","xnat_mrsessiondata_session_id"]},
"REST-API":"/data/archive/projects/{xnat_mrsessiondata_project}/subjects/{xnat_mrsessiondata_subject_id}/experiments/{xnat_mrsessiondata_session_id}/scans/{id}/resources/DICOM/files?format=zip",
"fields":[
{"label":"Type", "field":"xnat:mrScanData/TYPE", "required":true, "key": "type"},
......
{
"root-type":"xnat:mrSessionData",
"comparison_data":{"identifier":["session_id"]},
"fields":[
{"label":"Session-ID", "field":"xnat:mrSessionData/SESSION_ID", "required":false, "key": "session_id"},
{"label":"Date", "field":"xnat:mrSessionData/DATE", "required":false, "key": "date"},
......
{"root-type":"xnat:projectData",
{
"root-type":"xnat:projectData",
"comparison_data":{"identifier":["id"]},
"fields":[
{"label":"Project-ID", "field":"xnat:projectData/ID", "required":true, "key": "id"},
{"label":"NAME", "field":"xnat:projectData/NAME", "required":false, "key": "name"},
......
{"root-type":"xnat:subjectData",
{
"root-type":"xnat:subjectData",
"comparison_data":{"identifier":["subject_id"]},
"fields":[
{"label":"Subject-ID", "field":"xnat:subjectData/SUBJECT_ID", "required":true, "key": "subject_id"},
{"label":"Subject-Label", "field":"xnat:subjectData/SUBJECT_LABEL", "required":false, "key": "subject_label"},
......
......@@ -39,6 +39,26 @@ def get_all(force=False):
return get_all()
get_all.cache = None
def get_comparison_extra_source(root_type):
for data in get_all():
if data['root-type'] == root_type:
if not 'comparison_data' in data or not data['comparison_data']:
raise NoComparisonDataError("No comparison-data was defined for this datatype")
elif not 'extra-source' in data['comparison_data'] or not data['comparison_data']['extra-source']:
raise NoComparisonDataError("No Source of additional Data was defined for this datatype")
else:
return data['comparison_data']['extra-source']
def get_comparison_identifier(root_type):
for data in get_all():
if data['root-type'] == root_type:
if not 'comparison_data' in data or not data['comparison_data']:
raise NoComparisonDataError("No comparison-data was defined for this datatype")
elif not 'identifier' in data['comparison_data'] or not data['comparison_data']['identifier']:
raise NoComparisonDataError("No Identifier for this datatype was defined")
else:
return data['comparison_data']['identifier']
def get_rest(root_type):
"""Returns REST-API-Interface of a given Root-Type, defined in its json-file.
......@@ -83,6 +103,18 @@ def get_labels(root_type):
return []
def get_keys(root_type):
"""Returns a list of field-keys of a given root_type.
**Parameters**
:root_type: str, Root-Type of returned field-labels
"""
fields = get_fields(root_type)
if fields is not None:
return list(f['key'] for f in fields)
return []
def get_field_label_by_key(root_type, field_key):
"""Returns the label of the given field-key.
......
......@@ -38,6 +38,10 @@ class NoRestApiError(Error):
"""Should be raised when no REST-API was defined in the json-file of a root-type"""
pass
class NoComparisonDataError(Error):
"""Should be raised when no comparision was defined in the json-file of a root-type"""
pass
class CorruptedQueryError(Error):
"""Should be raised when the query-definition can not be loaded from file."""
pass
......@@ -164,6 +164,28 @@ def disconnect_xnat_server():
get_xnat_server_connection.cache = None
except:
return
def query_for_additional_data(rest, result, host, creds):
import re
def subfunc(f):
key = f.group(0).strip("{}")
return result[key]
ret = re.sub('\{\w+\}', subfunc, rest)
user, passw = creds
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):
"""
Does a search for given values. raises xsa -Exceptions
......@@ -182,6 +204,7 @@ def search_for(host, root_element, constraints, search_fields, user, passw):
:xsa.errors.ResponseNotReady:
:xsa.errors.QueryError:
"""
import xsa.errors
......
......@@ -10,9 +10,14 @@ This module contains the controller that provides the communication between view
import gtk
import gobject
import xsa.datatypereader
class QueryController(gobject.GObject):
"""Provides communication between different views."""
results = []
_host = ""
_user = ""
_passw = ""
......@@ -83,7 +88,6 @@ class QueryController(gobject.GObject):
def on_search_query(host, credentials, root_type, query, fields):
user, passw = credentials
import xsa.queries as queries
import xsa.errors
if passw =='' or user=='' or host=='':
......@@ -104,7 +108,7 @@ class QueryController(gobject.GObject):
return
try:
query_results = queries.search_for( host,
query_results = xsa.queries.search_for( host,
root_type,
query,
fields,
......@@ -119,11 +123,12 @@ class QueryController(gobject.GObject):
md.run()
md.destroy()
self.results = []
except Exception:
except Exception as e:
md = gtk.MessageDialog( type=gtk.MESSAGE_WARNING,
buttons=gtk.BUTTONS_OK,
message_format="An unexpected Error occured."
message_format="An unexpected Error occured:"
)
md.format_secondary_text(e)
md.run()
md.destroy()
self.results = []
......@@ -134,16 +139,28 @@ class QueryController(gobject.GObject):
self.statusbar.push(0, "%s Results"%len(self.results))
self.chartview.update_chart_view(self.results, root_type)
if self.results == []:
self.menuview.disable_download_button(False, "No Results for downloading")
self.menuview.disable_export_button(False, "No Results for exporting")
self.menuview.disable_adddata_button(False, "No Results for getting additional data")
return
else:
try:
xsa.datatypereader.get_rest(root_type)
self.menuview.disable_download_button(True, "Download selected items from result-table")
except xsa.errors.NoRestApiError as e:
self.menuview.disable_download_button(False, str(e))
self.menuview.disable_adddata_button(True, "Get additional data")
self.menuview.disable_export_button(True, "Export Result-Table as csv-file")
self.root=self.queryview.get_root_type()
self.query=self.queryview.get_query()
self.labels=self.queryview.get_fields()
import xsa.datatypereader as type_reader
fields = type_reader.get_fields_from_labels(self.labels,self.root)
on_search_query(self.host, self.credentials_tuple,
self.root,
self.query,
fields)
fields = xsa.datatypereader.get_fields_from_labels(self.labels,self.root)
on_search_query(self.host, self.credentials_tuple, self.root, self.query, fields)
def __init__(self, main, queryview, menuview, chartview, resultsview, statusbar):
......@@ -167,6 +184,48 @@ class QueryController(gobject.GObject):
self.resultsview = resultsview
self.statusbar = statusbar
def callback_get_additional_data(*_):
rest = xsa.datatypereader.get_comparison_extra_source(self.root)
new_data_sets = []
for r in self.results:
new_data_sets.append(xsa.queries.query_for_additional_data(rest, r, self.host, self.credentials_tuple))
keys = []
for data_set in new_data_sets:
for key in data_set.keys():
keys.append(key)
new_result = []
for data_set in new_data_sets:
dic = {}
for key in keys:
try:
dic[key] = data_set[key]
except:
dic[key] = "N/A"
new_result.append(dic)
from pyxnat.core.jsonutil import JsonTable
self.results = JsonTable(new_result)
self.resultsview.show_data(self.results, self.root)
self.chartview.update_chart_view(self.results, self.root)
def callback_export_csv(*_):
dialog = gtk.FileChooserDialog( "Save as...",
None,
gtk.FILE_CHOOSER_ACTION_SAVE,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OPEN, gtk.RESPONSE_OK))
dialog.set_default_response(gtk.RESPONSE_CANCEL)
response = dialog.run()
if response == gtk.RESPONSE_OK:
self.results.dump_csv(dialog.get_filename())
dialog.destroy()
def callback_edit_Server_Settings(*_):
dialog = gtk.Dialog( "Server Settings",
......@@ -178,7 +237,7 @@ class QueryController(gobject.GObject):
dialog.set_size_request(330, 180)
hbox = gtk.HBox(False, 8)
hbox.set_border_width(8)
dialog.vbox.pack_start(hbox, False, False, 0)
dialog.vbox.pack_start(hbox, True, True, 0)
stock = gtk.image_new_from_stock(
gtk.STOCK_DIALOG_AUTHENTICATION,
......@@ -221,6 +280,16 @@ class QueryController(gobject.GObject):
if response == gtk.RESPONSE_OK:
self.credentials_tuple = (entry_user.get_text(),entry_passw.get_text())
self.host = entry_host.get_text()
import xsa.queries
try:
xsa.queries.get_xnat_server_connection(force=True, host=self.host, user=self.credentials_tuple[0], passw=self.credentials_tuple[1])
except Exception as e:
md = gtk.MessageDialog( type=gtk.MESSAGE_WARNING,
buttons=gtk.BUTTONS_OK,
message_format=str(e)
)
md.run()
md.destroy()
dialog.destroy()
......@@ -232,9 +301,6 @@ class QueryController(gobject.GObject):
if self.resultsview.get_selected_items() == []:
return
import xsa.queries as queries
import xsa.datatypereader as type_reader
dialog = gtk.FileChooserDialog( "Open..",
None,
gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
......@@ -244,7 +310,7 @@ class QueryController(gobject.GObject):
response = dialog.run()
if response == gtk.RESPONSE_OK:
rest = type_reader.get_rest(self.root)
rest = xsa.datatypereader.get_rest(self.root)
enabled = (d for d in self.resultsview.get_store() if d[0])
for d in enabled:
def stop_spinner(_row):
......@@ -252,7 +318,7 @@ class QueryController(gobject.GObject):
_row[0] = False # Checkbox to false
d[3] = 0
d[2] = True # Show Spinner
queries.download_async( d[1],
xsa.queries.download_async( d[1],
self.host,
self.credentials_tuple,
rest,
......@@ -262,9 +328,6 @@ class QueryController(gobject.GObject):
)
dialog.destroy()
def disable_download_button(_, is_enable, text):
self.menuview.disable_download_button(is_enable, text)
root_key = 'root'
query_key = 'query'
labels_key = 'labels'
......@@ -314,15 +377,13 @@ class QueryController(gobject.GObject):
query = f[query_key],
field_labels = f[labels_key]
)
dialog.destroy()
self.menuview.connect("spawn-connection-dialog", callback_edit_Server_Settings)
self.menuview.connect("send-query", self.send_query)
self.menuview.connect("toggle-selection", event_clicked_toggle_selection)
self.menuview.connect("download-selection", event_clicked_download)
self.menuview.connect("save-query", save_query)
self.menuview.connect("load-query", load_query)
self.queryview.connect("rest-api", disable_download_button)
self.menuview.connect("get-additional-data",callback_get_additional_data)
self.menuview.connect("export-csv", callback_export_csv)
......@@ -13,6 +13,16 @@ import gobject
class MenuView(gtk.Toolbar):
"""A ToolBar containing Buttons"""
def disable_export_button(self, is_enable, text):
"""Disables export-button and sets given text as tooltip.
**Parameters**
:is_enable: bool: true for enabling and false for disabling export-button
:text: str, that will be set as tooltip
"""
self.tb_export.set_tooltip_text(text)
self.tb_export.set_sensitive(is_enable)
def disable_download_button(self, is_enable, text):
"""Disables download-button and sets given text as tooltip.
......@@ -23,6 +33,16 @@ class MenuView(gtk.Toolbar):
self.tb_downl.set_tooltip_text(text)
self.tb_downl.set_sensitive(is_enable)
def disable_adddata_button(self, is_enable, text):
"""Disables get-additional-data-button and sets given text as tooltip.
**Parameters**
:is_enable: bool: true for enabling and false for disabling button
:text: str, that will be set as tooltip
"""
self.tb_adddata.set_tooltip_text(text)
self.tb_adddata.set_sensitive(is_enable)
def __init__(self, *args, **kwargs):
"""Creates buttons for this Toolbar and defines signals when this buttons will be pressed.
......@@ -41,6 +61,11 @@ class MenuView(gtk.Toolbar):
gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
())
gobject.signal_new( "get-additional-data",
MenuView,
gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
())
gobject.signal_new( "send-query",
MenuView,
gobject.SIGNAL_RUN_FIRST,
......@@ -66,6 +91,11 @@ class MenuView(gtk.Toolbar):
gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
())
gobject.signal_new("export-csv",
MenuView,
gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
())
tb_savequery = gtk.ToolButton()
tb_savequery.set_label("Save Query")
......@@ -109,3 +139,23 @@ class MenuView(gtk.Toolbar):
self.tb_downl.set_stock_id(gtk.STOCK_SAVE)
self.tb_downl.connect("clicked",lambda *_: self.emit("download-selection"))
self.insert(self.tb_downl, 6)
self.tb_export = gtk.ToolButton()
self.tb_export.set_label("Export As CSV")
self.tb_export.set_stock_id(gtk.STOCK_CONVERT)
self.tb_export.connect("clicked",lambda *_: self.emit("export-csv"))
self.insert(self.tb_export, 7)
self.insert(gtk.SeparatorToolItem(), 8)
self.tb_adddata = gtk.ToolButton()
self.tb_adddata.set_label("Get Additional Data")
self.tb_adddata.set_stock_id(gtk.STOCK_INDEX)
self.tb_adddata.connect("clicked",lambda *_: self.emit("get-additional-data"))
self.insert(self.tb_adddata, 9)
self.disable_download_button(False, "No Results")
self.disable_adddata_button(False, "No Results")
self.disable_export_button(False, "No Results")
......@@ -13,7 +13,7 @@ class QueryView(gtk.VBox):
"""Contains all widgets for creating the query"""
def __init__(self, *args, **kwargs):
"""Creates signal "rest-api". Creates all query-view-widgets.
"""Creates all query-view-widgets.
**Parameters**
:\*args: same as gtk.VBox
......@@ -22,13 +22,6 @@ class QueryView(gtk.VBox):
super(QueryView, self).__init__(*args, **kwargs)
import gobject
gobject.signal_new( "rest-api",
QueryView,
gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
(bool,str))
# root-type
hBox_root_type = gtk.HBox()
......@@ -42,13 +35,6 @@ class QueryView(gtk.VBox):
import xsa.datatypereader as type_reader
import xsa.errors
selected = combobox.get_active_text()
try:
type_reader.get_rest(selected)
except xsa.errors.NoRestApiError as e:
self.emit("rest-api", False, e)
else:
self.emit("rest-api", True, "Download selected items from result-table")
self.treeView_search.set_root_type(selected)
self.treeView_fields.reset_fields(selected)
......
......@@ -75,7 +75,6 @@ class XsaApp(gtk.Window):
vpaned.add1(self.resultsview)
# chart
from xsagtk.chart_view import ChartView
self.chartview = ChartView()
vpaned.add2(self.chartview)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment