diff --git a/app/app/urls.py b/app/app/urls.py index e872d359b65..5ab7a2c6065 100644 --- a/app/app/urls.py +++ b/app/app/urls.py @@ -776,18 +776,7 @@ re_path(r'^_administration/stats/$', dataviz.views.stats, name='stats'), re_path(r'^_administration/cohort/$', dataviz.views.cohort, name='cohort'), re_path(r'^_administration/funnel/$', dataviz.views.funnel, name='funnel'), - re_path(r'^_administration/viz/?$', dataviz.d3_views.viz_index, name='viz_index'), re_path(r'^_administration/mesh/?$', dataviz.d3_views.mesh_network_viz, name='mesh_network_viz'), - re_path(r'^_administration/viz/sunburst/(.*)?$', dataviz.d3_views.viz_sunburst, name='viz_sunburst'), - re_path(r'^_administration/viz/chord/(.*)?$', dataviz.d3_views.viz_chord, name='viz_chord'), - re_path(r'^_administration/viz/steamgraph/(.*)?$', dataviz.d3_views.viz_steamgraph, name='viz_steamgraph'), - re_path(r'^_administration/viz/circles/(.*)?$', dataviz.d3_views.viz_circles, name='viz_circles'), - re_path(r'^_administration/viz/sankey/(.*)?$', dataviz.d3_views.viz_sankey, name='viz_sankey'), - re_path(r'^_administration/viz/spiral/(.*)?$', dataviz.d3_views.viz_spiral, name='viz_spiral'), - re_path(r'^_administration/viz/heatmap/(.*)?$', dataviz.d3_views.viz_heatmap, name='viz_heatmap'), - re_path(r'^_administration/viz/calendar/(.*)?$', dataviz.d3_views.viz_calendar, name='viz_calendar'), - re_path(r'^_administration/viz/draggable/(.*)?$', dataviz.d3_views.viz_draggable, name='viz_draggable'), - re_path(r'^_administration/viz/scatterplot/(.*)?$', dataviz.d3_views.viz_scatterplot, name='viz_scatterplot'), url(r'^blocknative', perftools.views.blocknative, name='blocknative'), # quadratic lands diff --git a/app/assets/v2/images/dataviz/calendar.png b/app/assets/v2/images/dataviz/calendar.png deleted file mode 100644 index bc21970a5f3..00000000000 Binary files a/app/assets/v2/images/dataviz/calendar.png and /dev/null differ diff --git a/app/assets/v2/images/dataviz/chord.png b/app/assets/v2/images/dataviz/chord.png deleted file mode 100644 index 9cd2bbf0ee2..00000000000 Binary files a/app/assets/v2/images/dataviz/chord.png and /dev/null differ diff --git a/app/assets/v2/images/dataviz/circles.png b/app/assets/v2/images/dataviz/circles.png deleted file mode 100644 index bec8e37f690..00000000000 Binary files a/app/assets/v2/images/dataviz/circles.png and /dev/null differ diff --git a/app/assets/v2/images/dataviz/graph.png b/app/assets/v2/images/dataviz/graph.png deleted file mode 100644 index 8c3983b3b89..00000000000 Binary files a/app/assets/v2/images/dataviz/graph.png and /dev/null differ diff --git a/app/assets/v2/images/dataviz/heatmap.png b/app/assets/v2/images/dataviz/heatmap.png deleted file mode 100644 index 0af7d0a10cc..00000000000 Binary files a/app/assets/v2/images/dataviz/heatmap.png and /dev/null differ diff --git a/app/assets/v2/images/dataviz/progression.png b/app/assets/v2/images/dataviz/progression.png deleted file mode 100644 index 9b28a06e4f1..00000000000 Binary files a/app/assets/v2/images/dataviz/progression.png and /dev/null differ diff --git a/app/assets/v2/images/dataviz/sankey.png b/app/assets/v2/images/dataviz/sankey.png deleted file mode 100644 index 9e109f9b2a0..00000000000 Binary files a/app/assets/v2/images/dataviz/sankey.png and /dev/null differ diff --git a/app/assets/v2/images/dataviz/scatterplot.png b/app/assets/v2/images/dataviz/scatterplot.png deleted file mode 100644 index 95971be6b92..00000000000 Binary files a/app/assets/v2/images/dataviz/scatterplot.png and /dev/null differ diff --git a/app/assets/v2/images/dataviz/spiral.png b/app/assets/v2/images/dataviz/spiral.png deleted file mode 100644 index e9416798973..00000000000 Binary files a/app/assets/v2/images/dataviz/spiral.png and /dev/null differ diff --git a/app/assets/v2/images/dataviz/steamgraph.png b/app/assets/v2/images/dataviz/steamgraph.png deleted file mode 100644 index b669d8e5272..00000000000 Binary files a/app/assets/v2/images/dataviz/steamgraph.png and /dev/null differ diff --git a/app/assets/v2/images/dataviz/sunburst.png b/app/assets/v2/images/dataviz/sunburst.png deleted file mode 100644 index 00236125181..00000000000 Binary files a/app/assets/v2/images/dataviz/sunburst.png and /dev/null differ diff --git a/app/assets/v2/js/dataviz/dragit.css b/app/assets/v2/js/dataviz/dragit.css deleted file mode 100644 index 739d88be2ff..00000000000 --- a/app/assets/v2/js/dataviz/dragit.css +++ /dev/null @@ -1,51 +0,0 @@ -path.lineTrajectory, path.subTrajectory { - stroke-width: 2; - stroke-opacity: 0.2; - opacity: 0.5; - fill-opacity: 0.3; - stroke: gray; - fill: none; - pointer-events: none; -} - -path.lineTrajectoryMonotone { - stroke-width: 2; - fill: none; - pointer-events: none; - stroke: lightgray; -} - -circle.pointTrajectory { - pointer-events: none; - stroke: lightgray; - fill: black; - opacity: .5; -} - -line.lineClosestTrajectory { - stroke: black; - stroke-dasharray: 4,4; - display: block; -} - -line.lineClosestPoint { - stroke: black; - opacity: .1; - display: block; -} - -.selected { - opacity: .5; -} - -circle.pointClosestTrajectory { - opacity: .1; - display: block; -} - -circle.focusGuide { - opacity: 1; - display: block; - fill: none; - stroke: black; -} \ No newline at end of file diff --git a/app/assets/v2/js/dataviz/dragit.js b/app/assets/v2/js/dataviz/dragit.js deleted file mode 100644 index a9c5d18791a..00000000000 --- a/app/assets/v2/js/dataviz/dragit.js +++ /dev/null @@ -1,692 +0,0 @@ -(function() { - - var dragit = window.dragit || {}; - - window.dragit = dragit; - - dragit.version = '0.1.5'; - - var vars = { - 'dev': false, - evt: [], - tc: [], - list_closest_datapoint: [], - svgLine: null, - container: null, - accessor_x: function(d) { - return d[0]; - }, - accessor_y: function(d) { - return d[1]; - }, - custom_focus: 'default', - custom_trajectory: 'default', - playback: {el: null} - }; - - dragit.custom = {}; - dragit.trajectory = {}; - dragit.statemachine = {}; - dragit.utils = {}; - dragit.evt = {}; - dragit.mouse = {}; - dragit.time = {}; - dragit.object = {}; - dragit.data = []; - dragit.constraint = []; - dragit.playback = {}; - - dragit.statemachine = {current_state: 'idle', current_id: -1}; - - dragit.time = {min: 0, max: 0, current: 0, step: 1, previous: 0, offset: 0}; - dragit.mouse = {scope: 'focus'}; - dragit.object = {update: function() {}, offsetX: 0, offsetY: 0, dragging: 'absolute'}; - dragit.evt = {register: function() {}, call: function() {}}; - - dragit.custom.line = { - 'default': {'mark': 'svg:path', 'style': {'stroke': 'black', 'stroke-width': 2}, 'interpolate': 'linear'}, - 'monotone': {'mark': 'svg:path', 'style': {'stroke': 'black', 'stroke-width': 2}, 'interpolate': 'monotone'} - }; - - dragit.custom.point = { - 'default': {'mark': 'svg:circle', 'style': {'stroke': 'black', 'stroke-width': 2}, - 'attr': {'cx': vars.accessor_x, 'cy': vars.accessor_y, 'r': 3}, - 'attr_static': {'cx': -10, 'cy': -10, 'r': 3} - } - }; - - dragit.playback = {playing: false, loop: false, interpolation: 'none', speed: 1000}; - - vars.svgLine = d3.svg.line() - .x(vars.accessor_x) - .y(vars.accessor_y) - .interpolate(dragit.custom.line[vars.custom_trajectory].interpolate); - - dragit.evt.register = function(evt, f, d) { - - if (vars.dev) - console.log('[register]', evt); - - if (typeof evt == 'string') - evt = [evt]; - - evt.forEach(function(e) { - if (typeof vars.evt[e] == 'undefined') - vars.evt[e] = []; - - vars.evt[e].push([ f, d ]); - }); - }; - - dragit.evt.call = function(evt, a) { - - if (vars.dev) - console.log('[call]', evt, a); - - if (typeof vars.evt[evt] == 'undefined') { - if (vars.dev) - console.warn('No callback for event', evt, a); - return; - } - - vars.evt[evt].forEach(function(e) { - if (vars.dev) - console.log('[calling evt]', e); - if (typeof (e[0]) != 'undefined') - e[0](a); - }); - }; - - dragit.init = function(container) { - - dragit.time.offset = dragit.time.offset ? dragit.time.offset : 0; - vars.container = d3.select(container); - }; - - dragit.trajectory.display = function(d, i, c) { - - // Making sure we do not display twice the same trajectory - if (dragit.statemachine.current_state == 'drag' && dragit.statemachine.current_id == i) - return; - - if (vars.dev) - console.log('[display]', dragit.statemachine.current_state, dragit.statemachine.current_id, i); - - vars.gDragit = vars.container.insert('g', ':first-child') - .attr('class', 'gDragit'); - - if (typeof c != 'undefined' && c != 0) { - vars.gDragit.classed(c, true); - } else { - vars.gDragit.classed('focus', true); - } - - dragit.lineTrajectory = vars.gDragit.selectAll('.lineTrajectory') - .data([dragit.data[i]]) - .enter().append('path') - .attr('class', 'lineTrajectory') - .attr('d', vars.svgLine.interpolate(dragit.custom.line[vars.custom_trajectory].interpolate)); - - dragit.pointTrajectory = vars.gDragit.selectAll('.pointTrajectory') - .data(dragit.data[i]) - .enter().append(dragit.custom.point[vars.custom_focus].mark) - .attr('class', 'pointTrajectory') - .attr(dragit.custom.point[vars.custom_focus].attr); - - return dragit.trajectory.displayUpdate(d, i); - }; - - dragit.trajectory.displayUpdate = function(d, i) { - - dragit.lineTrajectory.data([dragit.data[i]]) - .transition() - .duration(0) - .attr('d', vars.svgLine); - - dragit.pointTrajectory.data(dragit.data[i]) - .transition() - .duration(0) - .attr(dragit.custom.point[vars.custom_focus].attr); - - return dragit.lineTrajectory; - }; - - dragit.trajectory.toggleAll = function(c) { - var c = c || ''; - var class_c = ''; - - if (c.length > 0) - class_c = '.' + c; - if (d3.selectAll('.gDragit' + class_c)[0].length > 0) - dragit.trajectory.removeAll(c); - else - dragit.trajectory.displayAll(c); - }; - - dragit.trajectory.displayAll = function(c) { - var c = c || ''; - - dragit.data.map(function(d, i) { - dragit.trajectory.display({}, i, c); - }); - }; - - dragit.trajectory.remove = function(d, i) { - if (dragit.statemachine.current_state != 'drag') - d3.selectAll('.gDragit.focus').remove(); - }; - - dragit.trajectory.removeAll = function(c) { - var c = c || 'focus'; - - d3.selectAll('.gDragit.' + c).remove(); - }; - - // Main function that binds drag callbacks to the current element - dragit.object.activate = function(d, i) { - - if (vars.dev) - console.log('[activate]', d, i); - - - d3.select(this)[0][0].node().addEventListener('mouseenter', function() { - if (dragit.statemachine.current_state == 'idle') { - dragit.statemachine.setState('mouseenter'); - } - }, false); - - d3.select(this)[0][0].node().addEventListener('mouseleave', function() { - if (dragit.statemachine.current_state == 'idle') - dragit.statemachine.setState('mouseleave'); - }, false); - - d.call(d3.behavior.drag() - .on('dragstart', function(d, i) { - - d3.event.sourceEvent.stopPropagation(); - dragit.statemachine.setState('dragstart'); - - if (vars.dev) - console.log('[dragstart]', d, i); - - dragit.trajectory.index_closest_trajectorypoint = -1; - dragit.trajectory.index_closest_datapoint = -1; - // Initial coordinates for the dragged object of interest - d.x = 0; - d.y = 0; - - switch (dragit.mouse.dragging) { - case 'free': - case 'horizontal': - } - - var mousepoint = [ d3.mouse(this)[0] + dragit.object.offsetX, d3.mouse(this)[1] + dragit.object.offsetY ]; - - // Create the line guide to closest trajectory - dragit.lineClosestTrajectory = vars.gDragit.append('line') - .attr('class', 'lineClosestTrajectory'); - - // Create the line guide to closest point - dragit.lineClosestPoint = vars.gDragit.append('line') - .attr('class', 'lineClosestPoint'); - - // Create the point interesting guide line and closest trajectory - dragit.pointClosestTrajectory = vars.gDragit.append(dragit.custom.point[vars.custom_focus].mark) - .attr(dragit.custom.point[vars.custom_focus].attr_static) - .attr('class', 'pointClosestTrajectory') - .attr('cx', mousepoint[0]) - .attr('cy', mousepoint[1]); - - // Create the focus that follows the mouse cursor - dragit.focusGuide = vars.gDragit.append(dragit.custom.point[vars.custom_focus].mark) - .attr(dragit.custom.point[vars.custom_focus].attr_static) - .attr('class', 'focusGuide') - .attr('cx', mousepoint[0]) - .attr('cy', mousepoint[1]); - - dragit.evt.call('dragstart'); - - }) - .on('drag', function(d, i) { - - d3.event.sourceEvent.stopPropagation(); - dragit.time.previous = dragit.time.current; - dragit.statemachine.setState('drag'); - - var mousepoint = [ d3.event.x + dragit.object.offsetX, d3.event.y + dragit.object.offsetY ]; - - if (vars.dev) - console.log('[drag]', d, i); - - switch (dragit.mouse.dragging) { - - case 'free': - - d3.select(this).attr('transform', function(d, i) { - return 'translate(' + [ mousepoint[0], mousepoint[1] ] + ')'; - }); - - dragit.evt.call('drag'); - - return; - - case 'horizontal': - - d.x += d3.event.dx; - d.y = dragit.utils.findYgivenX(d.x, dragit.lineTrajectory); - - d3.select(this).attr('transform', function(d, i) { - return 'translate(' + [ d.x, d.y ] + ')'; - }); - - return; - - } - - var list_distances_datapoint = [], - list_distances_trajectorypoint = [], - list_times = []; - var list_closest_trajectorypoint = [], - list_closest_datapoint = []; - - var new_id = -1; - - // Browse all the .lineTrajectory trajectories - // If scope is focus: only current trajectory is inspected - // If scope is selected: all trajectories are inspected - d3.selectAll('.' + dragit.mouse.scope).selectAll('.lineTrajectory').forEach(function(e, j) { - - var thisTrajectory = d3.select(e[0]); - - var current_index = null; - - if (dragit.mouse.scope == 'focus') { - current_index = i; - } else if (dragit.mouse.scope == 'selected') { - current_index = j; - } - - var closest_trajectorypoint = dragit.utils.closestPointToTrajectory(thisTrajectory.node(), mousepoint); - - var closest_datapoints = dragit.utils.closestDataPoint(mousepoint, dragit.data[current_index]); - - var index_closest_time = closest_datapoints.indexOf(Math.min.apply(Math, closest_datapoints));// + dragit.time.min; - - // Find the closest data point - var closest_datapoint = dragit.data[current_index][index_closest_time]; - - list_closest_trajectorypoint.push(closest_trajectorypoint.concat([current_index])); - list_closest_datapoint.push(closest_datapoint.concat([current_index])); - - // Store all the closest distances between the mouse and the current trajectory point - // Will be further used to find out the closest index (if scope is broader than 1 trajectory) - list_distances_datapoint.push(Math.sqrt((closest_datapoint[0] - mousepoint[0]) * (closest_datapoint[0] - mousepoint[0]) + (closest_datapoint[1] - mousepoint[1]) * (closest_datapoint[1] - mousepoint[1]))); - list_distances_trajectorypoint.push(Math.sqrt((closest_trajectorypoint[0] - mousepoint[0]) * (closest_trajectorypoint[0] - mousepoint[0]) + (closest_trajectorypoint[1] - mousepoint[1]) * (closest_trajectorypoint[1] - mousepoint[1]))); - - // Store the list of all closest times (one per trajectory) - list_times.push(index_closest_time); - - }); - - // Find the index of the closest trajectory by looking at the shortest distance - // index_min should be used to retrieve the dragit.data[min_index] data - var index_closest_datapoint = list_distances_datapoint.indexOf(d3.min(list_distances_datapoint)); - var index_closest_trajectorypoint = list_distances_trajectorypoint.indexOf(d3.min(list_distances_trajectorypoint)); - - // It can happens the trajectory is not fully displayed yet, then leave because no closest one - if (index_closest_trajectorypoint == -1) - return; - - var new_time = list_times[index_closest_datapoint]; - - // Update the line guide to closest trajectory - dragit.lineClosestTrajectory.attr('x1', list_closest_trajectorypoint[index_closest_trajectorypoint][0]) - .attr('y1', list_closest_trajectorypoint[index_closest_trajectorypoint][1]) - .attr('x2', mousepoint[0]) - .attr('y2', mousepoint[1]); - - // Update the point interesting guide line and closest trajectory - dragit.pointClosestTrajectory.attr('cx', list_closest_trajectorypoint[index_closest_trajectorypoint][0]) - .attr('cy', list_closest_trajectorypoint[index_closest_trajectorypoint][1]); - - // Update line guide to closest point - dragit.lineClosestPoint.attr('x1', list_closest_datapoint[index_closest_datapoint][0]) - .attr('y1', list_closest_datapoint[index_closest_datapoint][1]) - .attr('x2', mousepoint[0]) - .attr('y2', mousepoint[1]); - - // Update the focus that follows the mouse cursor - dragit.focusGuide.attr('cx', mousepoint[0]) - .attr('cy', mousepoint[1]); - - - if (dragit.object.dragging == 'relative') { - svg.style('cursor', 'none'); - // console.log("relative", list_closest_trajectorypoint[index_closest_trajectorypoint][0]) - - } - - // We have a new time point - if (dragit.time.current != new_time || dragit.trajectory.current_id != index_closest_trajectorypoint) { - dragit.trajectory.index_closest_trajectorypoint = index_closest_trajectorypoint; - dragit.time.current = new_time; - dragit.evt.call('update', new_time, 0); - } - - // We have a new trajectoy focus - if (dragit.statemachine.current_id != index_closest_trajectorypoint && dragit.mouse.scope != 'focus') { - dragit.statemachine.current_id = index_closest_trajectorypoint; - dragit.evt.call('new_focus', dragit.statemachine.current_id); - } - - dragit.evt.call('drag'); - - }) - .on('dragend', function(d, i) { - - d3.event.sourceEvent.stopPropagation(); - dragit.statemachine.setState('dragend'); - - if (vars.dev) - console.log('[dragend]', d, i); - - switch (dragit.mouse.dragging) { - - case 'free': - - d3.select(this).transition() - .duration(200) - .attr('transform', function(d, i) { - return 'translate(' + [ dragit.data[dragit.statemachine.current_id][dragit.time.current][0], dragit.data[dragit.statemachine.current_id][dragit.time.current][1] ] + ')'; - }); - break; - - case 'horizontal': - break; - } - - dragit.lineClosestTrajectory.remove(); - dragit.lineClosestPoint.remove(); - dragit.pointClosestTrajectory.remove(); - dragit.focusGuide.remove(); - - // Remove the current focus trajectory - d3.selectAll('.gDragit.focus').remove(); - - dragit.evt.call('dragend'); - - dragit.statemachine.setState('idle'); - }) - - ); - }; - - dragit.statemachine.setState = function(state) { - - if (vars.dev) - console.log('[setState]', state); - - dragit.statemachine.current_state = state; - dragit.evt.call('new_state'); - }; - - dragit.statemachine.getState = function(state) { - - return dragit.statemachine.current_state; - }; - - dragit.playback.play = function() { - - if (dragit.playback.playing) { - setTimeout(function() { - - if (!dragit.playback.playing) - return; - - dragit.time.current++; - - dragit.evt.call('update', 0, 0); - - if (dragit.time.current == dragit.time.max - 1) { - if (dragit.playback.loop) { - dragit.time.current = 0; - dragit.playback.play(); - } else { - dragit.playback.stop(); - } - } else - dragit.playback.play(); - - }, dragit.playback.speed); - } - - dragit.evt.call('play'); - }; - - dragit.playback.start = function() { - - if (!dragit.playback.playing) { - dragit.playback.playing = true; - d3.select(vars.playback.el).select('button').text('| |').attr('class', 'playing'); - - if (dragit.time.current == dragit.time.max) - dragit.time.current; - - dragit.playback.play(); - } - }; - - dragit.playback.stop = function() { - d3.select(vars.playback.el).select('button').text('▶').attr('class', 'stop'); - dragit.playback.playing = false; - }; - - // Create and add a DOM HTML slider for time navigation - dragit.utils.slider = function(el, play_button) { - vars.playback.el = el; - d3.select(el).append('p') - .style('clear', 'both'); - - if (play_button) { - d3.select(el).append('button') - .style({'height': '25px', 'width': '25px'}) - .text('▶') - .attr('class', 'stop') - .on('click', function() { - if (dragit.playback.playing == false) { - dragit.playback.start(); - } else { - dragit.playback.stop(); - } - }); - } - - d3.select(el).append('span') - .attr('id', 'min-time') - .text(dragit.time.min); - - d3.select(el).append('input') - .attr('type', 'range') - .attr('class', 'slider-time') - .property('min', dragit.time.min) - .property('max', dragit.time.max) - // .attr("step", 1) - .on('input', function() { - dragit.time.previous = dragit.time.current; - dragit.time.current = parseInt(this.value) - dragit.time.min; - dragit.evt.call('update', this.value, 0); - }); - - d3.select(el).append('span') - .attr('id', 'max-time') - .text(dragit.time.max); - - d3.select('.slider-time').property('value', dragit.time.current + dragit.time.min); - - dragit.evt.register('drag', function() { - d3.select('.slider-time').property('value', dragit.time.current); - }); - - }; - - dragit.utils.sliderUpdate = function(el) { - d3.select(el).select('#max-time') - .text(dragit.time.max); - - d3.select(el).select('.slider-time') - .property('max', dragit.time.max) - .property('value', dragit.time.current); - }; - - // Calculate the centroid of a given SVG element - dragit.utils.centroid = function(s) { - var e = selection.node(), - bbox = e.getBBox(); - - return [ bbox.x + bbox.width / 2, bbox.y + bbox.height / 2 ]; - }; - - // Credits: http://bl.ocks.org/mbostock/8027637 - dragit.utils.closestPointToTrajectory = function(pathNode, point) { - var pathLength = pathNode.getTotalLength(), - precision = 8, - best, - bestLength, - bestDistance = Infinity; - - // linear scan for coarse approximation - for (var scan, scanLength = 0, scanDistance; scanLength <= pathLength; scanLength += precision) { - if ((scanDistance = distance2(scan = pathNode.getPointAtLength(scanLength))) < bestDistance) { - best = scan, bestLength = scanLength, bestDistance = scanDistance; - } - } - - // binary search for precise estimate - precision /= 2; - while (precision > 0.5) { - var before, - after, - beforeLength, - afterLength, - beforeDistance, - afterDistance; - - if ((beforeLength = bestLength - precision) >= 0 && (beforeDistance = distance2(before = pathNode.getPointAtLength(beforeLength))) < bestDistance) { - best = before, bestLength = beforeLength, bestDistance = beforeDistance; - } else if ((afterLength = bestLength + precision) <= pathLength && (afterDistance = distance2(after = pathNode.getPointAtLength(afterLength))) < bestDistance) { - best = after, bestLength = afterLength, bestDistance = afterDistance; - } else { - precision /= 2; - } - } - - best = [ best.x, best.y ]; - best.distance = Math.sqrt(bestDistance); - return best; - - function distance2(p) { - var dx = p.x - point[0], - dy = p.y - point[1]; - - return dx * dx + dy * dy; - } - }; - - dragit.utils.closestDataPoint = function(p, points) { - var distances = points.map(function(d, i) { - var dx = d[0] - p[0]; - var dy = d[1] - p[1]; - - return Math.sqrt(dx * dx + dy * dy); - }); - - return distances; - }; - - // Code from http://bl.ocks.org/duopixel/3824661 - dragit.utils.findYgivenX = function(x, path) { - var pathEl = path.node(); - var pathLength = pathEl.getTotalLength(); - var BBox = pathEl.getBBox(); - var scale = pathLength / BBox.width; - var offsetLeft = document.getElementsByClassName('lineTrajectory')[0].offsetLeft; - - x = x - offsetLeft; - - var beginning = x, - end = pathLength, - target; - - while (true) { - target = Math.floor((beginning + end) / 2); - pos = pathEl.getPointAtLength(target); - if ((target === end || target === beginning) && pos.x !== x) { - break; - } - if (pos.x > x) - end = target; - else if (pos.x < x) - beginning = target; - else - break; - } - return pos.y - 200; - }; - - dragit.utils.animateTrajectory = function(path, start_time, duration) { - - var totalLength = path.node().getTotalLength(); - - path.attr('stroke-width', '5') - .attr('stroke-dasharray', totalLength + ' ' + totalLength) - .attr('stroke-dashoffset', totalLength) - .transition() - .duration(duration) - .ease('linear') - .attr('stroke-dashoffset', 0); - }; - - // Credits: http://bl.ocks.org/mbostock/1705868 - dragit.utils.translateAlong = function(path, duration) { - var l = path.node().getTotalLength(); - - return function(d, i, a) { - return function(t) { - var p = path.node().getPointAtLength(t * l); - - return 'translate(' + p.x + ',' + p.y + ')'; - }; - }; - }; - - dragit.utils.getSubPath = function(start_time, end_time) { - - sub_data = dragit.data[dragit.statemachine.current_id].filter(function(d, i) { - return i >= start_time && i <= end_time; - }); - - dragit.subTrajectory = vars.gDragit.selectAll('.subTrajectory') - .data([sub_data]) - .enter().append('path') - .attr('class', 'subTrajectory') - .style({'stroke': 'black', 'stroke-width': 4}) - .attr('d', vars.svgLine.interpolate(dragit.custom.line[vars.custom_trajectory].interpolate)); - - return dragit.subTrajectory; - - }; - -})(); - -Array.prototype.equals = function(b) { - var a = this; - var i = a.length; - - if (i != b.length) - return false; - while (i--) { - if (a[i] !== b[i]) - return false; - } - return true; -}; \ No newline at end of file diff --git a/app/assets/v2/js/dataviz/heatmap.css b/app/assets/v2/js/dataviz/heatmap.css deleted file mode 100644 index 93444f72158..00000000000 --- a/app/assets/v2/js/dataviz/heatmap.css +++ /dev/null @@ -1,13 +0,0 @@ -body{font-size: 14px;} -.days-hours-heatmap{padding: 20px 0 20px 0;width: 500px;margin: 0 auto;} -.days-hours-heatmap .calibration{margin-bottom: 15px;width:400px;} -.days-hours-heatmap .calibration .group{display: inline-block;} -.days-hours-heatmap .calibration .description{width:108px;} -.days-hours-heatmap .calibration .description>label:last-child{float:right;} -.days-hours-heatmap .calibration>.display-control{float:right;} -.days-hours-heatmap .calibration>.display-control label{vertical-align: top;} -.days-hours-heatmap .calibration>.display-control input[type='radio']{cursor: pointer;} - -.days-hours-heatmap .heatmap .axis path{display: none;} -.days-hours-heatmap .heatmap .axis line{fill: none;stroke: #000;shape-rendering: crispEdges;} -.days-hours-heatmap .heatmap .axis text{font-size: 12px} diff --git a/app/assets/v2/js/dataviz/heatmap.js b/app/assets/v2/js/dataviz/heatmap.js deleted file mode 100644 index 6dea67de3be..00000000000 --- a/app/assets/v2/js/dataviz/heatmap.js +++ /dev/null @@ -1,161 +0,0 @@ -(function() { - // UI configuration - var itemSize = 18; - var cellSize = itemSize - 1; - var width = 800; - var height = 800; - var margin = {top: 20, right: 20, bottom: 20, left: 25}; - - // formats - var hourFormat = d3.time.format('%H'); - var dayFormat = d3.time.format('%j'); - var timeFormat = d3.time.format('%Y-%m-%dT%X'); - var monthDayFormat = d3.time.format('%m.%d'); - - // data vars for rendering - var dateExtent = null; - var data = null; - var dayOffset = 0; - var colorCalibration = [ '#f6faaa', '#FEE08B', '#FDAE61', '#F46D43', '#D53E4F', '#9E0142' ]; - var dailyValueExtent = {}; - - // axises and scales - var axisWidth = 0; - var axisHeight = itemSize * 24; - var xAxisScale = d3.time.scale(); - var xAxis = d3.svg.axis() - .orient('top') - .ticks(d3.time.days, 3) - .tickFormat(monthDayFormat); - var yAxisScale = d3.scale.linear() - .range([ 0, axisHeight ]) - .domain([ 0, 24 ]); - var yAxis = d3.svg.axis() - .orient('left') - .ticks(5) - .tickFormat(d3.format('02d')) - .scale(yAxisScale); - - initCalibration(); - - var svg = d3.select('[role="heatmap"]'); - var heatmap = svg - .attr('width', width) - .attr('height', height) - .append('g') - .attr('width', width - margin.left - margin.right) - .attr('height', height - margin.top - margin.bottom) - .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - var rect = null; - - var url = document.location.href + '?data=1&format=json'; - - d3.json(url, function(err, data) { - data = data.data; - data.forEach(function(valueObj) { - valueObj['date'] = timeFormat.parse(valueObj['timestamp']); - var day = valueObj['day'] = monthDayFormat(valueObj['date']); - - var dayData = dailyValueExtent[day] = (dailyValueExtent[day] || [ 1000, -1 ]); - var pmValue = valueObj['value']; - - console.log(day, pmValue); - - dayData[0] = d3.min([ dayData[0], pmValue ]); - dayData[1] = d3.max([ dayData[1], pmValue ]); - }); - - dateExtent = d3.extent(data, function(d) { - return d.date; - }); - - axisWidth = itemSize * (dayFormat(dateExtent[1]) - dayFormat(dateExtent[0]) + 1); - - // render axises - xAxis.scale(xAxisScale.range([ 0, axisWidth ]).domain([ dateExtent[0], dateExtent[1] ])); - svg.append('g') - .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') - .attr('class', 'x axis') - .call(xAxis) - .append('text') - .text('date') - .attr('transform', 'translate(' + axisWidth + ',-10)'); - - svg.append('g') - .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') - .attr('class', 'y axis') - .call(yAxis) - .append('text') - .text('time') - .attr('transform', 'translate(-10,' + axisHeight + ') rotate(-90)'); - - // render heatmap rects - dayOffset = dayFormat(dateExtent[0]); - rect = heatmap.selectAll('rect') - .data(data) - .enter().append('rect') - .attr('width', cellSize) - .attr('height', cellSize) - .attr('x', function(d) { - return itemSize * (dayFormat(d.date) - dayOffset); - }) - .attr('y', function(d) { - return hourFormat(d.date) * itemSize; - }) - .attr('fill', '#ffffff'); - - rect.filter(function(d) { - return d.value > 0; - }) - .append('title') - .text(function(d) { - return monthDayFormat(d.date) + ' ' + d.value; - }); - - renderColor(); - }); - - function initCalibration() { - d3.select('[role="calibration"] [role="example"]').select('svg') - .selectAll('rect').data(colorCalibration).enter() - .append('rect') - .attr('width', cellSize) - .attr('height', cellSize) - .attr('x', function(d, i) { - return i * itemSize; - }) - .attr('fill', function(d) { - return d; - }); - - // bind click event - d3.selectAll('[role="calibration"] [name="displayType"]').on('click', function() { - renderColor(); - }); - } - - function renderColor() { - var renderByCount = document.getElementsByName('displayType')[0].checked; - - rect - .filter(function(d) { - return (d.value >= 0); - }) - .transition() - .delay(function(d) { - return (dayFormat(d.date) - dayOffset) * 15; - }) - .duration(500) - .attrTween('fill', function(d, i, a) { - // choose color dynamicly - var colorIndex = d3.scale.quantize() - .range([ 0, 1, 2, 3, 4, 5 ]) - .domain((renderByCount ? [ 0, 500 ] : dailyValueExtent[d.day])); - - return d3.interpolate(a, colorCalibration[colorIndex(d.value)]); - }); - } - - // extend frame height in `http://bl.ocks.org/` - d3.select(self.frameElement).style('height', '600px'); -})(); diff --git a/app/assets/v2/js/dataviz/sequences.css b/app/assets/v2/js/dataviz/sequences.css deleted file mode 100644 index fb6db0d5ead..00000000000 --- a/app/assets/v2/js/dataviz/sequences.css +++ /dev/null @@ -1,54 +0,0 @@ -body { - font-size: 12px; - font-weight: 400; - background-color: #fff; - width: 960px; - height: 700px; - margin-top: 10px; -} - -#main { - float: left; - width: 750px; -} - -#sidebar { - float: right; - width: 100px; -} - -#sequence { - width: 600px; - height: 70px; -} - -#legend { - padding: 10px 0 0 3px; -} - -#sequence text, #legend text { - font-weight: 600; - fill: #fff; -} - -#chart { - position: relative; -} - -#chart path { - stroke: #fff; -} - -#explanation { - position: absolute; - top: 260px; - left: 305px; - width: 140px; - text-align: center; - color: #666; - z-index: -1; -} - -#percentage { - font-size: 2.5em; -} diff --git a/app/assets/v2/js/dataviz/sequences.js b/app/assets/v2/js/dataviz/sequences.js deleted file mode 100644 index ac0c29801ae..00000000000 --- a/app/assets/v2/js/dataviz/sequences.js +++ /dev/null @@ -1,332 +0,0 @@ -// Dimensions of sunburst. -var width = 750; -var height = 600; -var radius = Math.min(width, height) / 2; - -// Breadcrumb dimensions: width, height, spacing, width of tip/tail. -var b = { - w: 75, h: 30, s: 3, t: 10 -}; - - -// Total size of all segments; we set this later, after loading the data. -var totalSize = 0; - -var vis = d3.select('#chart').append('svg:svg') - .attr('width', width) - .attr('height', height) - .append('svg:g') - .attr('id', 'container') - .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')'); - -var partition = d3.layout.partition() - .size([ 2 * Math.PI, radius * radius ]) - .value(function(d) { - return d.size; - }); - -var arc = d3.svg.arc() - .startAngle(function(d) { - return d.x; - }) - .endAngle(function(d) { - return d.x + d.dx; - }) - .innerRadius(function(d) { - return Math.sqrt(d.y); - }) - .outerRadius(function(d) { - return Math.sqrt(d.y + d.dy); - }); - -// Use d3.text and d3.csv.parseRows so that we do not need to have a header -// row, and can receive the csv as an array of arrays. -d3.text(document.location.href + '?data=1', function(text) { - var csv = d3.csv.parseRows(text); - var json = buildHierarchy(csv); - - createVisualization(json); -}); - -// Main function to draw and set up the visualization, once we have the data. -function createVisualization(json) { - - // Basic setup of page elements. - initializeBreadcrumbTrail(); - drawLegend(); - d3.select('#togglelegend').on('click', toggleLegend); - - // Bounding circle underneath the sunburst, to make it easier to detect - // when the mouse leaves the parent g. - vis.append('svg:circle') - .attr('r', radius) - .style('opacity', 0); - - // For efficiency, filter nodes to keep only those large enough to see. - var nodes = partition.nodes(json) - .filter(function(d) { - return (d.dx > 0.005); // 0.005 radians = 0.29 degrees - }); - - var path = vis.data([json]).selectAll('path') - .data(nodes) - .enter().append('svg:path') - .attr('display', function(d) { - return d.depth ? null : 'none'; - }) - .attr('d', arc) - .attr('fill-rule', 'evenodd') - .style('fill', function(d) { - return colors[d.name]; - }) - .style('opacity', 1) - .on('mouseover', mouseover); - - // Add the mouseleave handler to the bounding circle. - d3.select('#container').on('mouseleave', mouseleave); - - // Get total size of the tree = value of root node from partition. - totalSize = path.node().__data__.value; -} - -// Fade all but the current sequence, and show it in the breadcrumb trail. -function mouseover(d) { - - var percentage = (100 * d.value / totalSize).toPrecision(3); - var percentageString = percentage + '%'; - - if (percentage < 0.1) { - percentageString = '< 0.1%'; - } - - d3.select('#percentage') - .text(percentageString); - - d3.select('#explanation') - .style('visibility', ''); - - var sequenceArray = getAncestors(d); - - updateBreadcrumbs(sequenceArray, percentageString); - - // Fade all the segments. - d3.selectAll('path') - .style('opacity', 0.3); - - // Then highlight only those that are an ancestor of the current segment. - vis.selectAll('path') - .filter(function(node) { - return (sequenceArray.indexOf(node) >= 0); - }) - .style('opacity', 1); -} - -// Restore everything to full opacity when moving off the visualization. -function mouseleave(d) { - - // Hide the breadcrumb trail - d3.select('#trail') - .style('visibility', 'hidden'); - - // Deactivate all segments during transition. - d3.selectAll('path').on('mouseover', null); - - // Transition each segment to full opacity and then reactivate it. - d3.selectAll('path') - .transition() - .duration(1000) - .style('opacity', 1) - .each('end', function() { - d3.select(this).on('mouseover', mouseover); - }); - - d3.select('#explanation') - .style('visibility', 'hidden'); -} - -// Given a node in a partition layout, return an array of all of its ancestor -// nodes, highest first, but excluding the root. -function getAncestors(node) { - var path = []; - var current = node; - - while (current.parent) { - path.unshift(current); - current = current.parent; - } - return path; -} - -function initializeBreadcrumbTrail() { - // Add the svg area. - var trail = d3.select('#sequence').append('svg:svg') - .attr('width', width) - .attr('height', 50) - .attr('id', 'trail'); - // Add the label at the end, for the percentage. - - trail.append('svg:text') - .attr('id', 'endlabel') - .style('fill', '#000'); -} - -// Generate a string that describes the points of a breadcrumb polygon. -function breadcrumbPoints(d, i) { - var points = []; - - points.push('0,0'); - points.push(b.w + ',0'); - points.push(b.w + b.t + ',' + (b.h / 2)); - points.push(b.w + ',' + b.h); - points.push('0,' + b.h); - if (i > 0) { // Leftmost breadcrumb; don't include 6th vertex. - points.push(b.t + ',' + (b.h / 2)); - } - return points.join(' '); -} - -// Update the breadcrumb trail to show the current sequence and percentage. -function updateBreadcrumbs(nodeArray, percentageString) { - - // Data join; key function combines name and depth (= position in sequence). - var g = d3.select('#trail') - .selectAll('g') - .data(nodeArray, function(d) { - return d.name + d.depth; - }); - - // Add breadcrumb and label for entering nodes. - var entering = g.enter().append('svg:g'); - - entering.append('svg:polygon') - .attr('points', breadcrumbPoints) - .style('fill', function(d) { - return colors[d.name]; - }); - - entering.append('svg:text') - .attr('x', (b.w + b.t) / 2) - .attr('y', b.h / 2) - .attr('dy', '0.35em') - .attr('text-anchor', 'middle') - .text(function(d) { - return d.name; - }); - - // Set position for entering and updating nodes. - g.attr('transform', function(d, i) { - return 'translate(' + i * (b.w + b.s) + ', 0)'; - }); - - // Remove exiting nodes. - g.exit().remove(); - - // Now move and update the percentage at the end. - d3.select('#trail').select('#endlabel') - .attr('x', (nodeArray.length + 0.5) * (b.w + b.s)) - .attr('y', b.h / 2) - .attr('dy', '0.35em') - .attr('text-anchor', 'middle') - .text(percentageString); - - // Make the breadcrumb trail visible, if it's hidden. - d3.select('#trail') - .style('visibility', ''); - -} - -function drawLegend() { - - // Dimensions of legend item: width, height, spacing, radius of rounded rect. - var li = { - w: 75, h: 30, s: 3, r: 3 - }; - - var legend = d3.select('#legend').append('svg:svg') - .attr('width', li.w) - .attr('height', d3.keys(colors).length * (li.h + li.s)); - - var g = legend.selectAll('g') - .data(d3.entries(colors)) - .enter().append('svg:g') - .attr('transform', function(d, i) { - return 'translate(0,' + i * (li.h + li.s) + ')'; - }); - - g.append('svg:rect') - .attr('rx', li.r) - .attr('ry', li.r) - .attr('width', li.w) - .attr('height', li.h) - .style('fill', function(d) { - return d.value; - }); - - g.append('svg:text') - .attr('x', li.w / 2) - .attr('y', li.h / 2) - .attr('dy', '0.35em') - .attr('text-anchor', 'middle') - .text(function(d) { - return d.key; - }); -} - -function toggleLegend() { - var legend = d3.select('#legend'); - - if (legend.style('visibility') == 'hidden') { - legend.style('visibility', ''); - } else { - legend.style('visibility', 'hidden'); - } -} - -// Take a 2-column CSV and transform it into a hierarchical structure suitable -// for a partition layout. The first column is a sequence of step names, from -// root to leaf, separated by hyphens. The second column is a count of how -// often that sequence occurred. -function buildHierarchy(csv) { - var root = {'name': 'root', 'children': []}; - - for (var i = 0; i < csv.length; i++) { - var sequence = csv[i][0]; - var size = +csv[i][1]; - - if (isNaN(size)) { // e.g. if this is a header row - continue; - } - var parts = sequence.split('-'); - var currentNode = root; - - for (var j = 0; j < parts.length; j++) { - var children = currentNode['children']; - var nodeName = parts[j]; - var childNode; - - if (j + 1 < parts.length) { - // Not yet at the end of the sequence; move down the tree. - var foundChild = false; - - for (var k = 0; k < children.length; k++) { - if (children[k]['name'] == nodeName) { - childNode = children[k]; - foundChild = true; - break; - } - } - // If we don't already have a child node for this branch, create it. - if (!foundChild) { - childNode = {'name': nodeName, 'children': []}; - children.push(childNode); - } - currentNode = childNode; - } else { - // Reached the end of the sequence; create a leaf node. - childNode = {'name': nodeName, 'size': size}; - children.push(childNode); - } - } - } - return root; -} diff --git a/app/assets/v2/js/dataviz/spiral.js b/app/assets/v2/js/dataviz/spiral.js deleted file mode 100644 index 23096d28b93..00000000000 --- a/app/assets/v2/js/dataviz/spiral.js +++ /dev/null @@ -1,164 +0,0 @@ -var width = 800; -var height = 800; -var start = 0; -var end = 2.25; -var numSpirals = 3; - -margin = {top: 50, bottom: 50, left: 50, right: 50}; - -var theta = function(r) { - return numSpirals * Math.PI * r; -}; - - // used to assign nodes color by group -var color = d3.scaleOrdinal(d3.schemeCategory10); - -var r = d3.min([ width, height ]) / 2 - 40; - -var radius = d3.scaleLinear() - .domain([ start, end ]) - .range([ 40, r ]); - -var svg = d3.select('#chart').append('svg') - .attr('width', width + margin.right + margin.left) - .attr('height', height + margin.left + margin.right) - .append('g') - .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')'); - -var points = d3.range(start, end + 0.001, (end - start) / 1000); - -var spiral = d3.radialLine() - .curve(d3.curveCardinal) - .angle(theta) - .radius(radius); - -var path = svg.append('path') - .datum(points) - .attr('id', 'spiral') - .attr('d', spiral) - .style('fill', 'none') - .style('stroke', 'steelblue'); - -var spiralLength = path.node().getTotalLength(); -var N = 365; -var barWidth = (spiralLength / N) - 1; - -var timeScale = d3.scaleTime() - .domain(d3.extent(someData, function(d) { - return d.date; - })) - .range([ 0, spiralLength ]); - - // yScale for the bar height -var yScale = d3.scaleLinear() - .domain([ 0, d3.max(someData, function(d) { - return d.value; - }) ]) - .range([ 0, (r / numSpirals) - 30 ]); - -svg.selectAll('rect') - .data(someData) - .enter() - .append('rect') - .attr('x', function(d, i) { - - var linePer = timeScale(d.date); - var posOnLine = path.node().getPointAtLength(linePer); - var angleOnLine = path.node().getPointAtLength(linePer - barWidth); - - d.linePer = linePer; // % distance are on the spiral - d.x = posOnLine.x; // x postion on the spiral - d.y = posOnLine.y; // y position on the spiral - - d.a = (Math.atan2(angleOnLine.y, angleOnLine.x) * 180 / Math.PI) - 90; // angle at the spiral position - - return d.x; - }) - .attr('y', function(d) { - return d.y; - }) - .attr('width', function(d) { - return barWidth; - }) - .attr('height', function(d) { - return yScale(d.value); - }) - .style('fill', function(d) { - return color(d.group); - }) - .style('stroke', 'none') - .attr('transform', function(d) { - return 'rotate(' + d.a + ',' + d.x + ',' + d.y + ')'; // rotate the bar - }); - -// add date labels -var tF = d3.timeFormat('%b %Y'); -var firstInMonth = {}; - -svg.selectAll('text') - .data(someData) - .enter() - .append('text') - .attr('dy', 10) - .style('text-anchor', 'start') - .style('font', '10px arial') - .append('textPath') -// only add for the first of each month - .filter(function(d) { - var sd = tF(d.date); - - if (!firstInMonth[sd]) { - firstInMonth[sd] = 1; - return true; - } - return false; - }) - .text(function(d) { - return tF(d.date); - }) -// place text along spiral - .attr('xlink:href', '#spiral') - .style('fill', 'grey') - .attr('startOffset', function(d) { - return ((d.linePer / spiralLength) * 100) + '%'; - }); - - -var tooltip = d3.select('#chart') - .append('div') - .attr('class', 'tooltip'); - -tooltip.append('div') - .attr('class', 'date'); -tooltip.append('div') - .attr('class', 'value'); - -svg.selectAll('rect') - .on('mouseover', function(d) { - - tooltip.select('.date').html('Date: ' + d.date.toDateString() + ''); - tooltip.select('.value').html('Value: ' + Math.round(d.value * 100) / 100 + ''); - - d3.select(this) - .style('fill', '#FFFFFF') - .style('stroke', '#000000') - .style('stroke-width', '2px'); - - tooltip.style('display', 'block'); - tooltip.style('opacity', 2); - - }) - .on('mousemove', function(d) { - tooltip.style('top', (d3.event.layerY + 10) + 'px') - .style('left', (d3.event.layerX - 25) + 'px'); - }) - .on('mouseout', function(d) { - d3.selectAll('rect') - .style('fill', function(d) { - return color(d.group); - }) - .style('stroke', 'none'); - - tooltip.style('display', 'none'); - tooltip.style('opacity', 0); - }); diff --git a/app/assets/v2/scss/dataviz/sequences.scss b/app/assets/v2/scss/dataviz/sequences.scss deleted file mode 100644 index fb6db0d5ead..00000000000 --- a/app/assets/v2/scss/dataviz/sequences.scss +++ /dev/null @@ -1,54 +0,0 @@ -body { - font-size: 12px; - font-weight: 400; - background-color: #fff; - width: 960px; - height: 700px; - margin-top: 10px; -} - -#main { - float: left; - width: 750px; -} - -#sidebar { - float: right; - width: 100px; -} - -#sequence { - width: 600px; - height: 70px; -} - -#legend { - padding: 10px 0 0 3px; -} - -#sequence text, #legend text { - font-weight: 600; - fill: #fff; -} - -#chart { - position: relative; -} - -#chart path { - stroke: #fff; -} - -#explanation { - position: absolute; - top: 260px; - left: 305px; - width: 140px; - text-align: center; - color: #666; - z-index: -1; -} - -#percentage { - font-size: 2.5em; -} diff --git a/app/dataviz/d3_views.py b/app/dataviz/d3_views.py index 0d73783bd0a..af15e7d23d9 100644 --- a/app/dataviz/d3_views.py +++ b/app/dataviz/d3_views.py @@ -111,197 +111,6 @@ def data_viz_helper_get_data_responses(request, visual_type): return data_dict - -@staff_member_required -def viz_spiral(request, key='email_open'): - """Render a spiral graph visualization. - - Args: - key (str): The key type to visualize. - - Returns: - TemplateResponse: The populated spiral data visualization template. - - """ - stats = Stat.objects.filter(created_on__hour=1) - type_options = stats.distinct('key').values_list('key', flat=True) - stats = stats.filter(key=key).order_by('created_on') - params = {'stats': stats, 'key': key, 'page_route': 'spiral', 'type_options': type_options, 'viz_type': key, } - return TemplateResponse(request, 'dataviz/spiral.html', params) - - -@staff_member_required -def viz_chord(request, key='bounties_paid'): - """Render a chord graph visualization. - - Args: - key (str): The key type to visualize. - - Returns: - TemplateResponse: The populated chord data visualization template. - - """ - type_options = ['bounties_paid'] - - if request.GET.get('data'): - rows = [['creditor', 'debtor', 'amount', 'risk']] - network = 'mainnet' - for bounty in Bounty.objects.current().filter(network=network, web3_type='bounties_network', idx_status='done'): - weight = bounty.value_in_usdt_then - if weight: - for fulfillment in bounty.fulfillments.filter(accepted=True): - length = (fulfillment.created_on - bounty.web3_created).seconds - target = fulfillment.fulfiller_github_username.lower() - source = bounty.bounty_owner_github_username.lower() - if source and target: - rows.append((helper_hide_pii(source), helper_hide_pii(target), str(weight), str(length))) - - output_rows = [] - for row in rows: - row = ",".join(row) - output_rows.append(row) - - output = "\n".join(output_rows) - return HttpResponse(output) - - params = {'key': key, 'page_route': 'spiral', 'type_options': type_options, 'viz_type': key, } - return TemplateResponse(request, 'dataviz/chord.html', params) - - -@staff_member_required -def viz_steamgraph(request, key='open'): - """Render a steamgraph graph visualization. - - Args: - key (str): The key type to visualize. - - Returns: - TemplateResponse: The populated steamgraph data visualization template. - - """ - type_options = Bounty.objects.all().distinct('idx_status').values_list('idx_status', flat=True) - if key not in type_options: - key = type_options[0] - - if request.GET.get('data'): - rows = [['key', 'value', 'date']] - network = 'mainnet' - bounties = Bounty.objects.filter(network=network, web3_type='bounties_network', idx_status=key) - org_names = set([bounty.org_name for bounty in bounties]) - #start_date = bounties.order_by('web3_created').first().web3_created - start_date = timezone.now() - timezone.timedelta(days=30) - end_date = timezone.now() - current_date = start_date - while current_date < end_date: - next_date = current_date + timezone.timedelta(days=1) - for org_name in org_names: - if org_name: - _bounties = bounties.filter(github_url__contains=org_name) - weight = round( - sum( - bounty.value_in_usdt_then for bounty in _bounties - if bounty.value_in_usdt_then and bounty.was_active_at(current_date) - ), 2 - ) - output_date = current_date.strftime(('%m/%d/%y')) - rows.append([org_name, str(weight), output_date]) - current_date = next_date - - output_rows = [] - for row in rows: - row = ",".join(row) - output_rows.append(row) - - output = "\n".join(output_rows) - return HttpResponse(output) - - params = {'key': key, 'page_route': 'steamgraph', 'type_options': type_options, 'viz_type': key, } - return TemplateResponse(request, 'dataviz/steamgraph.html', params) - - -@staff_member_required -def viz_calendar(request, key='email_open', template='calendar'): - return viz_heatmap(request, key, template) - - -@staff_member_required -def viz_heatmap(request, key='email_open', template='heatmap'): - """Render a heatmap graph visualization. - - Args: - key (str): The key type to visualize. - - Returns: - JsonResponse: If data param provided, return a JSON representation of data to be graphed. - TemplateResponse: If data param not provided, return the populated data visualization template. - - """ - time_now = timezone.now() - stats = Stat.objects.filter(created_on__lt=time_now, ) - if template == 'calendar': - stats = stats.filter(created_on__hour=1) - else: - stats = stats.filter(created_on__gt=(time_now - timezone.timedelta(weeks=2))) - - type_options = stats.distinct('key').values_list('key', flat=True) - stats = stats.filter(key=key).order_by('-created_on') - - if request.GET and request.GET.get('data'): - if request.GET.get('format', '') == 'json': - _max = max([stat.val_since_hour for stat in stats]) - output = { - # {"timestamp": "2014-10-16T22:00:00", "value": {"PM2.5": 61.92}} - "data": [{ - 'timestamp': stat.created_on.strftime("%Y-%m-%dT%H:00:00"), - 'value': stat.val_since_hour * 800.0 / _max, - } for stat in stats] - } - # Example output: https://gist.github.com/mbeacom/44f0114666d69bb5bf2756216c43b64d - return JsonResponse(output) - #csv - rows = [['Date', 'Value']] - _max = max([stat.val_since_yesterday for stat in stats]) - for stat in stats: - date = stat.created_on.strftime("%Y-%m-%d") - value = str(stat.val_since_yesterday / _max) if _max else str(0) - rows.append([date, value]) - output_rows = [] - for row in rows: - row = ",".join(row) - output_rows.append(row) - - output = "\n".join(output_rows) - return HttpResponse(output) - params = {'stats': stats, 'key': key, 'page_route': template, 'type_options': type_options, 'viz_type': key, } - return TemplateResponse(request, f'dataviz/{template}.html', params) - - -@staff_member_required -def viz_index(request): - """Render the visualization index. - - Returns: - TemplateResponse: The visualization index template response. - - """ - return TemplateResponse(request, 'dataviz/index.html', {}) - - -@staff_member_required -def viz_circles(request, visual_type): - """Render a circle graph visualization. - - Args: - visual_type (str): The visualization type. - - Returns: - JsonResponse: If data param provided, return a JSON representation of data to be graphed. - TemplateResponse: If data param not provided, return the populated data visualization template. - - """ - return viz_sunburst(request, visual_type, 'circles') - - def data_viz_helper_merge_json_trees(output): """Handle merging the visualization data trees. @@ -362,92 +171,6 @@ def data_viz_helper_get_json_output(key, value, depth=0): return result -@staff_member_required -def viz_sunburst(request, visual_type, template='sunburst'): - """Render a sunburst graph visualization. - - Args: - visual_type (str): The visualization type. - template (str): The template type to be used. Defaults to: sunburst. - - TODO: - * Reduce the number of local variables in this method from 18 to 15. - - Returns: - JsonResponse: If data param provided, return a JSON representation of data to be graphed. - TemplateResponse: If data param not provided, return the populated data visualization template. - - """ - visual_type_options = ['status_progression', 'repos', 'fulfillers', 'funders', ] - if visual_type not in visual_type_options: - visual_type = visual_type_options[0] - - if visual_type == 'status_progression': - title = "Status Progression Viz" - comment = 'of statuses begin with this sequence of status' - categories = list(Bounty.objects.distinct('idx_status').values_list('idx_status', flat=True)) + ['_'] - elif visual_type == 'repos': - title = "Github Structure of All Bounties" - comment = 'of bounties value with this github structure' - categories = [ - bounty.org_name.replace('-', '') for bounty in Bounty.objects.filter(network='mainnet') if bounty.org_name - ] - categories += [ - bounty.github_repo_name.replace('-', '') for bounty in Bounty.objects.filter(network='mainnet') - if bounty.github_repo_name - ] - categories += [str(bounty.github_issue_number) for bounty in Bounty.objects.filter(network='mainnet')] - elif visual_type == 'fulfillers': - title = "Fulfillers" - comment = 'of bounties value with this fulfiller' - categories = [] - for bounty in Bounty.objects.filter(network='mainnet'): - for fulfiller in bounty.fulfillments.all(): - categories.append(fulfiller.fulfiller_github_username.replace('-', '')) - elif visual_type == 'funders': - title = "Funders" - comment = 'of bounties value with this funder' - categories = [] - for bounty in Bounty.objects.filter(network='mainnet'): - categories.append(bounty.bounty_owner_github_username.replace('-', '')) - - if request.GET.get('data'): - data_dict = data_viz_helper_get_data_responses(request, visual_type) - - _format = request.GET.get('format', 'csv') - if _format == 'csv': - rows = [] - for key, value in data_dict.items(): - row = ",".join([key, str(value)]) - rows.append(row) - - output = "\n".join(rows) - return HttpResponse(output) - - if _format == 'json': - output = {'name': 'data', 'children': []} - for key, val in data_dict.items(): - if val: - output['children'].append(data_viz_helper_get_json_output(key, val)) - output = data_viz_helper_merge_json_trees(output) - return JsonResponse(output) - - params = { - 'title': title, - 'comment': comment, - 'viz_type': visual_type, - 'page_route': template, - 'type_options': visual_type_options, - 'categories': json.dumps(list(categories)), - } - return TemplateResponse(request, f'dataviz/{template}.html', params) - - -@staff_member_required -def viz_sankey(request, _type, template='square_graph'): - return viz_graph(request, _type, template) - - def helper_hide_pii(username, num_chars=3): if not username: return None @@ -516,74 +239,10 @@ def viz_graph(request, _type, template='graph'): return response -@staff_member_required -def viz_draggable(request, key='email_open'): - """Render a draggable graph visualization. - - Args: - key (str): The key type to visualize. - - Returns: - TemplateResponse: The populated draggable data visualization template. - - """ - stats = [] - type_options = [] - bfs = BountyFulfillment.objects.filter(accepted=True) - limit = 50 - usernames = list( - bfs.exclude(profile__handle='').distinct('profile__handle').values_list('profile__handle', flat=True) - )[0:limit] - if request.GET.get('data'): - output = [] - for username in usernames: - these_bounties = bfs.filter(profile__handle=username) - start_date = timezone.now() - timezone.timedelta(days=180) - income = [] - lifeExpectancy = [] - population = [] - val_usdt = 0 - for i in range(1, 180): - current_date = start_date + timezone.timedelta(days=i) - prev_date = start_date + timezone.timedelta(days=(i - 1)) - these_bounties_before_date = these_bounties.filter(created_on__lt=current_date) - these_bounties_in_range = these_bounties.filter(created_on__lt=current_date, created_on__gt=prev_date) - val_usdt += sum(bf.bounty.value_in_usdt for bf in these_bounties_in_range if bf.bounty.value_in_usdt) - num_bounties = these_bounties_before_date.distinct('bounty').count() - income.append([i, val_usdt]) # x axis - lifeExpectancy.append([i, num_bounties]) # y axis - population.append([i, 10000000 * num_bounties]) # size - #print(username, i, num_bounties, val_usdt) - output.append({ - 'name': username, - 'region': username, - 'income': income, - 'population': population, - 'lifeExpectancy': lifeExpectancy, - }) - - return JsonResponse(output, safe=False) - - params = { - 'stats': stats, - 'key': key, - 'usernames': json.dumps(usernames), - 'page_route': 'draggable', - 'type_options': type_options, - 'viz_type': key, - } - return TemplateResponse(request, 'dataviz/draggable.html', params) - - def viz_scatterplot_stripped(request, key='hourly_rate'): return viz_scatterplot_helper(request, 'hourly_rate', 'dataviz/scatterplot_stripped.html', True) -@staff_member_required -def viz_scatterplot(request, key='hourly_rate', template='dataviz/scatterplot.html', hide_usernames=False): - return viz_scatterplot_helper(request, key, template, hide_usernames) - - def is_an_edge(handle, edges): for edge in edges: if handle == edge[0]: diff --git a/app/dataviz/templates/dataviz/calendar.html b/app/dataviz/templates/dataviz/calendar.html deleted file mode 100644 index 7d331685b06..00000000000 --- a/app/dataviz/templates/dataviz/calendar.html +++ /dev/null @@ -1,86 +0,0 @@ -{% load i18n static bundle %} - - -< back -{% bundle merge_js file jquery %} - -{% endbundle %} -{% include "dataviz/shared/nav.html" %} -

