mirror of
https://github.com/elisspace/FxLifeSheet.git
synced 2026-08-29 15:44:00 +00:00
Further implement graphs
This commit is contained in:
@@ -4,18 +4,30 @@ require "date"
|
||||
|
||||
class API
|
||||
def fetch(key:, group_by:, start_date:)
|
||||
#
|
||||
# Modify and verify parameters
|
||||
#
|
||||
group_by = "year#{group_by}" unless group_by.include?("year")
|
||||
group_by = group_by.to_sym
|
||||
raise "Invalid group_by" unless [:yearmonth, :yearweek].include?(group_by)
|
||||
|
||||
# TODO: Filter start date
|
||||
results = database.fetch("SELECT ?, AVG(value::numeric) as avg from raw_data where key = ? GROUP BY ? ORDER BY ?", group_by, key, group_by, group_by)
|
||||
raise "`start_date` must be in format '2019-04'" unless start_date.match(/\d\d\d\d\-\d\d/)
|
||||
start_timestamp = Date.strptime(start_date, "%Y-%m").strftime("%Q")
|
||||
|
||||
#
|
||||
# Run the query
|
||||
#
|
||||
results = database.fetch("SELECT ?, AVG(value::numeric) as avg, SUM(value::numeric) as sum from raw_data where key = ? AND timestamp > ? GROUP BY ? ORDER BY ?", group_by, key, start_timestamp, group_by, group_by)
|
||||
|
||||
#
|
||||
# Return the results
|
||||
#
|
||||
return {
|
||||
total_count: raw_data.where(key: key).count,
|
||||
grouped_count: results.count,
|
||||
rows: results.to_a.collect do |row|
|
||||
row[:avg] = row[:avg].truncate(5).to_s('F').to_f # convert from BigFloat to float
|
||||
row[:sum] = row[:sum].truncate(5).to_s('F').to_f # convert from BigFloat to float
|
||||
if group_by == :yearmonth
|
||||
row[:as_date] = Date.strptime(row[:yearmonth].to_s, "%Y%m")
|
||||
else
|
||||
|
||||
@@ -1,36 +1,126 @@
|
||||
<html>
|
||||
<head>
|
||||
<!-- Load plotly.js into the DOM -->
|
||||
<script src='https://cdn.plot.ly/plotly-latest.min.js'></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<form>
|
||||
<select class="keys" id="keys-0" onchange="updateKeys(0)"></select>
|
||||
<select class="keys" id="keys-1" onchange="updateKeys(1)"></select>
|
||||
<select class="keys" id="keys-2" onchange="updateKeys(2)"></select>
|
||||
<select class="keys" id="keys-3" onchange="updateKeys(3)"></select>
|
||||
<select class="keys" id="keys-4" onchange="updateKeys(4)"></select>
|
||||
<form id="mainForm" onSubmit="return false;">
|
||||
<span id="keysContainer">
|
||||
<table id="keysTable">
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Avg</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><select class="keys" id="keys-0" onchange="updateKeys(0)" /></td>
|
||||
<td><input type="checkbox" id="keys-avg-0" onchange="updateKeys(0)" checked="checked" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><select class="keys" id="keys-1" onchange="updateKeys(1)" /></td>
|
||||
<td><input type="checkbox" id="keys-avg-1" onchange="updateKeys(1)" checked="checked" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><select class="keys" id="keys-2" onchange="updateKeys(2)" /></td>
|
||||
<td><input type="checkbox" id="keys-avg-2" onchange="updateKeys(2)" checked="checked" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><select class="keys" id="keys-3" onchange="updateKeys(3)" /></td>
|
||||
<td><input type="checkbox" id="keys-avg-3" onchange="updateKeys(3)" checked="checked" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><select class="keys" id="keys-4" onchange="updateKeys(4)" /></td>
|
||||
<td><input type="checkbox" id="keys-avg-4" onchange="updateKeys(4)" checked="checked" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
</span>
|
||||
<span id="attributesContainer">
|
||||
<h2>Attributes</h2>
|
||||
<table id="attributesTable">
|
||||
<tr>
|
||||
<td>Start date</td>
|
||||
<td><input type="text" value="2019-04" id="start-date" name="start-date" onchange="updatedAttribute()" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
</span>
|
||||
</form>
|
||||
<div id='myDiv' />
|
||||
<div id='myGraph' />
|
||||
</body>
|
||||
|
||||
<script type="text/javascript">
|
||||
let host = "http://127.0.0.1:4567"
|
||||
var keys = []
|
||||
var allData = []
|
||||
var allAxis = []
|
||||
// Multiple axis https://plotly.com/javascript/multiple-axes/
|
||||
for (let currentIndex of Array(5).keys()) {
|
||||
let current = {
|
||||
x: [],
|
||||
y: [],
|
||||
name: 'yaxis' + (currentIndex + 1) + ' data',
|
||||
type: 'scatter',
|
||||
line: {shape: 'spline'}
|
||||
}
|
||||
if (currentIndex > 0) { current["yaxis"] = "y" + (currentIndex + 1) }
|
||||
allData.push(current)
|
||||
};
|
||||
console.log(allData)
|
||||
|
||||
var layout = {
|
||||
title: 'Life Sheet Data'
|
||||
title: 'Life Sheet Data',
|
||||
yaxis: {
|
||||
titlefont: {color: '#1f77b4'},
|
||||
tickfont: {color: '#1f77b4'},
|
||||
showgrid: false,
|
||||
zeroline: false,
|
||||
showticklabels: false
|
||||
},
|
||||
yaxis2: {
|
||||
titlefont: {color: '#ff7f0e'},
|
||||
tickfont: {color: '#ff7f0e'},
|
||||
anchor: 'free',
|
||||
overlaying: 'y',
|
||||
showgrid: false,
|
||||
zeroline: false,
|
||||
showticklabels: false
|
||||
},
|
||||
yaxis3: {
|
||||
titlefont: {color: '#d62728'},
|
||||
tickfont: {color: '#d62728'},
|
||||
anchor: 'x',
|
||||
overlaying: 'y',
|
||||
showgrid: false,
|
||||
zeroline: false,
|
||||
showticklabels: false
|
||||
},
|
||||
yaxis4: {
|
||||
titlefont: {color: '#9467bd'},
|
||||
tickfont: {color: '#9467bd'},
|
||||
anchor: 'free',
|
||||
overlaying: 'y',
|
||||
showgrid: false,
|
||||
zeroline: false,
|
||||
showticklabels: false
|
||||
},
|
||||
yaxis5: {
|
||||
anchor: 'free',
|
||||
overlaying: 'y',
|
||||
showgrid: false,
|
||||
zeroline: false,
|
||||
showticklabels: false
|
||||
},
|
||||
};
|
||||
|
||||
Plotly.newPlot('myDiv', allData, layout);
|
||||
|
||||
loadKeys()
|
||||
loadData("mood", 0)
|
||||
loadData("weekLifeProgress", 1)
|
||||
Plotly.newPlot('myGraph', allData, layout);
|
||||
|
||||
function loadKeys() {
|
||||
loadKeys(function() {
|
||||
loadData("mood", 0)
|
||||
loadData("mood", 1)
|
||||
loadData("sleepDurationWithings", 2)
|
||||
loadData("bedTime", 3)
|
||||
loadData("gym", 4)
|
||||
})
|
||||
|
||||
function loadKeys(callback) {
|
||||
httpGetAsync(host + "/keys", function(data) {
|
||||
selects = document.getElementsByClassName("keys");
|
||||
for (let i = 0; i < selects.length; i++) {
|
||||
@@ -41,6 +131,7 @@
|
||||
selects[i].appendChild(opt);
|
||||
})
|
||||
}
|
||||
callback();
|
||||
})
|
||||
}
|
||||
|
||||
@@ -49,30 +140,29 @@
|
||||
loadData(select.value, index)
|
||||
}
|
||||
|
||||
function updatedAttribute() {
|
||||
for (let currentIndex of Array(5).keys()) {
|
||||
updateKeys(currentIndex)
|
||||
}
|
||||
}
|
||||
|
||||
function loadData(key, index) {
|
||||
httpGetAsync(host + "/data?group_by=month&key=" + key, function(data) {
|
||||
let newAxis = {
|
||||
title: 'yaxis ' + key,
|
||||
titlefont: {color: '#ff7f0e'},
|
||||
tickfont: {color: '#ff7f0e'},
|
||||
anchor: 'free',
|
||||
overlaying: 'y',
|
||||
// TODO: remove the line below
|
||||
select = document.getElementById("keys-" + index)
|
||||
select.value = key
|
||||
|
||||
let startDate = document.getElementById("start-date")
|
||||
|
||||
httpGetAsync(host + "/data?group_by=month&key=" + key + "&start_date=" + startDate.value, function(data) {
|
||||
allData[index]["x"] = data.rows.map((r) => r.as_date)
|
||||
if (document.getElementById("keys-avg-" + index).checked) {
|
||||
allData[index]["y"] = data.rows.map((r) => Math.round(r.avg * 1000) / 1000)
|
||||
} else {
|
||||
allData[index]["y"] = data.rows.map((r) => Math.round(r.sum * 1000) / 1000)
|
||||
}
|
||||
layout["yaxis" + index] = newAxis
|
||||
|
||||
// Remove item 'seven' from array
|
||||
// var filteredAry = allData.filter(function(e) { return e[yaxis] == ('y' + index) })
|
||||
// var filteredAry = ary.filter(e => e !== 'seven')
|
||||
|
||||
allData.push({
|
||||
x: data.rows.map((r) => r.as_date),
|
||||
y: data.rows.map((r) => r.avg),
|
||||
name: key,
|
||||
type: 'scatter',
|
||||
yaxis: 'y' + index,
|
||||
})
|
||||
allData[index]["name"] = key
|
||||
|
||||
Plotly.redraw('myDiv')
|
||||
Plotly.redraw('myGraph')
|
||||
console.log(allData)
|
||||
console.log(layout)
|
||||
})
|
||||
@@ -91,14 +181,49 @@
|
||||
</script>
|
||||
|
||||
<style type="text/css">
|
||||
#myDiv {
|
||||
#myGraph {
|
||||
width: "100%";
|
||||
height: 500;
|
||||
}
|
||||
.keys {
|
||||
#keysTable > tbody > tr > th {
|
||||
padding: 15px;
|
||||
font-weight: 800;
|
||||
}
|
||||
#keysTable > tbody > tr > td {
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
select.keys {
|
||||
display: block;
|
||||
height: 35px;
|
||||
margin: 20px 0px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
#keysContainer {
|
||||
float: left;
|
||||
width: 340px;
|
||||
}
|
||||
#keysContainer input {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
#keysContainer tr {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#attributesContainer {
|
||||
float: right;
|
||||
width: calc(100% - 550px);
|
||||
}
|
||||
#mainForm {
|
||||
height: 300px;
|
||||
padding: 20px;
|
||||
}
|
||||
* {
|
||||
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
font-weight: 300;
|
||||
}
|
||||
td:first-child {
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
</html>
|
||||
|
||||
@@ -19,7 +19,7 @@ get "/data" do
|
||||
JSON.pretty_generate(api.fetch(
|
||||
key: params.fetch("key"),
|
||||
group_by: params.fetch("group_by", "month"),
|
||||
start_date: params.fetch("start_date", "2019-04")
|
||||
start_date: params.fetch("start_date", ENV["DEFAULT_MIN_DATE"].strip)
|
||||
))
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user