block by zeffii 4a7b3135dd1dfba1e1e69d575a68bcd5

sleep_tkd - nested v2

Full Screen

sleep over time

http://blockbuilder.org/zeffii/4a7b3135dd1dfba1e1e69d575a68bcd5

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

milestones

forked from zeffii‘s block: sleep_tkd - nested

index.html

<!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>

dillitant.js

/*

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 bar_height = 15;
    var vertical_skip = 17;

    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 , -6] +")"})
              .attr({'height': 12})
              .style({fill: get_color(gval).bg})
            var cltext = ggroup.append('text');
            cltext.text(gval)
              .attr({transform: "translate(" + [gtime * width , 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})
        }
      
      
        yindex += 1; 
        mg.append('text')
        .text(formatTime(item.day))
        .attr("transform", "translate(-21," + (yindex * vertical_skip - 3) + ")")
        .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.36;
    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)+5 ] + 
                ')')
             .style({'fill': "#7c7c7c"})
             .text(tick.slice(0,2))
        
        }
        time_index += 1;
    }
}

style.css

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.axis text {
  font: 10px sans-serif;
}

times.json

{
  "21/03/2016": {
      "sleeptimes": "10:30->12:00,13:45->15:00,16:21->17:30,21:15->22:15,22:25->24:00",
      "comments": "",
      "glucose": [
        {"time": "06:32", "value": 10.16},
        {"time": "12:12", "value": 16.76},
        {"time": "17:43", "value": 18.76},
        {"time": "22:13", "value": 22.42}
      ]
  },
  "22/03/2016": {
      "sleeptimes": "00:00->01:00,01:05->02:00,02:05->03:53,04:05->05:30,09:45->12:00,14:20->16:45,19:45->22:00,22:40->24:00",
      "comments": "",
      "glucose": [
        {"time": "06:32", "value": 8.49},
        {"time": "12:00", "value": 20.32},
        {"time": "17:43", "value": 21.59},
        {"time": "22:19", "value": 19.48}
      ]
  },
  "23/03/2016": {
      "sleeptimes": "00:00->01:15,01:20->07:12,09:45->12:00,16:00->17:45,20:15->22:00,22:15->24:00",
      "comments": "",
      "glucose": [
        {"time": "07:23", "value": 7.27},
        {"time": "12:02", "value": 15.54},
        {"time": "17:53", "value": 19.43},
        {"time": "22:27", "value": 21.15}
      ]
  },
  "24/03/2016": {
      "sleeptimes": "00:00->05:10,05:20->07:00,09:40->11:10,13:52->16:00,19:30->21:30,23:10->24:00",
      "comments": "",
      "glucose": [
        {"time": "07:17", "value": 9.6},
        {"time": "12:13", "value": 22.26},
        {"time": "17:11", "value": 19.98},
        {"time": "23:02", "value": 17.65}
      ]
  },
  "25/03/2016": {
      "sleeptimes": "00:00->07:00,09:22->11:15,12:45->14:40,17:30->17:50,20:00->20:53,22:20->24:00",
      "comments": "",
      "glucose": [
        {"time": "07:10", "value": 6.11},
        {"time": "11:57", "value": 10.27},
        {"time": "17:47", "value": 23.37},
        {"time": "21:56", "value": 23.37}
      ]
  },
  "26/03/2016": {
      "sleeptimes": "00:00->07:00,09:20->11:45,14:15->17:30,22:10->24:00",
      "comments": "",
      "glucose": [
        {"time": "07:02", "value": 8.6},
        {"time": "11:40", "value": 20.43},
        {"time": "17:36", "value": 20.32},
        {"time": "21:34", "value": 17.32}
      ]
  },
  "27/03/2016": {
      "sleeptimes": "00:00->06:00,10:50->11:45,13:20->14:52,16:30->17:00,19:00->20:44,22:15->24:00",
      "comments": "",
      "glucose": [
        {"time": "06:40", "value": 9.05},
        {"time": "10:44", "value": 15.71},
        {"time": "16:31", "value": 21.31},
        {"time": "21:03", "value": 17.93}
      ]
  },  
  "28/03/2016": {
      "sleeptimes": "00:00->06:00,22:15->24:00",
      "comments": "",
      "glucose": [
        {"time": "06:23", "value": 6.88},
        {"time": "10:15", "value": 13.04},
        {"time": "16:43", "value": 13.93},
        {"time": "20:26", "value": 15.38}
      ]
  },
  "29/03/2016": {
      "sleeptimes": "00:00->07:00,16:00->18:50,21:20->24:00",
      "comments": "AMC visit Dr Soeters, Novomix 78-48",
      "glucose": [
        {"time": "07:29", "value": 7.72},
        {"time": "12:40", "value": 13.99},
        {"time": "19:07", "value": 19.15},
        {"time": "21:22", "value": 19.04}
      ]
  },
  "30/03/2016": {
      "sleeptimes": "00:00->06:30,09:30->11:30,15:30->18:15,22:00->24:00",
      "comments": "",
      "glucose": [
        {"time": "07:18", "value": 5.77},
        {"time": "11:31", "value": 20.87},
        {"time": "17:33", "value": 15.43},
        {"time": "20:40", "value": 15.43}
      ]
  },
  "31/03/2016": {
      "sleeptimes": "00:00->05:30,08:00->10:30,11:15->13:00,15:20->16:20,17:20->17:45,20:30->22:45,23:15->24:00",
      "comments": "",
      "glucose": [
        {"time": "06:00", "value": 5.83},
        {"time": "10:54", "value": 14.99},
        {"time": "17:07", "value": 18.26},
        {"time": "19:57", "value": 16.6}
      ]
  },
  "01/04/2016": {
      "sleeptimes": "00:00->06:35,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}
      ]
  },
  "02/04/2016": {
      "sleeptimes": "00:00->07:00,07:15->08:00,09:30->12:45,16:05->17:45,20:30->21:00,22:15->24:00",
      "comments": "",
      "glucose": [
        {"time": "08:21", "value": 9.27},
        {"time": "12:47", "value": 18.87},
        {"time": "18:14", "value": 17.76},
        {"time": "22:08", "value": 16.82}
      ]
  },
  "03/04/2016": {
      "sleeptimes": "00:00->06:30,08:10->11:30,14:30->18:30,21:00->22:40,23:00->24:00",
      "comments": "",
      "glucose": [
        {"time": "07:12", "value": 8.38},
        {"time": "11:50", "value": 12.88},
        {"time": "18:24", "value": 16.32},
        {"time": "22:44", "value": 11.93}
      ]
  },
  "04/04/2016": {
      "sleeptimes": "00:00->07:00,09:00->11:30,15:30->17:30,22:00->24:00",
      "comments": "",
      "glucose": [
        {"time": "07:38", "value": 9.21},
        {"time": "12:51", "value": 13.32},
        {"time": "18:13", "value": 13.1},
        {"time": "21:50", "value": 14.4}
      ]
  },
  "05/04/2016": {
      "sleeptimes": "00:00->06:50,09:20->12:00,13:50->15:30,21:50->24:00",
      "comments": "Grandad arrives from Ireland",
      "glucose": [
        {"time": "06:58", "value": 6.4},
        {"time": "12:51", "value": 17.2},
        {"time": "18:22", "value": 14.9},
        {"time": "21:37", "value": 21.8}
      ]
  },
  "06/04/2016": {
      "sleeptimes": "00:00->07:00,10:00->12:15,15:30->17:25,20:50->22:05,22:30->24:00",
      "comments": "",
      "glucose": [
        {"time": "22:17", "value": 14.99},
        {"time": "17:45", "value": 23.87},
        {"time": "12:22", "value": 16.71},
        {"time": "08:16", "value": 6.27}
      ]
  },
  "07/04/2016": {
      "sleeptimes": "00:00->07:00,07:10->08:00,10:30->12:05,15:50->17:30,22:05->24:00",
      "comments": "had fruit shortly before night-time measurement...|Nadifa increased NM to 82-50",
      "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": "00:00->07:00,9:00->11:15",
      "comments": "",
      "glucose": [
          {"time": "07:11", "value": 9.44}
      ]
  }   
}