- - diff --git a/app/dataviz/templates/dataviz/chord.html b/app/dataviz/templates/dataviz/chord.html deleted file mode 100644 index ec7537cc61c..00000000000 --- a/app/dataviz/templates/dataviz/chord.html +++ /dev/null @@ -1,170 +0,0 @@ -{% load i18n static bundle %} - - - - - -< back - -

payers (top) / payees (top)

-

The redder the node is the longer it took the bounty hunter to turn around.

- -{% bundle merge_js file jquery %} - -{% endbundle %} - diff --git a/app/dataviz/templates/dataviz/circles.html b/app/dataviz/templates/dataviz/circles.html deleted file mode 100644 index 3cd1d726fbd..00000000000 --- a/app/dataviz/templates/dataviz/circles.html +++ /dev/null @@ -1,144 +0,0 @@ -{% extends "admin/base_site.html" %} -{% load humanize i18n static bundle %} -{% comment %} - Copyright (C) 2021 Gitcoin Core - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -{% endcomment %} -{% block extrastyle %}{{ block.super }} - {% bundle css file admin_dashboard %} - - {% endbundle %} -{% endblock %} -{% block coltype %}colMS{% endblock %} - -{% block bodyclass %}{{ block.super }} cohort{% endblock %} - -{% block breadcrumbs %}{% endblock %} - -{% block title %}Stats | Gitcoin -{% endblock %} - -{% block content %} -< back - - -{% include "dataviz/shared/nav.html" %} - -
- -
- -{% bundle merge_js file jquery %} - -{% endbundle %} - -{% endblock %} diff --git a/app/dataviz/templates/dataviz/draggable.html b/app/dataviz/templates/dataviz/draggable.html deleted file mode 100644 index fec7b04fa7d..00000000000 --- a/app/dataviz/templates/dataviz/draggable.html +++ /dev/null @@ -1,379 +0,0 @@ -{% load static bundle %} - - - - - User balance progression over time - {% bundle css file dataviz_draggable %} - - {% endbundle %} - - - -User balance progression over time - - - - < back - -

