Changeset - 9cfea9fb543f
[Not reviewed]
ennstatus/donate/functions.py
Show inline comments
 
@@ -17,6 +17,19 @@
 
import os
 
import os.path
 
import csv
 
import statistics
 

	
 
from collections import defaultdict
 

	
 
from babel.numbers import parse_decimal
 

	
 

	
 
def load_csv_file(file):
 

	
 
    with open(file, encoding='utf-8', newline='') as csvfile:
 
        csvreader = csv.reader(csvfile, delimiter=',')
 
        rows = list(csvreader)
 
    return rows
 

	
 

	
 
def load_csv(date):
 
@@ -24,11 +37,8 @@ def load_csv(date):
 
    filename = '.'.join([date, 'csv'])
 
    path = os.path.join('donations', filename)
 

	
 
    with open(path, encoding='utf-8', newline='') as csvfile:
 
        csvreader = csv.reader(csvfile, delimiter=',')
 

	
 
        for row in csvreader:
 
            yield row
 
    for row in load_csv_file(path):
 
        yield row
 

	
 

	
 
def get_choices():
 
@@ -39,3 +49,141 @@ def get_choices():
 
        if not file.startswith('.') \
 
           and file.endswith('.csv'):
 
            yield os.path.splitext(file)[0]
 

	
 

	
 
def get_all_csv():
 

	
 
    for file in os.listdir('donations'):
 
        path = os.path.join('donations', file)
 
        yield load_csv_file(path)
 

	
 

	
 
def get_all_rows():
 

	
 
    for csvfile in get_all_csv():
 
        for row in csvfile:
 
            yield row
 

	
 

	
 
def all_csv_to_dict():
 

	
 
    data = defaultdict(set)
 

	
 
    for row in get_all_rows():
 
        data[row[0]].add(parse_decimal(row[2], locale='de'))
 
    return data
 

	
 

	
 
def data_to_year(data):
 

	
 
    year_data = defaultdict(set)
 

	
 
    for date, values in data.items():
 
        year = date.split('-')[0]
 
        for value in values:
 
            year_data[year].add(value)
 

	
 
    return year_data
 

	
 

	
 
def data_to_month(data):
 

	
 
    month_data = defaultdict(set)
 

	
 
    for date, values in data.items():
 
        month = date.rsplit('-', maxsplit=1)[0]
 
        for value in values:
 
            month_data[month].add(value)
 

	
 
    return month_data
 

	
 

	
 
def year_sum(data):
 

	
 
    year_data = data_to_year(data)
 

	
 
    for year, values in year_data.items():
 
        year_data[year] = sum(values)
 

	
 
    return year_data
 

	
 

	
 
def month_sum(data):
 

	
 
    month_data = data_to_month(data)
 

	
 
    for month, values in month_data.items():
 
        month_data[month] = sum(values)
 

	
 
    return month_data
 

	
 

	
 
def get_all_years_mean(data):
 

	
 
    mean = set()
 
    year_data = year_sum(data)
 

	
 
    for year, values in year_data.items():
 
        mean.add(values)
 

	
 
    return statistics.mean(mean)
 

	
 

	
 
def get_months_mean(data):
 

	
 
    mean = set()
 
    month_data = month_sum(data)
 

	
 
    for year, values in month_data.items():
 
        mean.add(values)
 

	
 
    return statistics.mean(mean)
 

	
 

	
 
def get_best_year(data):
 

	
 
    year_data = year_sum(data)
 
    return max(year_data.items(), key=lambda x: x[1])
 

	
 

	
 
def get_best_month(data):
 

	
 
    month_data = month_sum(data)
 
    return max(month_data.items(), key=lambda x: x[1])
 

	
 

	
 
def get_highest_donation():
 

	
 
    data = all_csv_to_dict()
 

	
 
    for date, values in data.items():
 
        data[date] = max(values)
 

	
 
    return max(
 
        sorted(
 
            data.items(),
 
            key=lambda x: x[0],
 
            reverse=True
 
        ),
 
        key=lambda x: x[1]
 
    )
 

	
 

	
 
def get_median_donation():
 
    data = all_csv_to_dict()
 
    donations = []
 

	
 
    for value in data.values():
 
        donations.extend(value)
 

	
 
    return statistics.median(donations)
 

	
 

	
 
def get_mode_donation():
 
    data = all_csv_to_dict()
 
    donations = []
 

	
 
    for value in data.values():
 
        donations.extend(value)
 

	
 
    return statistics.mode(donations)
ennstatus/donate/views.py
Show inline comments
 
@@ -14,13 +14,27 @@
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
import json
 

	
 
from flask import (Blueprint, render_template, request,
 
                   redirect, url_for, current_app)
 

	
 
