Built with blockbuilder.org
forked from zeffii's block: mediseen0.2
Built with blockbuilder.org
forked from zeffii's block: mediseen0.2
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> | |
<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> | |
/* | |
MIT license. Dealga McArdle. 2009-2015 | |
Rewrite in Progress. | |
Specifically written for people who have a difficult time | |
figuring out their meds. It's easy for family or a healthcare | |
professional to update and print when changes are made. | |
It parses a medicine descriptor string used to describe most | |
forms of medication. (Universal Medicine Descriptor Language). | |
only caviat is some meds have weird shapes. A solution is to | |
allow the medication descriptor to include a link to a bitmap. | |
Milestones | |
[x] parse csv | |
[x] draw time, number, medication name, dose | |
[x] parse UMDL as vector | |
[ ] parse IMGUR 5 or 7 digit code when no UMDL is present | |
[ ] write up formalized UMDL | |
*/ | |
d3.select("body").style("background-color", d3.rgb(255,255,255)) | |
var svg = d3.select("svg"); | |
var defs = svg.append("defs"); | |
var group1 = svg.append("g").classed("group1", true); | |
var group2 = svg.append("g").classed("group2", true); | |
var group3 = svg.append("g").classed("group2", true); | |
var global_transform = "translate(" + [40, 63] + ")"; | |
group1.attr("transform", global_transform) | |
group2.attr("transform", global_transform) | |
group3.attr("transform", global_transform) | |
d3.csv("medilist.csv", generateList); | |
var data = []; | |
function generateList(data){ | |
data = data; | |
var line_height = 23, | |
padding_y = 13, | |
padding_x = 130, | |
type_height = 19, | |
time_height = 29, | |
divider_height = 6, | |
divider_width = 291, | |
ty = 30, | |
my = ty, | |
vert_lines_x_offset = 259, | |
outline_width = {"stroke-width": 0.7 + "px"}, | |
div_style = {"fill": "#424242", "stroke": "none"}, | |
med_style = {"fill": "#837979", "stroke": "none", "font-family": "sans-serif"}, | |
dose_style = {"fill": "#77A2CA", "stroke": "none", "font-family": "sans-serif"}, | |
text_styles = [med_style, dose_style]; | |
function assemble_med_list(data, med_list, time_list){ | |
data.forEach(function(d){ | |
if (d.time in med_list) med_list[d.time].push(d) | |
else { | |
med_list[d.time] = [d] | |
time_list.push(d.time) | |
} | |
}) | |
return [med_list, time_list] | |
} | |
var med_and_time_list = assemble_med_list(data, {}, []); | |
var med_list = med_and_time_list[0]; | |
var time_list = med_and_time_list[1]; | |
function draw_text(text, size, pos, style){ | |
group1.append('text') | |
.text(text) | |
.attr(pos) | |
.style(style) | |
.style({"font-size": size}) | |
} | |
function draw_text2(text, size, pos){ | |
group1.append('text') | |
.text(text.med) | |
.attr(pos) | |
.style(med_style) | |
.style({"font-size": size}) | |
.append('tspan') | |
.text(" " + text.dose) | |
.style(dose_style) | |
} | |
function draw_divider(pos){ | |
group1.append("rect") | |
.attr(pos) | |
.attr({height: divider_height, width: divider_width}) | |
.style(div_style) | |
} | |
function _adjust(ty){ return ty-35 } | |
function buffer_space(n){ return (2 * padding_y) + (n * line_height) } | |
function parse_umdl(d){ | |
var descr_array, | |
descr_object, | |
descr = d.descriptor, | |
descr_length = descr.length; | |
// maybe enforcing this leads to unnecessary complexity | |
// but i like the way it helps keep the description together | |
// optically at least in the csv | |
if ((descr[0] != "(") || (descr[descr_length-1] != ")")){ | |
console.error(descr) | |
console.error("descriptor must be enclosed in parentheses") | |
return | |
} | |
// strip parentheses | |
descr = descr.slice(1, descr_length-1) | |
// split into color(s)|shape|[groovetype|]inscription object | |
descr_array = descr.split("|") | |
if (descr_array.length === 3){ | |
descr_object = { | |
color: descr_array[0].split("/"), | |
shape: descr_array[1], | |
inscription: descr_array[2], | |
numparams: 3 | |
} | |
} | |
else if (descr_array.length === 4){ | |
descr_object = { | |
color: descr_array[0].split("/"), | |
shape: descr_array[1], | |
groovetype: descr_array[2], | |
inscription: descr_array[3], | |
numparams: 4 | |
} | |
} | |
else { | |
var error_message = "descriptor must take form: \n"; | |
error_message += "color(s)|shape|groovetype|inscription\n" | |
error_message += "or\n" | |
error_message += "color|shape|inscription" | |
console.error(error_message) | |
return | |
} | |
return descr_object | |
} | |
function console_debug(d, medicine_object){ | |
console.log("______") | |
console.log(d.descriptor) | |
console.log("color(s):", medicine_object.color) | |
console.log("shape:", medicine_object.shape) | |
if (medicine_object.numparams === 4) | |
console.log("groove type:", medicine_object.groovetype) | |
if (medicine_object.inscription != "_") | |
console.log("inscription:", medicine_object.inscription) | |
} | |
function draw_from_umdl(d, pos){ | |
var medicine_object = parse_umdl(d); | |
// check if this parsed a valid umdl | |
if (!medicine_object){ | |
console.error(d, "didn't parse correctly") | |
// draw questionmark? | |
return | |
} | |
var show_debug = [false, true][0]; | |
if (show_debug) console_debug(d, medicine_object) | |
draw_medicine(medicine_object, pos) | |
} | |
c_list = { | |
"D-pink": "#F171E7" | |
// add custom colors | |
}; | |
function color_list_check(c_in){ | |
if (c_in in c_list) return c_list[c_in] | |
else { | |
console.error(c_in, "not in color list (c_list), add it!") | |
return "#fff" | |
} | |
} | |
function draw_liteline(pos){ | |
// function draw_text(text, size, pos, style){ | |
var y_tweak = 4; | |
var line_start_x = 239; | |
var line_width_x = 630; | |
var line_height_y = type_height+4; | |
group1.append("line") | |
.attr(pos) | |
.attr({x1: line_start_x, y1:0+y_tweak, x2: line_start_x + line_width_x, y2: 0+y_tweak}) | |
.style({"stroke-width": "1px", "stroke": "#eee"}) | |
group1.append("line") | |
.attr(pos) | |
.attr({x1: line_start_x, y1: -line_height_y + y_tweak, x2: line_start_x + line_width_x, y2: -line_height_y + y_tweak}) | |
.style({"stroke-width": "1px", "stroke": "#eee"}) | |
} | |
function draw_vertline(x, total_height){ | |
var line_start_y = -4; | |
group1.append("line") | |
.attr({"transform": "translate(" + [x, 0] + ")"}) | |
.attr({x1: x, y1:line_start_y, x2: x, y2: line_start_y + total_height}) | |
.style({"stroke-width": "1px", "stroke": "#aaa"}) | |
} | |
function draw_medicine(mo, pos){ | |
var med = group2.append("g") | |
var med_r = type_height/2; | |
var shape = mo.shape.toLowerCase(); | |
var size = shape.indexOf("small") >= 0 ? "small" : "normal"; | |
if (size==="small") med_r *= 0.8 | |
// round & round-small | |
if (shape.indexOf("round") >= 0){ | |
med.append("circle") | |
.attr({"cx": pos.x, "cy": pos.y-med_r/2, "r": med_r}) | |
.style({stroke: "#121212", "fill": mo.color[0]}) | |
.style(outline_width) | |
} | |
// oval | |
if (shape.indexOf("oval") >= 0){ | |
var oval_color = color_list_check(mo.color[0]); | |
med.append("ellipse") | |
.attr({"cx": pos.x, "cy": pos.y-med_r/2, | |
"rx": med_r*1.5, "ry": med_r}) | |
.style({stroke: "#121212", "fill": oval_color}) | |
.style(outline_width) | |
} | |
if (mo.groovetype){ | |
var groove = mo.groovetype.toLowerCase(); | |
var g_width = 1; | |
if (groove === "brkthin") g_width = 1 | |
if (groove === "brkwide") g_width = 4 | |
var groove_style = { | |
"stroke": "#000", | |
"stroke-width": g_width +"px", | |
"stroke-opacity": 0.6 | |
}; | |
med.append('line') | |
.attr({x1: pos.x, y1: pos.y, x2: pos.x, y2: pos.y-(med_r*2)}) | |
.style(groove_style) | |
.attr("transform", "translate("+ [0, med_r/2] + ")") | |
if (groove === "brkx"){ | |
med.append('line') | |
.attr({x1: pos.x-med_r, y1: pos.y-(med_r), x2: pos.x+med_r, y2: pos.y-(med_r)}) | |
.style(groove_style) | |
.attr("transform", "translate("+ [0, med_r/2] + ")") | |
} | |
} | |
function side_(dir, med_r){ | |
var th = med_r, | |
two_th = med_r*2, | |
straight_1 = "M" + [0,0,-th*dir,0].join(" "), | |
curve = "C" + [-two_th*dir, 0, -two_th*dir, -two_th, -th*dir, -two_th].join(" "), | |
straight_2 = "L" + [0,-two_th].join(" "); | |
return straight_1 + curve + straight_2; | |
} | |
// capsule | |
if (shape==="capsule"){ | |
var cap = {x: pos.x, y: pos.y+(med_r/2)}; | |
med.append("path") | |
.attr("d", side_(1, med_r)) | |
.style({stroke: "#121212", "fill": mo.color[0]}) | |
.style(outline_width) | |
med.append("path") | |
.attr("d", side_(-1, med_r)) | |
.style({stroke: "#121212", "fill": mo.color[1]}) | |
.style(outline_width) | |
med.attr("transform", "translate(" + [cap.x, cap.y] + ")") | |
} | |
// inscriptions | |
if (mo.inscription != "_"){ | |
group3.append('text') | |
.attr({x: pos.x + 30, y: pos.y}) | |
.text(mo.inscription) | |
.style(med_style) | |
} | |
} | |
var end_height = 0; | |
time_list.forEach(function(d){ | |
my = ty; | |
draw_divider({x: padding_x, y: _adjust(ty)}) | |
var num_items = med_list[d].length; | |
var extra_space = buffer_space(num_items); | |
var text_y = ty + (extra_space / 2) - (padding_y * 1.4); | |
// draw time | |
draw_text(d, time_height, {x: 37, y: text_y-5}, med_style) | |
ty += extra_space | |
// draw details for each | |
med_list[d].forEach(function(m){ | |
draw_text(m.num, type_height, {x: 152, y: my}, med_style) | |
draw_text2(m, type_height, {x: 172, y: my}) | |
draw_liteline({"transform": "translate(" + [172, my] + ")"}) | |
draw_from_umdl(m, {x: 389, y: my}) | |
my += line_height | |
}) | |
end_height = my; | |
}) | |
draw_divider({x: padding_x, y: _adjust(ty)}) | |
// draw vertical markings | |
'Za Zo Ma Di Wo Do Vr'.split(' ').forEach(function(d, i){ | |
// draw_vertline(x, total_height) | |
var twinky = 35; | |
var line_x = vert_lines_x_offset + 36*i; | |
draw_vertline(line_x, end_height); | |
draw_text(d, 14, {x: line_x+287+(twinky*i), y: 0}) | |
console.log(d) | |
}) | |
console.log("______\nscript finished") | |
/* EOF */ | |
} | |
</script> | |
</body> |
time | med | num | dose | descriptor | |
---|---|---|---|---|---|
08:00 | Nexium | 1 | 40mg | (D-pink|oval|AEl 40mg) | |
08:00 | Thyrax | 1 | 0.025mg | (#45D0FC|round|brkwide|ZK 5) | |
08:00 | Thyrax | 1 | 0.10mg | (white|round|brkwide|ZK 2) | |
08:00 | Hydrocortison | 1 | 10mg | (white|round-small|brkthin|fullname) | |
08:00 | Hydrocortison | 1 | 5mg | (white|round|brkx|fullname) | |
08:00 | Furosemide | 1 | 20mg | (white|round|FUR.20) | |
08:00 | BREAKFAST | - | -- | (--) | |
08:00 | Spironolacton | 1 | 100mg | (white|round|AF) | |
08:00 | Metformine | 1 | 500mg | (white|round|_) | |
08:00 | Ursofalk | 2 | 250mg | (white/white|capsule|_) | |
08:00 | Lyrica | 1 | 150mg | (white/white|capsule|PGN 150) | |
10:00 | Desmopressin | 1 | 0.1mg | (white|round|brkthin|0.1) | |
18:00 | Furosemide | 1 | 20mg | (white|round|FUR.20) | |
18:00 | DINNER | - | -- | (--) | |
18:00 | Metformine | 1 | 500mg | (white|round|_) | |
18:00 | Ursofalk | 2 | 250mg | (white/white|capsule|_) | |
22:00 | Hydrocortison | 1 | 5mg | (white|round|brkx|fullname) | |
22:00 | Desmopressin | 1 | 0.1mg | (white|round|brkthin|0.1) | |
22:00 | SLEEP | - | -- | (--) |
Universal Medicine Descriptor Language | Revision B, Draft | |
Consider describing any medication using a set of rules: | |
color(s)|shape|groovetype|inscription | |
color|shape|inscription |