User Balance Progression over Time

- -Use your mouse to click and drag

- -
-1 - -360 -
-

- Days ^^ -

- - - - diff --git a/app/dataviz/templates/dataviz/heatmap.html b/app/dataviz/templates/dataviz/heatmap.html deleted file mode 100644 index f306e088b9d..00000000000 --- a/app/dataviz/templates/dataviz/heatmap.html +++ /dev/null @@ -1,127 +0,0 @@ -{% extends "admin/base_site.html" %} -{% load humanize i18n static bundle %} -{% comment %} - Copyright (C) 2021 Gitcoin Core - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -{% endcomment %} -{% block extrastyle %}{{ block.super }} - {% bundle css file admin_dashboard %} - - {% endbundle %} -{% endblock %} -{% block coltype %}colMS{% endblock %} - -{% block bodyclass %}{{ block.super }} cohort{% endblock %} - -{% block breadcrumbs %}{% endblock %} - -{% block title %}Heatmap | Gitcoin -{% endblock %} - -{% block content %} - - - - - - Days Hours Heatmap - - - {% bundle merge_js file jquery %} - - {% endbundle %} - {% bundle css file dataviz_heatmap %} - - {% endbundle %} - - - < back - {% include "dataviz/shared/nav.html" %} -
- -
-
- - -
- - -
-
-
-
- - -
-
- - -
-
-
- - -
- - - - -{% endblock %} diff --git a/app/dataviz/templates/dataviz/index.html b/app/dataviz/templates/dataviz/index.html deleted file mode 100644 index 34c0a846768..00000000000 --- a/app/dataviz/templates/dataviz/index.html +++ /dev/null @@ -1,103 +0,0 @@ -{% extends "admin/base_site.html" %} -{% load humanize %} -{% comment %} - Copyright (C) 2021 Gitcoin Core - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -{% endcomment %} - -{% load i18n static bundle %} - -{% block extrastyle %}{{ block.super }} - {% bundle css file admin_dashboard %} - - {% endbundle %} -{% endblock %} - -{% block coltype %}colMS{% endblock %} - -{% block bodyclass %}{{ block.super }} cohort{% endblock %} - -{% block breadcrumbs %}{% endblock %} - -{% block title %}Analytics Playground | Gitcoin -{% endblock %} - -{% block content %} -< back - - - -{% endblock %} diff --git a/app/dataviz/templates/dataviz/scatterplot.html b/app/dataviz/templates/dataviz/scatterplot.html deleted file mode 100644 index 6388ab3c6f1..00000000000 --- a/app/dataviz/templates/dataviz/scatterplot.html +++ /dev/null @@ -1,230 +0,0 @@ -{% load humanize %} -{% load i18n static bundle %} -{% comment %} - Copyright (C) 2021 Gitcoin Core - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -{% endcomment %} - - - - - < back - {% include "dataviz/shared/nav.html" %} -
-