from babel.numbers import parse_decimal, format_decimal
 

	
 
from ennstatus.donate.forms import DateForm
 
from ennstatus.donate.functions import load_csv, get_choices
 
from ennstatus.donate.functions import (load_csv,
 
                                        get_choices,
 
                                        all_csv_to_dict,
 
                                        get_all_years_mean,
 
                                        get_months_mean,
 
                                        get_best_year,
 
                                        get_best_month,
 
                                        get_highest_donation,
 
                                        get_median_donation,
 
                                        get_mode_donation,
 
                                        year_sum,
 
                                        month_sum,
 
                                        )
 

	
 
from ennstatus.root.forms import BPMForm
 
from ennstatus.root.constants import BPM_ADDRESSES
 
@@ -125,4 +139,66 @@ def received():
 

	
 
@donate_page.route('/statistics')
 
def statistics():
 
    return render_template('donate/statistics.html')
 
    data = all_csv_to_dict()
 
    all_years_mean = format_decimal(
 
        get_all_years_mean(data),
 
        locale='de'
 
    )
 
    month_mean = format_decimal(
 
        get_months_mean(data),
 
        locale='de'
 
    )
 
    best_year = get_best_year(data)
 
    best_year = (best_year[0], format_decimal(best_year[1], locale='de'))
 

	
 
    best_month = get_best_month(data)
 
    best_month = (best_month[0], format_decimal(best_month[1], locale='de'))
 

	
 
    highest_donation = get_highest_donation()
 
    highest_donation = (highest_donation[0], format_decimal(highest_donation[1], locale='de'))
 

	
 
    median_donation = format_decimal(
 
        get_median_donation(),
 
        locale='de'
 
    )
 

	
 
    mode_donation = format_decimal(
 
        get_mode_donation(),
 
        locale='de'
 
    )
 

	
 
    return render_template('donate/statistics.html',
 
                           all_years_mean=all_years_mean,
 
                           month_mean=month_mean,
 
                           best_year=best_year,
 
                           best_month=best_month,
 
                           highest_donation=highest_donation,
 
                           median_donation=median_donation,
 
                           mode_donation=mode_donation
 
                           )
 

	
 
@donate_page.route('/data/year')
 
def yeardata():
 
    data = all_csv_to_dict() 
 
    data = year_sum(data)
 
    datalist = []
 

	
 
    for key, values in data.items():
 
        datalist.append({"key": key, "value": float(values)})
 
    datalist.sort(key=lambda x: x["key"])
 

	
 
    return json.dumps(datalist)
 

	
 

	
 
@donate_page.route('/data/month')
 
def monthdata():
 
    data = all_csv_to_dict()
 
    data = month_sum(data)
 
    data_list = []
 

	
 
    for key, values in data.items():
 
        data_list.append({"key": key, "value": float(values)})
 

	
 
    data_list.sort(key=lambda x: x["key"])
 

	
 
    return json.dumps(data_list)
ennstatus/static/css/barchart.css
Show inline comments
 
new file 100644
 
.axis path,
 
.axis line {
 
    fill: none;
 
    stroke: black;
 
    shape-rendering: crispEdges;
 
}
 
.axis text {
 
    font-family: sans-serif;
 
    font-size: 11px;
 
}
 
rect {
 
    -moz-transition: all 0.3s;
 
    -webkit-transition: all 0.3s;
 
    -o-transition: all 0.3s;
 
    transition: all 0.3s;
 
}
 
rect:hover{
 
    fill: orange;
 
}
 
.d3-tip {
 
  line-height: 1;
 
  font-weight: bold;
 
  padding: 12px;
 
  background: rgba(0, 0, 0, 0.8);
 
  color: #fff;
 
  border-radius: 2px;
 
}
 

	
 
/* Creates a small triangle extender for the tooltip */
 
.d3-tip:after {
 
  box-sizing: border-box;
 
  display: inline;
 
  font-size: 10px;
 
  width: 100%;
 
  line-height: 1;
 
  color: rgba(0, 0, 0, 0.8);
 
  content: "\25BC";
 
  position: absolute;
 
  text-align: center;
 
}
 

	
 
/* Style northward tooltips differently */
 
.d3-tip.n:after {
 
  margin: -1px 0 0 0;
 
  top: 100%;
 
  left: 0;
 
}
ennstatus/static/js/barcharts.js
Show inline comments
 
new file 100644
 
d3.select(window).on("resize", throttle);
 
var ywidth = document.getElementById('yearchart').offsetWidth - 40 -30;
 
var yheight = (ywidth / 2) - 20 - 30;
 

	
 
