Adds time slider with AJAX reloading of graphs
This commit is contained in:
@@ -130,6 +130,9 @@ def main(global_config, **settings):
|
||||
|
||||
# JSON-ONLY ROUTES
|
||||
config.add_route('json_lastcard', "/json/lastcard")
|
||||
config.add_route('json_valuesbetween_wparams', "/json/values/between/{startdt}/{enddt}")
|
||||
config.add_route('json_valuesbetween', "/json/values/between")
|
||||
config.add_route("json_valuesdaterange", "/json/values/daterange")
|
||||
|
||||
config.scan()
|
||||
return config.make_wsgi_app()
|
||||
|
||||
@@ -7,3 +7,56 @@ from bson import json_util
|
||||
@view_config(route_name="json_lastcard", renderer="prettyjson")
|
||||
def json_lastcard(request):
|
||||
return get_latest_card(request)
|
||||
|
||||
|
||||
@view_config(route_name="json_valuesbetween", renderer="prettyjson")
|
||||
@view_config(route_name="json_valuesbetween_wparams", renderer="prettyjson")
|
||||
def json_valuesbetween(request):
|
||||
end = datetime.now()
|
||||
try: # Attempt to get a value from the request.
|
||||
end = request.matchdict['enddt']
|
||||
end = end.replace("T", " ")
|
||||
end = datetime.strptime(end, "%Y-%m-%d %H:%M:%S.%fZ")
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
start = end - timedelta(days=7)
|
||||
try: # Attempt to get a value from the request.
|
||||
start = request.matchdict['startdt']
|
||||
start = start.replace("T", " ")
|
||||
start = datetime.strptime(start, "%Y-%m-%d %H:%M:%S.%fZ")
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
tag_data = []
|
||||
grouped_tags = request.db['wellData'].aggregate([
|
||||
{
|
||||
'$match': {"timestamp": {"$gt": start, "$lte": end}}
|
||||
},
|
||||
{
|
||||
'$sort': {"tagname": 1, "timestamp": 1}
|
||||
},
|
||||
{
|
||||
'$group': {
|
||||
'_id': "$tagname",
|
||||
'timestamps': {'$push': "$timestamp"},
|
||||
'currentValues': {'$push': "$currentValue"}
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
for t in grouped_tags:
|
||||
tag_data.append({"tagname": t['_id'], "timestamps": list(map(lambda a: a.strftime("%Y-%m-%d %H:%M:%S.%fZ"), t['timestamps'])), "currentValues": t['currentValues']})
|
||||
return {'values': tag_data, 'start': start, 'end': end}
|
||||
|
||||
|
||||
@view_config(route_name="json_valuesdaterange", renderer="prettyjson")
|
||||
def json_valuesdaterange(request):
|
||||
date_limits = list(request.db['wellData'].aggregate([
|
||||
{"$group": {
|
||||
"_id": 'null',
|
||||
"last": {"$max": "$timestamp"},
|
||||
"first": {"$min": "$timestamp"}
|
||||
}}
|
||||
]))[0]
|
||||
return {'first_date': date_limits['first'], 'last_date': date_limits['last']}
|
||||
|
||||
@@ -146,3 +146,107 @@ body {
|
||||
fill: none;
|
||||
stroke-width: 2.5px;
|
||||
}
|
||||
|
||||
#time-range p {
|
||||
font-family:"Arial", sans-serif;
|
||||
font-size:14px;
|
||||
color:#333;
|
||||
}
|
||||
.ui-slider-horizontal {
|
||||
height: 8px;
|
||||
background: #D7D7D7;
|
||||
border: 1px solid #BABABA;
|
||||
box-shadow: 0 1px 0 #FFF, 0 1px 0 #CFCFCF inset;
|
||||
clear: both;
|
||||
margin: 8px 0;
|
||||
-webkit-border-radius: 6px;
|
||||
-moz-border-radius: 6px;
|
||||
-ms-border-radius: 6px;
|
||||
-o-border-radius: 6px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.ui-slider {
|
||||
position: relative;
|
||||
text-align: left;
|
||||
}
|
||||
.ui-slider-horizontal .ui-slider-range {
|
||||
top: -1px;
|
||||
height: 100%;
|
||||
}
|
||||
.ui-slider .ui-slider-range {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
height: 8px;
|
||||
font-size: .7em;
|
||||
display: block;
|
||||
border: 1px solid #5BA8E1;
|
||||
box-shadow: 0 1px 0 #AAD6F6 inset;
|
||||
-moz-border-radius: 6px;
|
||||
-webkit-border-radius: 6px;
|
||||
-khtml-border-radius: 6px;
|
||||
border-radius: 6px;
|
||||
background: #81B8F3;
|
||||
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgi…pZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');
|
||||
background-size: 100%;
|
||||
background-image: -webkit-gradient(linear, 50% 0, 50% 100%, color-stop(0%, #A0D4F5), color-stop(100%, #81B8F3));
|
||||
background-image: -webkit-linear-gradient(top, #A0D4F5, #81B8F3);
|
||||
background-image: -moz-linear-gradient(top, #A0D4F5, #81B8F3);
|
||||
background-image: -o-linear-gradient(top, #A0D4F5, #81B8F3);
|
||||
background-image: linear-gradient(top, #A0D4F5, #81B8F3);
|
||||
}
|
||||
.ui-slider .ui-slider-handle {
|
||||
border-radius: 50%;
|
||||
background: #F9FBFA;
|
||||
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgi…pZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');
|
||||
background-size: 100%;
|
||||
background-image: -webkit-gradient(linear, 50% 0, 50% 100%, color-stop(0%, #C7CED6), color-stop(100%, #F9FBFA));
|
||||
background-image: -webkit-linear-gradient(top, #C7CED6, #F9FBFA);
|
||||
background-image: -moz-linear-gradient(top, #C7CED6, #F9FBFA);
|
||||
background-image: -o-linear-gradient(top, #C7CED6, #F9FBFA);
|
||||
background-image: linear-gradient(top, #C7CED6, #F9FBFA);
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
-webkit-box-shadow: 0 2px 3px -1px rgba(0, 0, 0, 0.6), 0 -1px 0 1px rgba(0, 0, 0, 0.15) inset, 0 1px 0 1px rgba(255, 255, 255, 0.9) inset;
|
||||
-moz-box-shadow: 0 2px 3px -1px rgba(0, 0, 0, 0.6), 0 -1px 0 1px rgba(0, 0, 0, 0.15) inset, 0 1px 0 1px rgba(255, 255, 255, 0.9) inset;
|
||||
box-shadow: 0 2px 3px -1px rgba(0, 0, 0, 0.6), 0 -1px 0 1px rgba(0, 0, 0, 0.15) inset, 0 1px 0 1px rgba(255, 255, 255, 0.9) inset;
|
||||
-webkit-transition: box-shadow .3s;
|
||||
-moz-transition: box-shadow .3s;
|
||||
-o-transition: box-shadow .3s;
|
||||
transition: box-shadow .3s;
|
||||
}
|
||||
.ui-slider .ui-slider-handle {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
cursor: default;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ui-slider .ui-slider-handle:after {
|
||||
content:"";
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
top: 50%;
|
||||
margin-top: -4px;
|
||||
left: 50%;
|
||||
margin-left: -4px;
|
||||
background: #30A2D2;
|
||||
-webkit-box-shadow: 0 1px 1px 1px rgba(22, 73, 163, 0.7) inset, 0 1px 0 0 #FFF;
|
||||
-moz-box-shadow: 0 1px 1px 1px rgba(22, 73, 163, 0.7) inset, 0 1px 0 0 white;
|
||||
box-shadow: 0 1px 1px 1px rgba(22, 73, 163, 0.7) inset, 0 1px 0 0 #FFF;
|
||||
}
|
||||
.ui-slider-horizontal .ui-slider-handle {
|
||||
top: -.5em;
|
||||
margin-left: -.6em;
|
||||
}
|
||||
.ui-slider a:focus {
|
||||
outline:none;
|
||||
}
|
||||
|
||||
.slider-time {
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
<script src="../../assets/js/ie-emulation-modes-warning.js"></script>
|
||||
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="http://code.jquery.com/ui/1.12.0/jquery-ui.min.js"></script>
|
||||
<script src="http://d3js.org/d3.v3.js"></script>
|
||||
<script src="{{request.static_url('pocwww:static/moment.min.js')}}"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
|
||||
|
||||
@@ -10,19 +10,23 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th></th>
|
||||
<th>Value</th>
|
||||
<th>Timestamp</th>
|
||||
<th>Max</th>
|
||||
<th>Min</th>
|
||||
<th>Average</th>
|
||||
<th>Total</th>
|
||||
<th>Last Stored</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for t in current_tag_values %}
|
||||
<tr>
|
||||
<td><a href="/values/tag/{{t._id}}">{{t._id}}</a></td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-default" title="{{t._id}}" data-container="body" data-toggle="popover" data-trigger="focus" data-placement="right" data-content="Max: {{t.max | round(3)}}<br \>Min: {{t.min | round(3)}}<br \>Average: {{t.average | round(3)}}<br \>Total: {{t.total | round(3)}}">Details</button>
|
||||
</td>
|
||||
<td>{{t.value | round(3)}}</td>
|
||||
<td>{{t.max | round(3)}}</td>
|
||||
<td>{{t.min | round(3)}}</td>
|
||||
<td>{{t.average | round(3)}}</td>
|
||||
<td>{{t.total | round(3)}}</td>
|
||||
<td>{{t.timestamp | datetime('medium')}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@@ -34,60 +38,157 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<pre>
|
||||
<!-- <pre>
|
||||
{% for t in all_dates %}
|
||||
{{t}}
|
||||
{% endfor %}
|
||||
</pre>
|
||||
</pre> -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<canvas id="myChart" width="400" height="400"></canvas>
|
||||
<div id="time-range">
|
||||
<p><span id="slider-start" class="slider-time pull-left well"></span><span id="slider-end" class="slider-time pull-right well"></span></p>
|
||||
<div class="sliders_step1">
|
||||
<div id="slider-range"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<canvas id="valueChart" height="150"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<meta id="tag_data" data-name="{{tag_history}}">
|
||||
|
||||
<script type="text/javascript" src="{{request.static_url('pocwww:static/linechart.js')}}"></script>
|
||||
<script>
|
||||
var simple_color_scale = ["#e41a1c", "#377eb8", "#4daf4a", "#984ea3", "#ff7f00", "#ffff33", "#a65628", "#f781bf"];
|
||||
var color_scale = ['#a6cee3','#1f78b4','#b2df8a','#33a02c','#fb9a99','#e31a1c','#fdbf6f','#ff7f00','#cab2d6','#6a3d9a','#ffff99','#b15928'];
|
||||
var margin = {top: 20, right: 80, bottom: 30, left: 250};
|
||||
var graph_data = [] ;
|
||||
var data = $('#tag_data').data().name.replace(/'/g, '"');
|
||||
var json_data = JSON.parse(data);
|
||||
|
||||
var ctx = document.getElementById("myChart");
|
||||
for (var i = 0; i < json_data.length; i++){
|
||||
var newObj = {
|
||||
label: json_data[i].tagname,
|
||||
fill: false,
|
||||
data: [],
|
||||
borderColor: color_scale[i % color_scale.length]
|
||||
function drawChart(data){
|
||||
var simple_color_scale = ["#e41a1c", "#377eb8", "#4daf4a", "#984ea3", "#ff7f00", "#ffff33", "#a65628", "#f781bf"];
|
||||
var color_scale = ['#a6cee3','#1f78b4','#b2df8a','#33a02c','#fb9a99','#e31a1c','#fdbf6f','#ff7f00','#cab2d6','#6a3d9a','#ffff99','#b15928'];
|
||||
var graph_data = [] ;
|
||||
var json_data = data.values;
|
||||
var ctx = document.getElementById("valueChart");
|
||||
for (var i = 0; i < json_data.length; i++){
|
||||
var newObj = {
|
||||
label: json_data[i].tagname,
|
||||
fill: false,
|
||||
data: [],
|
||||
lineTension: 0.05,
|
||||
borderColor: color_scale[i % color_scale.length]
|
||||
}
|
||||
for(var j = 0; j < json_data[i].timestamps.length; j++){
|
||||
newObj.data.push({
|
||||
x: json_data[i].timestamps[j],
|
||||
y: json_data[i].currentValues[j]
|
||||
});
|
||||
}
|
||||
graph_data.push(newObj);
|
||||
}
|
||||
for(var j = 0; j < json_data[i].timestamps.length; j++){
|
||||
newObj.data.push({
|
||||
x: json_data[i].timestamps[j],
|
||||
y: json_data[i].currentValues[j]
|
||||
});
|
||||
}
|
||||
graph_data.push(newObj);
|
||||
var scatterChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
responsive: true,
|
||||
data: {
|
||||
datasets: graph_data
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'time',
|
||||
position: 'bottom'
|
||||
}]
|
||||
},
|
||||
legend: {
|
||||
labels: {
|
||||
boxWidth: 20
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var scatterChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: graph_data
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'time',
|
||||
position: 'bottom'
|
||||
}]
|
||||
function drawSlider(data){
|
||||
|
||||
var date_min = new Date(Date.parse(data.first_date));
|
||||
var date_max = new Date(Date.parse(data.last_date));
|
||||
console.log(data);
|
||||
|
||||
var min_val = date_min/1000;
|
||||
var max_val = date_max/1000;
|
||||
|
||||
var urlparts = decodeURIComponent(window.location).split("/").slice(3)
|
||||
var endD = new Date();
|
||||
var end = endD.toISOString().replace("T", " ");
|
||||
var dummyDate = new Date();
|
||||
var startD = new Date(dummyDate.setDate(endD.getDate() - 7));
|
||||
var start = startD.toISOString().replace("T", " ");
|
||||
|
||||
|
||||
var start_date = startD/1000;
|
||||
$('#slider-start').html(start);
|
||||
if(start_date < min_val){
|
||||
start_date = min_val;
|
||||
$('#slider-start').html(date_min.toISOString().replace("T", " "));
|
||||
}
|
||||
|
||||
var end_date = endD/1000;
|
||||
$('#slider-end').html(end);
|
||||
if(end_date > max_val){
|
||||
end_date = max_val;
|
||||
$('#slider-end').html(date_max.toISOString().replace("T", " "));
|
||||
}
|
||||
|
||||
$("#slider-range").slider({
|
||||
range: true,
|
||||
min: min_val,
|
||||
max: max_val,
|
||||
step: 10,
|
||||
values: [start_date, end_date],
|
||||
slide: function (e, ui) {
|
||||
var dt_cur_from = new Date(ui.values[0]*1000); //.format("yyyy-mm-dd hh:ii:ss");
|
||||
$('#slider-start').html(dt_cur_from.toISOString().replace("T", " "));
|
||||
|
||||
var dt_cur_to = new Date(ui.values[1]*1000); //.format("yyyy-mm-dd hh:ii:ss");
|
||||
$('#slider-end').html(dt_cur_to.toISOString().replace("T", " "));
|
||||
},
|
||||
stop: function(e, ui){
|
||||
var startD = new Date(ui.values[0]*1000);
|
||||
var endD = new Date(ui.values[1]*1000);
|
||||
$.ajax({
|
||||
dataType: 'json',
|
||||
url:"/json/values/between/" + startD.toISOString() + "/" + endD.toISOString(),
|
||||
success: drawChart
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var urlparts = decodeURIComponent(window.location).split("/").slice(3)
|
||||
var endD = new Date();
|
||||
var end = endD.toISOString();
|
||||
var dummyDate = new Date();
|
||||
var startD = new Date(dummyDate.setDate(endD.getDate() - 7));
|
||||
var start = startD.toISOString();
|
||||
|
||||
if(urlparts.length >= 3){
|
||||
start = urlparts[1];
|
||||
end = urlparts[2];
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
dataType: 'json',
|
||||
url:"/json/values/daterange",
|
||||
success: drawSlider
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
dataType: 'json',
|
||||
url:"/json/values/between/" + start + "/" + end,
|
||||
success: drawChart
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
{% endblock content %}
|
||||
|
||||
@@ -3,7 +3,6 @@ from math import ceil
|
||||
from .pagination import Pagination
|
||||
|
||||
|
||||
|
||||
def get_lastest_tag_values(request):
|
||||
latest_tag_values = []
|
||||
latesttag_agg = request.db['wellData'].aggregate([
|
||||
@@ -30,8 +29,6 @@ def get_lastest_tag_values(request):
|
||||
def get_latest_card(request):
|
||||
try:
|
||||
latest_card = list(request.db['cards'].find().sort("timestamp", -1).limit(1))[0]
|
||||
for x in latest_card:
|
||||
print("{} = {}".format(x, type(latest_card[x])))
|
||||
return latest_card
|
||||
except IndexError:
|
||||
return []
|
||||
@@ -58,23 +55,3 @@ def card_page(request):
|
||||
|
||||
cards = request.db['cards'].find({'timestamp': {'$lt': cards_date_end, '$gte': cards_date_start}}).sort("timestamp", -1).skip(num_per_page * (page_num - 1)).limit(num_per_page)
|
||||
return {'cards': list(cards), 'pagination': Pagination(page_num, num_per_page, num_cards), 'cards_date': cards_date_start.strftime("%Y-%m-%d"), 'navgroup': 'cards'}
|
||||
|
||||
|
||||
def get_all_tags_between(request):
|
||||
tag_data = []
|
||||
grouped_tags = request.db['wellData'].aggregate([
|
||||
{
|
||||
'$sort': {"tagname": 1, "timestamp": 1}
|
||||
},
|
||||
{
|
||||
'$group': {
|
||||
'_id': "$tagname",
|
||||
'timestamps': {'$push': "$timestamp"},
|
||||
'currentValues': {'$push': "$currentValue"}
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
for t in grouped_tags:
|
||||
tag_data.append({"tagname": t['_id'], "timestamps": list(map(lambda a: a.strftime("%Y-%m-%d %H:%M:%S.%fZ"), t['timestamps'])), "currentValues": t['currentValues']})
|
||||
return tag_data
|
||||
|
||||
@@ -54,16 +54,7 @@ def card_single(request):
|
||||
@view_config(route_name='values_all', renderer="templates/valuesall.jinja2")
|
||||
@view_config(route_name='json_values_all', renderer='prettyjson')
|
||||
def values_all(request):
|
||||
# all_dates = list(request.db['wellData'].distinct('timestamp'))
|
||||
all_dates = request.db['wellData'].aggregate([
|
||||
{"$group": {
|
||||
"_id": {"$dateToString": {'format': "%Y-%m-%d %H:%M:%S.000Z", 'date': "$timestamp"}},
|
||||
"count": {"$sum": 1}
|
||||
}},
|
||||
{"$sort": {"_id": -1}}
|
||||
])
|
||||
all_dates_formatted = list(map(lambda x: {'count': x['count'], 'timestamp': datetime.strptime(x['_id'], "%Y-%m-%d %H:%M:%S.000Z")}, all_dates))
|
||||
return {'navgroup': 'values', 'current_tag_values': get_lastest_tag_values(request), 'all_dates': all_dates_formatted, 'tag_history': get_all_tags_between(request)}
|
||||
return {'navgroup': 'values', 'current_tag_values': get_lastest_tag_values(request)}
|
||||
|
||||
|
||||
@view_config(route_name="values_tag", renderer="templates/values_single.jinja2")
|
||||
|
||||
Reference in New Issue
Block a user