block by zeffii f075d90310da09d3aab6d4e6439b27e7

Glucose - nested v4 - comments

Full Screen

sleep over time

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

milestones

forked from zeffii‘s block: sleep_tkd - nested v3 - comments

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:

  ....
  "YYYY/MM/DD": {
      "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("%Y/%m/%d");
var format_hours = d3.time.format("%H:%M");
var formatTime = d3.time.format("%m / %d");
  
d3.json("times.json", function(error, times) {
  if (error) throw error;
  times = json_preprocessor(times);
  draw_graph(times);
});


function times_preprocessor(t){
  if (!t){return []}
  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;
    }
}

style.css

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

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

times.json

{
    "2016/04/13": {
        "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
            }
        ],
        "times": ""
    },
    "2016/04/14": {
        "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
            }
        ],
        "times": ""
    },
    "2016/04/15": {
        "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
            }
        ],
        "times": ""
    },
    "2016/04/16": {
        "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
            }
        ],
        "times": ""
    },
    "2016/04/17": {
        "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
            }
        ],
        "times": ""
    },
    "2016/04/18": {
        "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
            }
        ],
        "times": ""
    },
    "2016/04/19": {
        "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
            }
        ],
        "times": ""
    },
    "2016/04/20": {
        "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
            }
        ],
        "times": ""
    },
    "2016/04/21": {
        "comments": "",
        "glucose": [
            {
                "time": "07:54",
                "value": 7.38
            },
            {
                "time": "11:47",
                "value": 14.82
            },
            {
                "time": "17:18",
                "value": 13.99
            },
            {
                "time": "22:21",
                "value": 8.44
            }
        ],
        "times": ""
    },
    "2016/04/22": {
        "comments": "",
        "glucose": [
            {
                "time": "08:02",
                "value": 7.77
            },
            {
                "time": "12:47",
                "value": 11.93
            },
            {
                "time": "18:46",
                "value": 10.44
            },
            {
                "time": "22:19",
                "value": 11.55
            }
        ],
        "times": ""
    },
    "2016/04/23": {
        "comments": "",
        "glucose": [
            {
                "time": "08:22",
                "value": 8.49
            },
            {
                "time": "12:34",
                "value": 10.1
            },
            {
                "time": "18:12",
                "value": 14.99
            },
            {
                "time": "21:37",
                "value": 13.88
            }
        ],
        "times": ""
    },
    "2016/04/24": {
        "comments": "",
        "glucose": [
            {
                "time": "07:52",
                "value": 6.55
            },
            {
                "time": "11:55",
                "value": 10.88
            },
            {
                "time": "17:18",
                "value": 14.6
            },
            {
                "time": "21:41",
                "value": 17.87
            }
        ],
        "times": ""
    },
    "2016/04/25": {
        "comments": "",
        "glucose": [
            {
                "time": "08:04",
                "value": 7.83
            },
            {
                "time": "12:32",
                "value": 9.6
            },
            {
                "time": "17:25",
                "value": 17.98
            },
            {
                "time": "21:31",
                "value": 13.82
            }
        ],
        "times": ""
    },
    "2016/04/26": {
        "comments": "",
        "glucose": [
            {
                "time": "08:08",
                "value": 5.83
            },
            {
                "time": "12:10",
                "value": 11.21
            },
            {
                "time": "17:56",
                "value": 21.37
            },
            {
                "time": "22:04",
                "value": 10.82
            }
        ],
        "times": ""
    },
    "2016/04/27": {
        "comments": "",
        "glucose": [
            {
                "time": "08:09",
                "value": 6.11
            }
        ],
        "times": ""
    }
}