Commit 0353a431 authored by Franziska Koehn's avatar Franziska Koehn
Browse files

files for web interface added

parent 4d9e943a
from flask import Flask, render_template, request, flash, Response, make_response
app = Flask(__name__)
app.secret_key = 'some_secret' #TODO set secret
state = {
"host": "",
"username": "",
"password": "",
"roottype": "",
"results": [],
"results_export":[]
}
@app.route("/server_settings")
def server_settings():
return render_template("server_settings.html", state=state)
@app.route("/")
def step1():
from xsa.datatypereader import get_root_types, get_fields
root_types = get_root_types()
roottypefields = ()
roottypefieldsjson = ""
if state["roottype"]:
roottypefields = get_fields(state["roottype"])
import json
roottypefieldsjson = json.dumps(roottypefields, indent=2)
return render_template("step1.html", state=state, roottypes=root_types,
roottypefields=roottypefields, roottypefieldsjson=roottypefieldsjson)
@app.route("/results")
def results(res):
from xsa.datatypereader import get_field_label_by_key
from xsa.queries import prepare_rest
from xsa.datatypereader import get_rest
from xsa.datatypereader import get_comparison_extra_source
from urllib.parse import quote
import json
if state["roottype"] and res is not None:
roottypefields=[]
for h in res.headers():
roottypefields.append({'key': h, 'label':get_field_label_by_key(state["roottype"], h)})
rest_downl = ""
try:
rest_downl = get_rest(state["roottype"])
for r in res:
r['rest'] = quote(prepare_rest(r, rest_downl, state["host"]))
except:
pass
rest_adddata = ""
try:
rest_adddata = get_comparison_extra_source(state["roottype"])
except:
pass
return render_template("results.html", state=state, results=res, resultcount=len(res), roottypefields=roottypefields, rest_downl=rest_downl, rest_adddata= rest_adddata)
else:
flash("No root-type was chosen and/or no search-results are available.")
@app.route("/download", methods=["GET"])
def download():
from xsa.queries import download_file_iter
url = request.args.get("url")
creds = state["username"], state["password"]
response = Response(download_file_iter(url, creds), mimetype='application/zip')
response.headers["Content-Disposition"] = "attachment; filename=test.zip"#TODO rename file
return response
@app.route("/set_root_type", methods=["GET"])
def set_root_type():
state["roottype"] = request.args.get("t")
return step1()
#MODEL: [{"operator":"AND","children":[{"name":"Type","predicate":"LIKE","value":"%t1%","idx":"0","field":"xnat:mrScanData/TYPE"}]}]
@app.route("/send_query", methods=["POST", "GET"])
def send_query():
import json
from xsa.queries import search_for
#TODO show message: sending query...
def func(list, parent):
for item in list:
if isinstance(item, dict):
for key in item:
if key=='operator':
child = []
child.append(item['operator'])
func(item['children'], child)
parent.append(child)
elif key=='field':
parent.append((item['field'], item['predicate'], item['value']))
else:
pass
else:
pass
constraints=[]
func(json.loads(request.form.get("model")), constraints)
search_fields = json.loads(request.form.get("fields"))
state["host"] = json.loads(request.form.get("host"))
state["username"] = json.loads(request.form.get("user"))
state["password"] = json.loads(request.form.get("passw"))
res = search_for(state["host"], state["roottype"], constraints, search_fields, state["username"], state["password"])
if res is None:
flash("Something went wrong. Check the connection to your server and your query.")
return step1()
else:
state["results"] = res
state["results_export"] = res
return results(res)
@app.route("/get_adddata", methods=["POST", "GET"])
def get_adddata():
from xsa.queries import query_for_additional_data
from xsa.datatypereader import get_comparison_extra_source
rest = get_comparison_extra_source(state["roottype"])
new_data_sets = []
for result in state["results"]:
new_data_sets.append(query_for_additional_data(rest, result, state["host"], (state["username"], state["password"])))
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 xsa.jsonutil import JsonTable
state["results"] = JsonTable(new_result)
state["results_export"] = JsonTable(new_result)
print("FINISHED")
return results(state["results"])
@app.route("/downl_csv", methods=["POST", "GET"])
def download_csv():
from datetime import datetime
response = Response(state['results_export'].dumps_csv(","), mimetype='text/csv')
filename= "search_export_%s.csv" % str(datetime.now())
response.headers["Content-Disposition"] = "attachment; filename="+filename #TODO rename file
return response
def start_web_gui(args):
if args.host:
state["host"] = args.host
if args.user:
state["username"], _, state["password"] = args.user.partition(":")
app.debug=True
app.run()
This diff is collapsed.
This diff is collapsed.
/*
* Base structure
*/
/* Move down content because we have a fixed navbar that is 50px tall */
body {
padding-top: 50px;
}
/*
* Global add-ons
*/
.sub-header {
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
/*
* Top navigation
* Hide default border to remove 1px line.
*/
.navbar-fixed-top {
border: 0;
}
/*
* Sidebar
*/
/* Hide for mobile, show later */
.sidebar {
display: none;
}
@media (min-width: 768px) {
.sidebar {
position: fixed;
top: 51px;
bottom: 0;
left: 0;
z-index: 1000;
display: block;
padding: 20px;
overflow-x: hidden;
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
background-color: #f5f5f5;
border-right: 1px solid #eee;
}
}
/* Sidebar navigation */
.nav-sidebar {
margin-right: -21px; /* 20px padding + 1px border */
margin-bottom: 20px;
margin-left: -20px;
}
.nav-sidebar > li > a {
padding-right: 20px;
padding-left: 20px;
}
.nav-sidebar > .active > a,
.nav-sidebar > .active > a:hover,
.nav-sidebar > .active > a:focus {
color: #fff;
background-color: #428bca;
}
/*
* Main content
*/
.main {
padding: 20px;
}
@media (min-width: 768px) {
.main {
padding-right: 40px;
padding-left: 40px;
}
}
.main .page-header {
margin-top: 0;
}
/*
* Placeholder dashboard ideas
*/
.placeholders {
margin-bottom: 30px;
text-align: center;
}
.placeholders h4 {
margin-bottom: 0;
}
.placeholder {
margin-bottom: 20px;
}
.placeholder img {
display: inline-block;
border-radius: 50%;
}
/*!
* IE10 viewport hack for Surface/desktop Windows 8 bug
* Copyright 2014 Twitter, Inc.
* Licensed under the Creative Commons Attribution 3.0 Unported License. For
* details, see http://creativecommons.org/licenses/by/3.0/.
*/
// See the Getting Started docs for more information:
// http://getbootstrap.com/getting-started/#support-ie10-width
(function () {
'use strict';
if (navigator.userAgent.match(/IEMobile\/10\.0/)) {
var msViewportStyle = document.createElement('style')
msViewportStyle.appendChild(
document.createTextNode(
'@-ms-viewport{width:auto!important}'
)
)
document.querySelector('head').appendChild(msViewportStyle)
}
})();
This diff is collapsed.
* {
background-color: #f3f;
}
<!DOCTYPE html>
<html lang="en"><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="http://getbootstrap.com/favicon.ico">
<title>Search XNAT</title>
<!-- Bootstrap core CSS -->
<link href="static/bootstrap.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="static/dashboard.css" rel="stylesheet">
<style type="text/css">
.boxes {
height: 150px;
overflow: auto;
width: 400px;
}
</style>
<script>
var getadddata = function() {
$.ajax("/get_adddata", { success: function(data) { console.log(data);
var newDoc = document.open("text/html", "replace");
newDoc.write(data);
newDoc.close();
window.history.pushState("", $("title").text(), "/get_adddata");
},
data:{}, method:"POST" });
};
var exportascsv = function(){
console.log("export");
};
</script>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Search XNAT</a>
</div>
</div>
</nav>
<div class="col-sm-12 col-md-10 col-md-offset-1 main">
<h1 class="page-header">Results</h1>
<div class="alert alert-info" role="alert">
{{resultcount}} Results
</div>
<script>
selectTab = function(tabname, idx) {
$(tabname).addClass("hidden");
$(tabname+":eq(" + idx+ ")").removeClass("hidden");
$(tabname+"-menu > li").removeClass("active");
$(tabname+"-menu > li:eq(" + idx+ ")").addClass("active");
};
</script>
<ul class="nav nav-tabs tabbed1-menu">
<li role="presentation" class="active" onclick="selectTab('.tabbed1',0)"><a>Table</a></li>
<li role="presentation" onclick="selectTab('.tabbed1',1)"><a>Chart</a></li>
</ul>
<div class="tabbed1">
<h2 class="sub-header">List of Results</h2>
<div class="btn-group" style="margin-top: 1em; margin-bottom:2em;" role="group" aria-label="...">
{% if rest_adddata%}
<button type="button" class="btn btn-lg btn-primary" onclick="getadddata()">Get Additional Data</button>
{% endif %}
<a href="/downl_csv"><button type="button" class="btn btn-lg btn-default">Export as CSV</button></a>
</div>
<div class="container">
<table class="table table-striped">
<thead>
<tr>
{% if rest_downl%}
<th></th>
{% endif %}
{% for field in roottypefields %}
<th>{{field["label"]}}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for r in results %}
<tr>
{% if r["rest"] %}
<td style="text-align: center; vertical-align: middle;">
<a href="/download?url={{r['rest']}}"><button type="button" class="btn btn-lg btn-primary" >Download</button></a>
</td>
{% endif %}
{% for field in roottypefields %}
<td>{{r[field["key"]]}}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="tabbed1 hidden">
<h2 class="sub-header">Chart</h2>
<div class="input-group">
<span class="input-group-addon">Field</span>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
Choose a Field!
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
{% for field in roottypefields %}
<li role="presentation"><a role="menuitem" tabindex="-1" href="">{{field["label"]}}</a></li>
{% endfor %}
</ul>
</div>
</div>
<script type="text/javascript">
var m = [30, 10, 10, 30],
w = 960 - m[1] - m[3],
h = 930 - m[0] - m[2];
var format = d3.format(",.0f");
var x = d3.scale.linear().range([0, w]),
y = d3.scale.ordinal().rangeRoundBands([0, h], .1);
var xAxis = d3.svg.axis().scale(x).orient("top").tickSize(-h),
yAxis = d3.svg.axis().scale(y).orient("left").tickSize(0);
var svg = d3.select("body").append("svg")
.attr("width", w + m[1] + m[3])
.attr("height", h + m[0] + m[2])
.append("g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
d3.csv("sample-data.csv", function(data) {
// Parse numbers, and sort by value.
data.forEach(function(d) { d.value = +d.value; });
data.sort(function(a, b) { return b.value - a.value; });
// Set the scale domain.
x.domain([0, d3.max(data, function(d) { return d.value; })]);
y.domain(data.map(function(d) { return d.name; }));
var bar = svg.selectAll("g.bar")
.data(data)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) { return "translate(0," + y(d.name) + ")"; });
bar.append("rect")
.attr("width", function(d) { return x(d.value); })
.attr("height", y.rangeBand());
bar.append("text")
.attr("class", "value")
.attr("x", function(d) { return x(d.value); })
.attr("y", y.rangeBand() / 2)
.attr("dx", -3)
.attr("dy", ".35em")
.attr("text-anchor", "end")
.text(function(d) { return format(d.value); });
svg.append("g")
.attr("class", "x axis")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
</script>
</div>
</div>
</body>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="static/jquery.js"></script>
<script src="static/bootstrap.js"></script>
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
<script src="static/ie10-viewport-bug-workaround.js"></script>
<svg style="visibility: hidden; position: absolute; top: -100%; left: -100%;" preserveAspectRatio="none" viewBox="0 0 200 200" height="200" width="200"><defs></defs><text style="font-weight:bold;font-size:10pt;font-family:Arial, Helvetica, Open Sans, sans-serif;dominant-baseline:middle" y="10" x="0">200x200</text></svg></body></html>
<!DOCTYPE html>
<html lang="en"><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="http://getbootstrap.com/favicon.ico">
<title>Search XNAT</title>
<!-- Bootstrap core CSS -->
<link href="static/bootstrap.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="static/dashboard.css" rel="stylesheet">
<style type="text/css">
.boxes {
height: 150px;
overflow: auto;
width: 400px;
}
</style>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Search XNAT</a>
</div>
</div>
</nav>
<div class="col-sm-12 col-md-10 col-md-offset-1 main">
<h2 class="sub-header">Server Settings</h2>
<div class="input-group">
<span class="input-group-addon">Host-Address</span>
<input id="inputHost" class="form-control" placeholder="Your Host-Address" required="required" autofocus="" type="" value="{{state.host}}">
</div>
<h3> </h3>
<div class="input-group">
<span class="input-group-addon">Username</span>
<input id="inputUser" class="form-control" placeholder="Your User-Name" required="" autofocus="" type="" value="{{state.username}}">
<span class="input-group-addon">Password</span>
<input id="inputPassw" class="form-control" placeholder="Your User-Password" required="" autofocus="" type="password" value="{{state.password}}">
</div>
<button type="button" class="btn btn-lg btn-primary">Send Query</button>
</div>
</div>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="static/jquery.js"></script>
<script src="static/bootstrap.js"></script>