Hourly Rate of Bounties over Time, by Repo

-{% bundle merge_js file jquery %} - -{% endbundle %} - - - - - - - - - - -
- Repo - - NumBounties - - Hourly Rate - - Standard Deviation -
diff --git a/app/dataviz/templates/dataviz/spiral.html b/app/dataviz/templates/dataviz/spiral.html deleted file mode 100644 index fe6e8f8b03f..00000000000 --- a/app/dataviz/templates/dataviz/spiral.html +++ /dev/null @@ -1,114 +0,0 @@ -{% extends "admin/base_site.html" %} -{% load humanize i18n static bundle %} -{% comment %} - Copyright (C) 2021 Gitcoin Core - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -{% endcomment %} -{% block extrastyle %}{{ block.super }} - {% bundle css file admin_dashboard %} - - {% endbundle %} -{% endblock %} -{% block coltype %}colMS{% endblock %} - -{% block bodyclass %}{{ block.super }} cohort{% endblock %} - -{% block breadcrumbs %}{% endblock %} - -{% block title %}Spiral Graph | Gitcoin -{% endblock %} - -{% block content %} - - - - - Condegram Spiral Plot - {% bundle css file typography %} - - - {% endbundle %} - - -< back - -

{{key}}

- {% include "dataviz/shared/nav.html" %} -
- {% bundle merge_js file jquery %} - - {% endbundle %} - - - - -{% endblock %} diff --git a/app/dataviz/templates/dataviz/square_graph.html b/app/dataviz/templates/dataviz/square_graph.html deleted file mode 100644 index c47d0a42e76..00000000000 --- a/app/dataviz/templates/dataviz/square_graph.html +++ /dev/null @@ -1,492 +0,0 @@ -{% load i18n static bundle %} - - - - - - - {% bundle merge_js file jquery %} - - {% endbundle %} - - - - - - - - - < back - {% include "dataviz/shared/nav.html" %} - -