var mwidth = document.getElementById('monthchart').offsetWidth - 40 -30;
 
var mheight = (mwidth / 2) - 20 - 30;
 

	
 
var yearchart, monthchart, tip;
 

	
 
setup(ywidth, yheight, mwidth, mheight);
 

	
 
function setup(ywidth, yheight, mwidth, mheight) {
 
	yearchart = d3.select("#yearchart").append("svg")
 
		.attr("width", ywidth)
 
		.attr("height", yheight + 20 + 30)
 
		.append("g")
 
		.attr("transform", "translate(" + 40 + "," + 20 + ")");
 

	
 
	monthchart = d3.select("#monthchart").append("svg")
 
		.attr("width", mwidth)
 
		.attr("height", mheight + 20 + 30)
 
		.append("g")
 
		.attr("transform", "translate(" + 40 + "," + 20 + ")");
 

	
 
	tip = d3.tip()
 
		.attr('class', 'd3-tip')
 
		.offset([-10, 0])
 
		.html(function(d) {
 
			return "<strong>" + d.key + ":</strong> <span style='color:#00ae18'>" + d.value + "</span>";
 
		});
 
	yearchart.call(tip);
 
	monthchart.call(tip);
 
	draw(ywidth, yheight, mwidth, mheight)
 
}
 

	
 
function draw(ywidth, yheight, mwidth, mheight) {
 
	d3.json("/donate/data/year", function(err, data) {
 
		var xscale = d3.scale.ordinal().rangeRoundBands([0, ywidth], .1);
 
		xscale.domain(data.map(function(d) { return d.key; }));
 
                var yscale = d3.scale.linear().range([yheight, 0]);
 
		yscale.domain([0, d3.max(data, function(d) { return d.value; })]);
 
		var key = function(d) { return d.key; };
 

	
 
		var xaxis = d3.svg.axis()
 
			.scale(xscale)
 
			.orient("bottom");
 
		var yaxis = d3.svg.axis()
 
			.scale(yscale)
 
			.orient("left");
 

	
 
		yearchart.append("g")
 
			.attr("class", "x axis")
 
			.attr("transform", "translate(0, " + yheight + ")")
 
			.call(xaxis);
 
		yearchart.append("g")
 
			.attr("class", "y axis")
 
			.call(yaxis)
 
			.append("text")
 
			.attr("transform", "rotate(-90)")
 
			.attr("y", 6)
 
			.attr("dy", ".71em")
 
			.style("text-anchor", "end");
 

	
 
		yearchart.selectAll("rect")
 
			.data(data, key)
 
			.enter()
 
			.append("rect")
 
			.attr("class", "bar")
 
			.attr("x", function(d) {
 
				return xscale(d.key);
 
			})
 
			.attr("width", xscale.rangeBand())
 
			.attr("y", function(d) {
 
				return yscale(d.value);
 
			})
 
			.attr("height", function(d) {
 
				return yheight - yscale(d.value);
 
			})
 
			.attr("fill", function(d) {
 
				return "#00ae18";
 
			})
 
			.on("mouseover", tip.show)
 
			.on("mouseout", tip.hide)
 
			
 
	});
 
	d3.json("/donate/data/month", function(err, data) {
 
		var xscale = d3.scale.ordinal().rangeRoundBands([0, mwidth], .1);
 
		xscale.domain(data.map(function(d) { return d.key }));
 
		var yscale = d3.scale.linear().range([mheight, 0]);
 
		yscale.domain([0, d3.max(data, function(d) { return d.value; })]);
 

	
 
		var key = function(d) { return d.key; };
 
		var xaxis = d3.svg.axis()
 
			.scale(xscale)
 
			.orient("bottom");
 
		var yaxis = d3.svg.axis()
 
			.scale(yscale)
 
			.orient("left");
 

	
 
		monthchart.append("g")
 
			.attr("class", "x axis")
 
			.attr("transform", "translate(0, " + mheight + ")")
 
			.call(xaxis)
 
		monthchart.append("g")
 
			.attr("class", "y axis")
 
			.call(yaxis)
 
			.append("text")
 
			.attr("transform", "rotate(-90)")
 
			.attr("y", 6)
 
			.attr("dy", ".71em")
 
			.style("text-anchor", "end");
 

	
 
		monthchart.selectAll("rect")
 
			.data(data, key)
 
			.enter()
 
			.append("rect")
 
			.attr("class", "bar")
 
			.attr("x", function(d) {
 
				return xscale(d.key);
 
			})
 
			.attr("width", xscale.rangeBand())
 
			.attr("y", function(d) {
 
				return yscale(d.value);
 
			})
 
			.attr("height", function(d) {
 
				return mheight - yscale(d.value);
 
			})
 
			.attr("fill", function(d) {
 
				return "#00ae18";
 
			})
 
			.on("mouseover", tip.show)
 
			.on("mouseout", tip.hide)
 
			
 
	});	
 
};
 

	
 
