var graphs = {} graphs.calendar = function (claims, id, from, thru) { var selector = '#' + id; var results = helpers.pdc.calc(claims, from, thru); var calendar = {} for (var i = 0; i <= helpers.dates.toNumber(thru, from); i++) { calendar[helpers.dates.toString(i, from)] = results.calendar[i] } const texture = textures .lines() .thicker() .stroke("#EDEDEC"); var colors = { '-1': texture.url(), //'#EDEDEC', '0': '#FF1919', '1': '#00B233', '9': '#8A8989' } var captions = function (d) { var obj = { '-1': 'Prior to Index', '0': 'Not Covered', '1': 'Covered', '9': 'IP Stay' }; return obj[calendar[d]]; }; var weeksInMonth = function(month){ var m = d3.time.month.floor(month) return d3.time.weeks(d3.time.week.floor(m), d3.time.month.offset(m,1)).length; } var minDate = moment(from).toDate(); var maxDate = moment(thru).toDate(); var cellResizeFactor = .7, cellMargin = 2 * cellResizeFactor, cellSize = 20 * cellResizeFactor; var day = d3.time.format("%w"), week = d3.time.format("%U"), format = d3.time.format("%Y-%m-%d"), titleFormat = d3.time.format("%Y-%m-%d"); monthName = d3.time.format("%B"), months= d3.time.month.range(d3.time.month.floor(minDate), maxDate); d3.select(selector).selectAll("svg").remove(); var svg = d3.select(selector).selectAll("svg") .data(months) .enter().append("svg") .attr("class", "month") .attr("height", ((cellSize * 7) + (cellMargin * 8) + 20) ) // the 20 is for the month labels .attr("width", function(d) { var columns = weeksInMonth(d); return ((cellSize * columns) + (cellMargin * (columns + 1)) + 25); }) .append("g") svg.append("text") .attr("class", "month-name") .attr("y", (cellSize * 7) + (cellMargin * 8) + 15 ) .attr("x", function(d) { var columns = weeksInMonth(d); return (((cellSize * columns) + (cellMargin * (columns + 1))) / 2); }) .attr("text-anchor", "middle") .text(function(d) { return monthName(d).substring(0,3); }) var tip = d3.tip() .attr('class', 'd3-tip') .style('line-height', 1) .style('padding', '12px') .style('background', 'rgba(0, 0, 0, 0.8)') .style('color', '#fff') .style('border-radius', '2px') .style('pointer-events', 'none') .style('z-index', 1000) .html(function(d, i, datum) { var html = '' + captions(d) + '
' + '' + d + ''; return html; }); console.log(cellSize); var rect = svg.selectAll("rect.day") .data(function(d, i) { return d3.time.days(d, new Date(d.getFullYear(), d.getMonth()+1, 1)); }) .enter().append("rect") .attr("class", "day") .attr("width", cellSize) .attr("height", cellSize) .attr("rx", 3).attr("ry", 3) // rounded corners .attr("fill", '#eaeaea') // default light grey fill .attr("y", function(d) { return (day(d) * cellSize) + (day(d) * cellMargin) + cellMargin; }) .attr("x", function(d) { return ((week(d) - week(new Date(d.getFullYear(),d.getMonth(),1))) * cellSize) + ((week(d) - week(new Date(d.getFullYear(),d.getMonth(),1))) * cellMargin) + cellMargin ; }) .on("mouseover", tip.show) .on("mouseout", tip.hide) .datum(format); rect.filter(function(d) { return d in calendar; }) .style("fill", function(d) { return colors[calendar[d]];}); svg.call(texture).call(tip); } graphs.timeline = function (claims, id, from, thru) { var selector = '#' + id; var result = helpers.pdc.calc(claims, from, thru); var data = []; claims.forEach(function (item) { var row = { claim: item, class: item.drug, label: item.drug, times: [] } if(item.overlap) { row.times.push({ class: item.drug, id: "overlap", "starting_time": moment(item.dateOfFill).valueOf(), "ending_time": moment(item.dateOfFirstDose).add(1, 'day').subtract(1, 'second').valueOf(), }) } row.times.push({ class: item.drug, "starting_time": moment(item.dateOfFirstDose).valueOf(), "ending_time": moment(item.dateOfLastDose).add(1, 'day').subtract(1, 'second').valueOf(), }) data.push(row); }) var colorScale = d3.scale.category10(); var patterns = {}; var classes = _.chain(data) .map(function (item) { return item.class }).uniq(false) .forEach(function (item) { patterns[item] = textures .lines() .thicker() .stroke(colorScale(item)) }); var tip = d3.tip() .attr('class', 'd3-tip') .style('line-height', 1) .style('padding', '12px') .style('background', 'rgba(0, 0, 0, 0.8)') .style('color', '#fff') .style('border-radius', '2px') .style('pointer-events', 'none') .style('z-index', 1000) .html(function(d, i, datum) { if (d.id == "overlap") { var html = 'Overlapping Fills
' + 'Date of Fill: ' + datum.claim.dateOfFill + '
' + 'Overlap: ' + datum.claim.overlap + ' days
' + 'Date of First Dose: ' + datum.claim.dateOfFirstDose + ''; } else { var html = 'Days Covered
' + 'Date of Fill: ' + datum.claim.dateOfFill + '
' + 'Date of Last Dose: ' + datum.claim.dateOfLastDose + ''; } return html; }); var chart = d3.timeline() .beginning(moment(from).valueOf()) .ending(moment(thru).valueOf()) .colors(colorScale) .colorProperty('class') .tickFormat({ format: d3.time.format("%m-%d"), tickTime: d3.time.day, tickInterval: 45, tickSize: 6 }) .orient('bottom') .itemHeight(30) .rowSeparators('black') .stack() .margin({left:150, right:0, top:0, bottom:0}) .rotateTicks(45) .mouseover(tip.show) .mouseout(tip.hide); d3.select(selector).selectAll("svg").remove() var svg = d3 .select(selector) .datum(data) .append("svg") .attr("height", 100 + data.length * 30) .attr("width", 650) .call(chart) .call(tip); _.forEach(patterns, function (key) { svg.call(key); }) svg .selectAll("#overlap") .style('fill', function (d) { return patterns[d.class].url() }); svg .selectAll("rect") .style('stroke', function (d) { return colorScale(d.class); }); svg .selectAll(".domain").remove(); } Vue.component('my-graph', { template: '
', props: { id: String, type: String, claims: Array, from: String, thru: String }, mounted: function () { this.visualize(); }, methods: { visualize: function () { graphs[this.type](this.claims, this.id, this.from, this.thru); } } });