- - - - - - diff --git a/app/dataviz/templates/dataviz/steamgraph.html b/app/dataviz/templates/dataviz/steamgraph.html deleted file mode 100644 index 03f5ae966e3..00000000000 --- a/app/dataviz/templates/dataviz/steamgraph.html +++ /dev/null @@ -1,267 +0,0 @@ -{% extends "admin/base_site.html" %} -{% load humanize bundle %} -{% comment %} - Copyright (C) 2021 Gitcoin Core - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -{% endcomment %} - -{% load i18n static %} - -{% block extrastyle %}{{ block.super }} - {% bundle css file admin_dashboard %} - - {% endbundle %} -{% endblock %} - -{% block coltype %}colMS{% endblock %} - -{% block bodyclass %}{{ block.super }} cohort{% endblock %} - -{% block breadcrumbs %}{% endblock %} - -{% block title %}Steamgraph | Gitcoin -{% endblock %} - -{% block content %} - - - - -< back -

Bounties by repo volume that were in status at this time

-{% include "dataviz/shared/nav.html" %} - -{% bundle merge_js file jquery %} - -{% endbundle %} -
-
-
-
- - -
-
- - -{% endblock %} diff --git a/app/dataviz/templates/dataviz/sunburst.html b/app/dataviz/templates/dataviz/sunburst.html deleted file mode 100644 index 727856ce881..00000000000 --- a/app/dataviz/templates/dataviz/sunburst.html +++ /dev/null @@ -1,109 +0,0 @@ -{% extends "admin/base_site.html" %} -{% load humanize i18n static bundle %} -{% comment %} - Copyright (C) 2021 Gitcoin Core - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -{% endcomment %} -{% block extrastyle %}{{ block.super }} - {% bundle css file admin_dashboard %} - - {% endbundle %} -{% endblock %} - -{% block coltype %}colMS{% endblock %} - -{% block bodyclass %}{{ block.super }} cohort{% endblock %} - -{% block breadcrumbs %}{% endblock %} - -{% block title %}Sunburst | Gitcoin -{% endblock %} - -{% block content %} - - - - {% bundle merge_js file jquery %} - - {% endbundle %} - {% bundle css file dataviz_sunburst %} - - - {% endbundle %} - - - - - < back - {% include "dataviz/shared/nav.html" %} -
-
-
- -
-
- - - - - - -{% endblock %}