function redraw() {
 
	ywidth = document.getElementById('yearchart').offsetWidth - 40 -30;
 
	yheight = (ywidth / 2) - 20 - 30;
 
	mwidth = document.getElementById('monthchart').offsetWidth - 40 -30;
 
	mheight = (mwidth / 2) - 20 - 30;
 
	d3.select('svg').remove();
 
	setup(ywidth, yheight, mwidth, mheight);
 
}
 

	
 
var throttleTimer;
 
function throttle() {
 
	  window.clearTimeout(throttleTimer);
 
	      throttleTimer = window.setTimeout(function() {
 
		            redraw();
 
			        }, 200);
 
}
ennstatus/static/js/d3-tip/LICENSE
Show inline comments
 
new file 100644
 
The MIT License (MIT)
 
Copyright (c) 2013 Justin Palmer
 

	
 
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 

	
 
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 

	
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
\ No newline at end of file
ennstatus/static/js/d3-tip/README.md
Show inline comments
 
new file 100644
 
# d3.tip: Tooltips for d3.js visualizations
 

	
 
[![](https://github-images.s3.amazonaws.com/skitch/Screen_Shot_2013-04-08_at_11.40.10_AM-20130408-114054.png)](http://bl.ocks.org/Caged/6476579)
 

	
 
* [See a live demo](http://bl.ocks.org/Caged/6476579)
 
* [Example code](/examples)
 

	
 
### API Docs
 
See the [API Documentation](docs/index.md)
 

	
 
### Download Latest Version
 
* [Development Version](https://raw.github.com/Caged/d3-tip/master/index.js) : **6kb** / **~2kb gzipped**
 

	
 
### Install with Bower
 
```
 
bower install d3-tip
 
```
 

	
 
### Quick Usage
 
```javascript
 
/* Initialize tooltip */
 
tip = d3.tip().attr('class', 'd3-tip').html(function(d) { return d; });
 

	
 
/* Invoke the tip in the context of your visualization */
 
vis.call(tip)
 

	
 
vis.selectAll('rect')
 
  .data(data)
 
.enter().append('rect')
 
  .attr('width', function() { return x.rangeBand() })
 
  .attr('height', function(d) { return h - y(d) })
 
  .attr('y', function(d) { return y(d) })
 
  .attr('x', function(d, i) { return x(i) })
 
  .on('mouseover', tip.show)
 
  .on('mouseout', tip.hide)
 
```
 

	
 
If you want basic styling, you can include `example-styles.css` using a service like
 
rawgithub.com.
 

	
 
```html
 
<link rel="stylesheet" href="//rawgithub.com/Caged/d3-tip/master/examples/example-styles.css">
 
```
ennstatus/static/js/d3-tip/index.js
Show inline comments
 
new file 100644
 
// d3.tip
 
// Copyright (c) 2013 Justin Palmer
 
//
 
// Tooltips for d3.js SVG visualizations
 

	
 
(function (root, factory) {
 
  if (typeof define === 'function' && define.amd) {
 
    // AMD. Register as an anonymous module with d3 as a dependency.
 
    define(['d3'], factory)
 
  } else if (typeof module === 'object' && module.exports) {
 
    // CommonJS
 
    module.exports = function(d3) {
 
      d3.tip = factory(d3)
 
      return d3.tip
 
    }
 
  } else {
 
    // Browser global.
 
    root.d3.tip = factory(root.d3)
 
  }
 
}(this, function (d3) {
 

	
 
  // Public - contructs a new tooltip
 
  //
 
  // Returns a tip
 
  return function() {
 
    var direction = d3_tip_direction,
 
        offset    = d3_tip_offset,
 
        html      = d3_tip_html,
 
        node      = initNode(),
 
        svg       = null,
 
        point     = null,
 
        target    = null
 

	
 
    function tip(vis) {
 
      svg = getSVGNode(vis)
 
      point = svg.createSVGPoint()
 
      document.body.appendChild(node)
 
    }
 

	
 
    // Public - show the tooltip on the screen
 
    //
 
    // Returns a tip
 
    tip.show = function() {
 
      var args = Array.prototype.slice.call(arguments)
 
      if(args[args.length - 1] instanceof SVGElement) target = args.pop()
 

	
 
      var content = html.apply(this, args),
 
          poffset = offset.apply(this, args),
 
          dir     = direction.apply(this, args),
 
          nodel   = getNodeEl(),
 
          i       = directions.length,
 
          coords,
 
          scrollTop  = document.documentElement.scrollTop || document.body.scrollTop,
 
          scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft
 

	
 
      nodel.html(content)
 
        .style({ opacity: 1, 'pointer-events': 'all' })
 

	
 
      while(i--) nodel.classed(directions[i], false)
 
      coords = direction_callbacks.get(dir).apply(this)
 
      nodel.classed(dir, true).style({
 
        top: (coords.top +  poffset[0]) + scrollTop + 'px',
 
        left: (coords.left + poffset[1]) + scrollLeft + 'px'
 
      })
 

	
 
      return tip
 
    }
 

	
 
    // Public - hide the tooltip
 
    //
 
    // Returns a tip
 
    tip.hide = function() {
 
      var nodel = getNodeEl()
 
      nodel.style({ opacity: 0, 'pointer-events': 'none' })
 
      return tip
 
    }
 

	
 
    // Public: Proxy attr calls to the d3 tip container.  Sets or gets attribute value.
 
    //
 
    // n - name of the attribute
 
    // v - value of the attribute
 
    //
 
    // Returns tip or attribute value
 
    tip.attr = function(n, v) {
 
      if (arguments.length < 2 && typeof n === 'string') {
 
        return getNodeEl().attr(n)
 
      } else {
 
        var args =  Array.prototype.slice.call(arguments)
 
        d3.selection.prototype.attr.apply(getNodeEl(), args)
 
      }
 

	
 
      return tip
 
    }
 

	
 
    // Public: Proxy style calls to the d3 tip container.  Sets or gets a style value.
 
    //
 
    // n - name of the property
 
    // v - value of the property
 
    //
 
    // Returns tip or style property value
 
    tip.style = function(n, v) {
 
      if (arguments.length < 2 && typeof n === 'string') {
 
        return getNodeEl().style(n)
 
      } else {
 
        var args =  Array.prototype.slice.call(arguments)
 
        d3.selection.prototype.style.apply(getNodeEl(), args)
 
      }
 

	
 
      return tip
 
    }
 

	
 
    // Public: Set or get the direction of the tooltip
 
    //
 
    // v - One of n(north), s(south), e(east), or w(west), nw(northwest),
 
    //     sw(southwest), ne(northeast) or se(southeast)
 
    //
 
    // Returns tip or direction
 
    tip.direction = function(v) {
 
      if (!arguments.length) return direction
 
      direction = v == null ? v : d3.functor(v)
 

	
 
      return tip
 
    }
 

	
 
    // Public: Sets or gets the offset of the tip
 
    //
 
    // v - Array of [x, y] offset
 
    //
 
    // Returns offset or
 
    tip.offset = function(v) {
 
      if (!arguments.length) return offset
 
      offset = v == null ? v : d3.functor(v)
 

	
 
      return tip
 
    }
 

	
 
    // Public: sets or gets the html value of the tooltip
 
    //
 
    // v - String value of the tip
 
    //
 
    // Returns html value or tip
 
    tip.html = function(v) {
 
      if (!arguments.length) return html
 
      html = v == null ? v : d3.functor(v)
 

	
 
      return tip
 
    }
 

	
 
    // Public: destroys the tooltip and removes it from the DOM
 
    //
 
    // Returns a tip
 
    tip.destroy = function() {
 
      if(node) {
 
        getNodeEl().remove();
 
        node = null;
 
      }
 
      return tip;
 
    }
 

	
 
    function d3_tip_direction() { return 'n' }
 
    function d3_tip_offset() { return [0, 0] }
 
    function d3_tip_html() { return ' ' }
 

	
 
    var direction_callbacks = d3.map({
 
      n:  direction_n,
 
      s:  direction_s,
 
      e:  direction_e,
 
      w:  direction_w,
 
      nw: direction_nw,
 
      ne: direction_ne,
 
      sw: direction_sw,
 
      se: direction_se
 
    }),
 

	
 
    directions = direction_callbacks.keys()
 

	
 
    function direction_n() {
 
      var bbox = getScreenBBox()
 
      return {
 
        top:  bbox.n.y - node.offsetHeight,
 
        left: bbox.n.x - node.offsetWidth / 2
 
      }
 
    }
 

	
 
    function direction_s() {
 
      var bbox = getScreenBBox()
 
      return {
 
        top:  bbox.s.y,
 
        left: bbox.s.x - node.offsetWidth / 2
 
      }
 
    }
 

	
 
    function direction_e() {
 
      var bbox = getScreenBBox()
 
      return {
 
        top:  bbox.e.y - node.offsetHeight / 2,
 
        left: bbox.e.x
 
      }
 
    }
 

	
 
    function direction_w() {
 
      var bbox = getScreenBBox()
 
      return {
 
        top:  bbox.w.y - node.offsetHeight / 2,
 
        left: bbox.w.x - node.offsetWidth
 
      }
 
    }
 

	
 
    function direction_nw() {
 
      var bbox = getScreenBBox()
 
      return {
 
        top:  bbox.nw.y - node.offsetHeight,
 
        left: bbox.nw.x - node.offsetWidth
 
      }
 
    }
 

	
 
    function direction_ne() {
 
      var bbox = getScreenBBox()
 
      return {
 
        top:  bbox.ne.y - node.offsetHeight,
 
        left: bbox.ne.x
 
      }
 
    }
 

	
 
    function direction_sw() {
 
      var bbox = getScreenBBox()
 
      return {
 
        top:  bbox.sw.y,
 
        left: bbox.sw.x - node.offsetWidth
 
      }
 
    }
 

	
 
    function direction_se() {
 
      var bbox = getScreenBBox()
 
      return {
 
        top:  bbox.se.y,
 
        left: bbox.e.x
 
      }
 
    }
 

	
 
    function initNode() {
 
      var node = d3.select(document.createElement('div'))
 
      node.style({
 
        position: 'absolute',
 
        top: 0,
 
        opacity: 0,
 
        'pointer-events': 'none',
 
        'box-sizing': 'border-box'
 
      })
 

	
 
      return node.node()
 
    }
 

	
 
    function getSVGNode(el) {
 
      el = el.node()
 
      if(el.tagName.toLowerCase() === 'svg')
 
        return el
 

	
 
      return el.ownerSVGElement
 
    }
 

	
 
    function getNodeEl() {
 
      if(node === null) {
 
        node = initNode();
 
        // re-add node to DOM
 
        document.body.appendChild(node);
 
      };
 
      return d3.select(node);
 
    }
 

	
 
    // Private - gets the screen coordinates of a shape
 
    //
 
    // Given a shape on the screen, will return an SVGPoint for the directions
 
    // n(north), s(south), e(east), w(west), ne(northeast), se(southeast), nw(northwest),
 
    // sw(southwest).
 
    //
 
    //    +-+-+
 
    //    |   |
 
    //    +   +
 
    //    |   |
 
    //    +-+-+
 
    //
 
    // Returns an Object {n, s, e, w, nw, sw, ne, se}
 
    function getScreenBBox() {
 
      var targetel   = target || d3.event.target;
 

	
 
      while ('undefined' === typeof targetel.getScreenCTM && 'undefined' === targetel.parentNode) {
 
          targetel = targetel.parentNode;
 
      }
 

	
 
      var bbox       = {},
 
          matrix     = targetel.getScreenCTM(),
 
          tbbox      = targetel.getBBox(),
 
          width      = tbbox.width,
 
          height     = tbbox.height,
 
          x          = tbbox.x,
 
          y          = tbbox.y
 

	
 
      point.x = x
 
      point.y = y
 
      bbox.nw = point.matrixTransform(matrix)
 
      point.x += width
 
      bbox.ne = point.matrixTransform(matrix)
 
      point.y += height
 
      bbox.se = point.matrixTransform(matrix)
 
      point.x -= width
 
      bbox.sw = point.matrixTransform(matrix)
 
      point.y -= height / 2
 
      bbox.w  = point.matrixTransform(matrix)
 
      point.x += width
 
      bbox.e = point.matrixTransform(matrix)
 
      point.x -= width / 2
 
      point.y -= height / 2
 
      bbox.n = point.matrixTransform(matrix)
 
      point.y += height
 
      bbox.s = point.matrixTransform(matrix)
 

	
 
      return bbox
 
    }
 

	
 
    return tip
 
  };
 

	
 
}));
ennstatus/static/js/monthbar.js
Show inline comments
 
new file 100644
 
d3.select(window).on("resize", mthrottle);
 
var width = document.getElementById('monthchart').offsetWidth - 40 -30;
 
var height = (width / 2) - 20 - 30;
 

	
 
var svg, tip;
 

	
 
msetup(width, height);
 

	
 
function msetup(width, height) {
 
	svg = d3.select("#monthchart").append("svg")
 
		.attr("width", width)
 
		.attr("height", height + 20 + 30)
 
		.append("g")
 
		.attr("transform", "translate(" + 40 + "," + 20 + ")");
 
	tip = d3.tip()
 
		.attr('class', 'd3-tip')
 
		.offset([-10, 0])
 
		.html(function(d) {
 
			return "<strong>" + d.key + ":</strong> <span style='color:#00ae18'>" + d.value + "</span>";
 
		});
 
	svg.call(tip);
 
	mdraw(width, height)
 
}
 

	
 
function mdraw(width, height) {
 
	d3.json("/donate/data/month", function(err, data) {
 
		var xscale = d3.scale.ordinal().rangeRoundBands([0, width], .1);
 
		xscale.domain(data.map(function(d) { return d.key; }));
 
                var yscale = d3.scale.linear().range([height, 0]);
 
		yscale.domain([0, d3.max(data, function(d) { return d.value; })]);
 
		var key = function(d) { return d.key; };
 

	
 
		var xaxis = d3.svg.axis()
 
			.scale(xscale)
 
			.orient("bottom");
 
		var yaxis = d3.svg.axis()
 
			.scale(yscale)
 
			.orient("left");
 

	
 
		svg.append("g")
 
			.attr("class", "x axis")
 
			.attr("transform", "translate(0, " + height + ")")
 
			.call(xaxis);
 
		svg.append("g")
 
			.attr("class", "y axis")
 
			.call(yaxis)
 
			.append("text")
 
			.attr("transform", "rotate(-90)")
 
			.attr("y", 6)
 
			.attr("dy", ".71em")
 
			.style("text-anchor", "end");
 

	
 
		svg.selectAll("rect")
 
			.data(data, key)
 
			.enter()
 
			.append("rect")
 
			.attr("class", "bar")
 
			.attr("x", function(d) {
 
				return xscale(d.key);
 
			})
 
			.attr("width", xscale.rangeBand())
 
			.attr("y", function(d) {
 
				return yscale(d.value);
 
			})
 
			.attr("height", function(d) {
 
				return height - yscale(d.value);
 
			})
 
			.attr("fill", function(d) {
 
				return "#00ae18";
 
			})
 
			.on("mouseover", tip.show)
 
			.on("mouseout", tip.hide)
 
			
 
	});	
 
};
 

	
 
function mredraw() {
 
	width = document.getElementById('monthchart').offsetWidth - 40 -30;
 
	height = (width / 2) - 20 - 30;
 
	d3.select('svg').remove();
 
	msetup(width, height);
 
}
 

	
 
var throttleTimer;
 
function mthrottle() {
 
	  window.clearTimeout(throttleTimer);
 
	      throttleTimer = window.setTimeout(function() {
 
		            mredraw();
 
			        }, 200);
 
}
ennstatus/static/js/yearbar.js
Show inline comments
 
new file 100644
 
d3.select(window).on("resize", throttle);
 
var width = document.getElementById('yearchart').offsetWidth - 40 -30;
 
var height = (width / 2) - 20 - 30;
 

	
 
var svg, tip;
 

	
 
setup(width, height);
 

	
 
function setup(width, height) {
 
	svg = d3.select("#yearchart").append("svg")
 
		.attr("width", width)
 
		.attr("height", height + 20 + 30)
 
		.append("g")
 
		.attr("transform", "translate(" + 40 + "," + 20 + ")");
 
	tip = d3.tip()
 
		.attr('class', 'd3-tip')
 
		.offset([-10, 0])
 
		.html(function(d) {
 
			return "<strong>" + d.key + ":</strong> <span style='color:#00ae18'>" + d.value + "</span>";
 
		});
 
	svg.call(tip);
 
	draw(width, height)
 
}
 

	
 
function draw(width, height) {
 
	d3.json("/donate/data/year", function(err, data) {
 
		var xscale = d3.scale.ordinal().rangeRoundBands([0, width], .1);
 
		xscale.domain(data.map(function(d) { return d.key; }));
 
                var yscale = d3.scale.linear().range([height, 0]);
 
		yscale.domain([0, d3.max(data, function(d) { return d.value; })]);
 
		var key = function(d) { return d.key; };
 

	
 
		var xaxis = d3.svg.axis()
 
			.scale(xscale)
 
			.orient("bottom");
 
		var yaxis = d3.svg.axis()
 
			.scale(yscale)
 
			.orient("left");
 

	
 
		svg.append("g")
 
			.attr("class", "x axis")
 
			.attr("transform", "translate(0, " + height + ")")
 
			.call(xaxis);
 
		svg.append("g")
 
			.attr("class", "y axis")
 
			.call(yaxis)
 
			.append("text")
 
			.attr("transform", "rotate(-90)")
 
			.attr("y", 6)
 
			.attr("dy", ".71em")
 
			.style("text-anchor", "end");
 

	
 
		svg.selectAll("rect")
 
			.data(data, key)
 
			.enter()
 
			.append("rect")
 
			.attr("class", "bar")
 
			.attr("x", function(d) {
 
				return xscale(d.key);
 
			})
 
			.attr("width", xscale.rangeBand())
 
			.attr("y", function(d) {
 
				return yscale(d.value);
 
			})
 
			.attr("height", function(d) {
 
				return height - yscale(d.value);
 
			})
 
			.attr("fill", function(d) {
 
				return "#00ae18";
 
			})
 
			.on("mouseover", tip.show)
 
			.on("mouseout", tip.hide)
 
			
 
	});	
 
};
 

	
 
function redraw() {
 
	width = document.getElementById('yearchart').offsetWidth - 40 -30;
 
	height = (width / 2) - 20 - 30;
 
	d3.select('#yearchart').select('svg').remove();
 
	setup(width, height);
 
}
 

	
 
var throttleTimer;
 
function throttle() {
 
	  window.clearTimeout(throttleTimer);
 
	      throttleTimer = window.setTimeout(function() {
 
		            redraw();
 
			        }, 200);
 
}
ennstatus/static/js/yearchart.js
Show inline comments
 
new file 100644
 
var width = document.getElementById('yearchart').offsetWidth;
 
var height = width / 2;
 

	
 
setup(width, height);
 

	
 
function setup(width, height) {
 
	svg = d3.select("#yearchart").append("svg")
 
		.attr("width", width)
 
		.attr("height", height)
 
	draw(width, height)
 
}
 

	
 
function draw(width, height) {
 
	d3.json("/donate/data/year", function(err, data) {
 
		var xscale = d3.scale.ordinal()
 
				.domain(d3.range(data.length))
 
		                .rangeRoundBands([0, width], 0.05);
 
                var yscale = d3.scale.linear()
 
	                        .domain([0, d3.max(data, function(d) {return d.value;})]);
 
		var key = function(d) { return d.key; };
 

	
 
		svg.selectAll("rect")
 
			.data(data, key)
 
			.enter()
 
			.append("rect")
 
			.attr("x", function(d, i) {
 
				return xscale(i);
 
			})
 
			.attr("y", function(d, i) {
 
				return height - yscale(d.value);
 
			})
 
			.attr("width", xscale.rangeBand())
 
			.attr("height", function(d) {
 
				return yscale(d.value);
 
			})
 
			.attr("fill", function(d) {
 
				return "rgb(0, 0, " + (d.value * 10) + ")";
 
			});
 
			
 
	}		
 
}
ennstatus/templates/donate/statistics.html
Show inline comments
 
@@ -19,10 +19,54 @@
 

	
 
{% set title = "Donate - Statistics" %}
 

	
 
{% block styles %}
 
  {{ super() }}
 
  <link rel="stylesheet" href="{{ url_for('static', filename='css/barchart.css') }}" />
 
{% endblock %}
 

	
 
{% block content %}
 
  <div class="col-md-12">
 
    <h2>Donate Statistics</h2>
 
  </div>
 
  <div class="col-md-12">
 
    <dl class="dl-horizontal">
 
      <dt>Mean per year:</dt>
 
      <dd>{{ all_years_mean }}</dd>
 
      <dt>Mean per month:</dt>
 
      <dd>{{ month_mean }}</dd>
 
      <dt>Best year:</dt>
 
      <dd>{{ best_year[0] }} {{ best_year[1] }}</dd>
 
      <dt>Best month:</dt>
 
      <dd>{{ best_month[0] }} {{ best_month[1] }}</dd>
 
      <dt>Highest donation:</dt>
 
      <dd>{{ highest_donation[0] }} {{ highest_donation[1] }}</dd>
 
      <dt>Median donation:</dt>
 
      <dd>{{ median_donation }}</dd>
 
      <dt>Mode donation:</dt>
 
      <dd>{{ mode_donation }}</dd>
 
    </dl>
 
  </div>
 
  <div class="col-md-12">
 
    <div class="panel panel-default">
 
      <div class="panel-heading">Donations per year</div>
 
      <div class="panel-body">
 
        <div id="yearchart" class="center-block"></div>
 
      </div>
 
    </div>
 
  </div>
 
  <div class="col-md-12">
 
    <div class="panel panel-default">
 
      <div class="panel-heading">Donations per month</div>
 
      <div class="panel-body">
 
        <div id="monthchart"></div>
 
      </div>
 
    </div>
 
  </div>
 
{% endblock %}
 

	
 
{% block scripts %}
 
  {{ super() }}
 
  <script src="{{ url_for('static', filename='js/d3/d3.min.js') }}"></script>
 
  <script src="{{ url_for('static', filename='js/d3-tip/index.js') }}"></script>
 
  <script src="{{ url_for('static', filename='js/barcharts.js') }}"></script>
 
{% endblock %}
0 comments (0 inline, 0 general)