Skip to content

Instantly share code, notes, and snippets.

@zeffii
Last active April 21, 2016 10:44
Show Gist options
  • Save zeffii/8467c2c06517ff17418ff459fa5b0c83 to your computer and use it in GitHub Desktop.
Save zeffii/8467c2c06517ff17418ff459fa5b0c83 to your computer and use it in GitHub Desktop.
sleep_tkd - nested v3 - comments

sleep over time

Charting sleep periods over the course of a number of days.

  • vertical axis day
  • horizontal time of day

milestones

  • each day gets unique track
/*
MIT License 2016 - Dealga McArdle.
=================================
For now this is moderately light usage of d3.js - maybe to the chagrin of more weathered
d3.js masters - simply to illustrate a problem i'm facing.
typical json to parse:
....
"01/04/2016": {
"sleeptimes": "00:00->07:00,15:20->18:30,23:00->24:00",
"comments": "AMC visit, Cystoscopy - all clear",
"glucose": [
{"time": "06:35", "value": 6.94},
{"time": "15:18", "value": 14.93},
{"time": "20:28", "value": 11.77},
{"time": "22:13", "value": 12.71}
]
},
....
- could color grade using 0-5 5-10 10-15 15-20 20-25 25-up
intentionally left blank.
*/
function get_ratio_from_time(time_str){
// this function converts a time_str into how far into the day it is
// f.ex 12:00 => 0.5 06:00 => 0.25
var time_parts = time_str.split(':');
var a = +time_parts[0];
var b = +time_parts[1];
return (1/1440*((a*60) + b))
}
function get_color(tval){
if (tval < 5.0){return {bg:"#18dff5", tx: "#111"}}
else if (tval >= 5.0 && tval < 10.0){return {bg:"#27f518", tx: "#111"}}
else if (tval >= 10.0 && tval < 15.0){return {bg:"#c8f97f", tx: "#111"}}
else if (tval >= 15.0 && tval < 20.0){return {bg:"#ffb76b", tx: "#111"}}
else if (tval >= 20.0){return {bg:"#fe70bc", tx: "#ffffff"}}
}
var svg = d3.select("svg")
var format_day = d3.time.format("%d/%m/%Y");
var format_hours = d3.time.format("%H:%M");
var formatTime = d3.time.format("%m / %d"); // formatTime(new Date); // "June 30, 2015"
d3.json("times.json", function(error, times) {
if (error) throw error;
times = json_preprocessor(times);
draw_graph(times);
});
function times_preprocessor(t){
var ts = t.split(',');
var emb = [];
for (var k of ts){
var abl = k.split('->');
if (abl.length === 2){ emb.push(abl); }
}
return emb;
}
function json_preprocessor(p){
var new_object_array = [];
for (var key in p) {
if (p.hasOwnProperty(key)) {
var day_datum = format_day.parse(key);
var time_object = p[key];
var processed_times = times_preprocessor(time_object.sleeptimes);
new_object_array.push({
day: day_datum,
times: processed_times,
comments: time_object.comments,
glucose: time_object.glucose
});
}
}
return new_object_array;
}
function draw_graph(times){
var margin = {top: 20, right: 80, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
svg
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var main_group = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var tracks = main_group.append('g').classed('tracks', true)
var yindex = 0;
var begin_time, end_time;
var tab_height = 12;
var bar_height = 15;
var tab_ear = 4;
var vertical_skip = 17;
var time_offset_down = 6;
for (var item of times){
var mg = tracks.append('g');
for (var time_slot of item.times){
begin_time = get_ratio_from_time(time_slot[0]);
end_time = get_ratio_from_time(time_slot[1]);
var rec = mg.append('rect');
rec.attr("width", (end_time - begin_time) * width)
.attr("height", bar_height)
.style({fill: "#badcfc"})
.attr("transform", function(d){
return "translate(" + [
begin_time * width,
yindex * vertical_skip
] + ")"
})
}
var gg = tracks.append('g');
for (var reading of item.glucose){
var gtime = get_ratio_from_time(reading.time);
var gval = reading.value;
var ggroup = gg.append('g');
ggroup.attr({
transform: "translate(" + [0, (yindex * vertical_skip + 8)] + ")"})
var cl = ggroup.append('rect');
cl.attr({transform: "translate(" + [(gtime * width) + tab_ear , -6] +")"})
.attr({'height': tab_height})
.style({fill: get_color(gval).bg})
var cl2 = ggroup.append('path');
cl2.attr({transform: "translate(" + [(gtime * width) + tab_ear , -6] +")"})
.attr({'d': 'M' + [0,0,-tab_ear,tab_height/2,0,tab_height] + 'z'})
.style({fill: get_color(gval).bg})
var cltext = ggroup.append('text');
cltext.text(gval)
.attr({transform: "translate(" + [(gtime * width) + tab_ear , 4] +")"})
.attr({
'text-anchor': "start",
"font-size": 11,
"font-family": "sans-serif"
})
cltext.style({'fill': get_color(gval).tx })
var textwidth = cltext.node().getComputedTextLength();
cl.attr({'width': textwidth})
}
// draw comments
if (item.comments.length > 0){
var comment_group = mg.append('g');
comment_group.attr({
transform: "translate(" + [width + 20, yindex * vertical_skip] + ")"
})
var newrec = comment_group.append('rect')
newrec.attr('width', 20).attr('height', 13)
newrec.style({fill: "#fce7ba"})
}
yindex += 1;
mg.append('text')
.text(formatTime(item.day))
.attr("transform", "translate(-21," + (yindex * vertical_skip - 7) + ")")
.attr({'text-anchor': "middle", "font-size": 10, "font-family": "sans-serif"})
}
var indicat = main_group.append('g').classed('indications', true);
var ditimes = "00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24".split(" ");
var circle_push_vertical = 1.593856; // <---- use to push hourly circles up / down
var time_index = 0;
for (var tick of ditimes){
tick = tick + ":00";
var tgl = indicat.append('g');
var tl = tgl.append('line');
var xpostime = Math.floor(get_ratio_from_time(tick) * width);
tl.attr('x1', xpostime)
.attr('x2', xpostime)
.attr('y1', 0)
.attr('y2', height)
.style({"stroke-width": 1, stroke: "#cfdbe7"})
if (time_index % 3 === 0){
tl.style({"stroke-width": 1, stroke: "#aabfd4"})
var tcl = tgl.append('circle');
tcl.attr('cx', Math.floor(get_ratio_from_time(tick) * width))
.attr('cy', height/circle_push_vertical)
.attr('r', 16)
.style({fill: "#e0eeff"})
var txl = tgl.append('text');
txl.attr({
'text-anchor': "middle",
"font-size": 17,
"font-family": "sans-serif"
})
.attr(
'transform',
'translate(' + [
Math.floor(get_ratio_from_time(tick) * width),
(height/circle_push_vertical)+time_offset_down ] +
')')
.style({'fill': "#7c7c7c"})
.text(tick.slice(0,2))
}
time_index += 1;
}
}
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="https://d3js.org/d3-time.v0.2.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css" />
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
svg { width:100%; height: 100% }
</style>
</head>
<body>
<svg></svg>
<script src="dillitant.js"></script>
</body>
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis text {
font: 10px sans-serif;
}
{
"07/04/2016": {
"sleeptimes": "",
"comments": "",
"glucose": [
{"time": "21:53", "value": 22.92},
{"time": "17:57", "value": 19.59},
{"time": "12:12", "value": 19.26},
{"time": "08:06", "value": 5.72}
]
},
"08/04/2016": {
"sleeptimes": "",
"comments": "",
"glucose": [
{ "time": "07:11", "value": 9.44 },
{ "time": "11:44", "value": 15.82 },
{ "time": "18:25", "value": 18.87 },
{ "time": "22:13", "value": 21.87 }
]
},
"09/04/2016": {
"sleeptimes": "",
"comments": "",
"glucose": [
{ "time": "07:36", "value": 12.99 },
{ "time": "07:38", "value": 12.49 },
{ "time": "11:42", "value": 17.43 },
{ "time": "17:22", "value": 15.82 },
{ "time": "21:45", "value": 8.1 }
]
},
"10/04/2016": {
"sleeptimes": "",
"comments": "",
"glucose": [
{ "time": "08:08", "value": 6.99 },
{ "time": "12:23", "value": 15.6 },
{ "time": "15:51", "value": 14.32 },
{ "time": "18:37", "value": 11.55 },
{ "time": "23:13", "value": 14.99 }
]
},
"11/04/2016": {
"sleeptimes": "",
"comments": "",
"glucose": [
{
"time": "07:44",
"value": 9.38
},
{
"time": "12:02",
"value": 15.21
},
{
"time": "17:41",
"value": 16.98
},
{
"time": "22:13",
"value": 12.1
}
]
},
"12/04/2016": {
"sleeptimes": "",
"comments": "",
"glucose": [
{
"time": "07:05",
"value": 6.83
},
{
"time": "11:28",
"value": 15.87
},
{
"time": "17:52",
"value": 20.7
},
{
"time": "22:16",
"value": 20.37
}
]
},
"13/04/2016": {
"sleeptimes": "",
"comments": "",
"glucose": [
{
"time": "07:46",
"value": 8.77
},
{
"time": "11:18",
"value": 12.88
},
{
"time": "17:25",
"value": 15.65
},
{
"time": "21:24",
"value": 23.81
}
]
},
"14/04/2016": {
"sleeptimes": "",
"comments": "",
"glucose": [
{
"time": "08:18",
"value": 7.49
},
{
"time": "12:56",
"value": 9.1
},
{
"time": "15:27",
"value": 15.38
},
{
"time": "15:29",
"value": 19.7
},
{
"time": "20:59",
"value": 17.15
}
]
},
"15/04/2016": {
"sleeptimes": "",
"comments": "",
"glucose": [
{"time": "08:08", "value": 7.55},
{"time": "12:26", "value": 14.82},
{"time": "17:27", "value": 19.21},
{"time": "21:43", "value": 14.6}
]
},
"16/04/2016": {
"sleeptimes": "",
"comments": "",
"glucose": [
{"time": "07:33", "value": 8.38},
{"time": "11:54", "value": 12.49},
{"time": "17:17", "value": 12.1},
{"time": "21:27", "value": 12.6}
]
},
"17/04/2016": {
"sleeptimes": "",
"comments": "",
"glucose": [
{"time": "08:03", "value": 8.1},
{"time": "13:12", "value": 16.6},
{"time": "17:55", "value": 15.71},
{"time": "22:18", "value": 10.44}
]
},
"18/04/2016": {
"sleeptimes": "",
"comments": "",
"glucose": [
{"time": "08:10", "value": 7.77},
{"time": "12:49", "value": 12.77},
{"time": "18:17", "value": 10.55},
{"time": "22:03", "value": 14.49}
]
},
"19/04/2016": {
"sleeptimes": "",
"comments": "",
"glucose": [
{"time": "08:20", "value": 6.88},
{"time": "12:41", "value": 13.65},
{"time": "17:06", "value": 16.49},
{"time": "22:04", "value": 11.6}
]
},
"20/04/2016": {
"sleeptimes": "",
"comments": "",
"glucose": [
{"time": "08:28", "value": 5.66},
{"time": "13:03", "value": 11.93},
{"time": "18:01", "value": 13.04},
{"time": "22:25", "value": 12.49}
]
},
"21/04/2016": {
"sleeptimes": "",
"comments": "",
"glucose": [
{"time": "07:54", "value": 7.38},
{"time": "11:47", "value": 14.82}
]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment