From 698548529a570b3cab576ae33b5b514d2db7ddb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Tue, 10 Sep 2013 02:03:23 +0200 Subject: First pygal/chart stuff --- app/views/__init__.py | 7 +- app/views/svg.py | 43 ++++++++++ static/pygal/pygal.js | 180 ++++++++++++++++++++++++++++++++++++++++++ templates/expenses/show.jinja | 8 +- 4 files changed, 234 insertions(+), 4 deletions(-) create mode 100644 app/views/svg.py create mode 100644 static/pygal/pygal.js diff --git a/app/views/__init__.py b/app/views/__init__.py index d64b945..6405d3e 100644 --- a/app/views/__init__.py +++ b/app/views/__init__.py @@ -13,8 +13,8 @@ def handle_mobile(): flask.g.is_mobile = any((x in ua) for x in mobile_checks) @app.template_filter("static_url") -def static_url(s): - return url_for("static", filename=s) +def static_url(s, **kwargs): + return url_for("static", filename=s, **kwargs) @app.template_filter("eur") def eur(s): @@ -31,10 +31,11 @@ def format_date(s, format="%Y/%m"): def page_not_found (error): return render_template("404.jinja", page = request.path), 404 -from . import categories, consts, expenses +from . import categories, consts, expenses, svg app.register_blueprint(expenses.mod) app.register_blueprint(consts.mod, url_prefix="/const") app.register_blueprint(categories.mod, url_prefix="/cat") +app.register_blueprint(svg.mod, url_prefix="/svg") app.add_url_rule("/", endpoint = "index", build_only = True) diff --git a/app/views/svg.py b/app/views/svg.py new file mode 100644 index 0000000..06666b3 --- /dev/null +++ b/app/views/svg.py @@ -0,0 +1,43 @@ +from ..flask_extend import Blueprint + +from .expenses import calc_month_exp +from . import static_url +from ..model import Category + +from pygal.style import DefaultStyle as Style +from pygal import Pie as _Pie + +mod = Blueprint('svg', __name__) + +Style.background = Style.plot_background = "rgba(255,0,0,0)" + +def Pie(*args, **kwargs): + # does not work as functools.partial, because static_url would be called without request + return _Pie(*args, + style=Style, + js = [static_url('pygal/pygal.js', _external = True)], + legend_font_size = 24, + tooltip_font_size = 24, + value_font_size = 24, + margin = 0, + legend_at_bottom = True, + pretty_print = True, + **kwargs) + +@mod.route("/month//.svg") +def month(year, month): + exp = calc_month_exp(year, month) + pie = Pie() + + expenses = {} + for c in exp.catexps: + expenses[c.cat.name] = float(c.expense) + + for c in Category.query.order_by(Category.name).all(): + pie.add(c.name, expenses.get(c.name, 0.0)) + + if not expenses: + pie.raw_series = [] + pie.add("Empty", 0.1) + + return pie.render_response() diff --git a/static/pygal/pygal.js b/static/pygal/pygal.js new file mode 100644 index 0000000..8cc1b21 --- /dev/null +++ b/static/pygal/pygal.js @@ -0,0 +1,180 @@ +(function(s,m){function $(a){var b=ra[a]={},c,e;a=a.split(/\s+/);c=0;for(e=a.length;cf;f+=2)d||(e-=parseFloat(c.css(a,"padding"+P[f]))||0),e="margin"===d?e+(parseFloat(c.css(a,d+P[f]))||0):e-(parseFloat(c.css(a,"border"+P[f]+"Width"))||0);return e+"px"}e=R(a,b);if(0>e||null==e)e=a.style[b];if(ga.test(e))return e;e=parseFloat(e)||0;if(d)for(;4>f;f+=2)e+=parseFloat(c.css(a,"padding"+P[f]))||0,"padding"!==d&&(e+=parseFloat(c.css(a,"border"+P[f]+"Width"))||0),"margin"===d&&(e+=parseFloat(c.css(a,d+ +P[f]))||0);return e+"px"}function va(a){return function(b,d){"string"!==typeof b&&(d=b,b="*");if(c.isFunction(d))for(var e=b.toLowerCase().split(wa),f=0,g=e.length,k,h;f").appendTo(b),e=d.css("display");d.remove();if("none"===e||""===e){J||(J=r.createElement("iframe"),J.frameBorder=J.width=J.height=0);b.appendChild(J);if(!X||!J.createElement)X=(J.contentWindow||J.contentDocument).document,X.write((c.support.boxModel?"":"")+""),X.close();d=X.createElement(a);X.body.appendChild(d);e=c.css(d,"display");b.removeChild(J)}ja[a]= +e}return ja[a]}function Ba(a){return c.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}var r=s.document,ib=s.navigator,jb=s.location,c=function(){function a(){if(!b.isReady){try{r.documentElement.doScroll("left")}catch(c){setTimeout(a,1);return}b.ready()}}var b=function(a,c){return new b.fn.init(a,c,f)},c=s.jQuery,e=s.$,f,g=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,k=/\S/,h=/^\s+/,l=/\s+$/,p=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,q=/^[\],:{}\s]*$/,n=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,kb=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, +t=/(?:^|:|,)(?:\s*\[)+/g,lb=/(webkit)[ \/]([\w.]+)/,mb=/(opera)(?:.*version)?[ \/]([\w.]+)/,w=/(msie) ([\w.]+)/,A=/(mozilla)(?:.*? rv:([\w.]+))?/,x=/-([a-z]|[0-9])/ig,F=/^-ms-/,u=function(a,b){return(b+"").toUpperCase()},y=ib.userAgent,v,H,nb=Object.prototype.toString,ka=Object.prototype.hasOwnProperty,la=Array.prototype.push,Z=Array.prototype.slice,Ca=String.prototype.trim,Da=Array.prototype.indexOf,Ea={};b.fn=b.prototype={constructor:b,init:function(a,c,d){var e;if(!a)return this;if(a.nodeType)return this.context= +this[0]=a,this.length=1,this;if("body"===a&&!c&&r.body)return this.context=r,this[0]=r.body,this.selector=a,this.length=1,this;if("string"===typeof a){if((e="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&3<=a.length?[null,a,null]:g.exec(a))&&(e[1]||!c)){if(e[1])return d=(c=c instanceof b?c[0]:c)?c.ownerDocument||c:r,(a=p.exec(a))?b.isPlainObject(c)?(a=[r.createElementNS("http://www.w3.org/2000/svg",a[1])],b.fn.attr.call(a,c,!0)):a=[d.createElementNS("http://www.w3.org/2000/svg",a[1])]:(a=b.buildFragment([e[1]], +[d]),a=(a.cacheable?b.clone(a.fragment):a.fragment).childNodes),b.merge(this,a);if((c=r.getElementById(e[2]))&&c.parentNode){if(c.id!==e[2])return d.find(a);this.length=1;this[0]=c}this.context=r;this.selector=a;return this}return!c||c.jquery?(c||d).find(a):this.constructor(c).find(a)}if(b.isFunction(a))return d.ready(a);a.selector!==m&&(this.selector=a.selector,this.context=a.context);return b.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return Z.call(this, +0)},get:function(a){return null==a?this.toArray():0>a?this[this.length+a]:this[a]},pushStack:function(a,c,d){var e=this.constructor();b.isArray(a)?la.apply(e,a):b.merge(e,a);e.prevObject=this;e.context=this.context;"find"===c?e.selector=this.selector+(this.selector?" ":"")+d:c&&(e.selector=this.selector+"."+c+"("+d+")");return e},each:function(a,c){return b.each(this,a,c)},ready:function(a){b.bindReady();v.add(a);return this},eq:function(a){a=+a;return-1===a?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)}, +last:function(){return this.eq(-1)},slice:function(){return this.pushStack(Z.apply(this,arguments),"slice",Z.call(arguments).join(","))},map:function(a){return this.pushStack(b.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:la,sort:[].sort,splice:[].splice};b.fn.init.prototype=b.fn;b.extend=b.fn.extend=function(){var a,c,d,e,f,v=arguments[0]||{},g=1,k=arguments.length,h=!1;"boolean"===typeof v&&(h=v,v=arguments[1]||{},g=2);"object"!== +typeof v&&!b.isFunction(v)&&(v={});k===g&&(v=this,--g);for(;gc?Math.max(0,d+c):c:0;ca.indexOf("compatible")&&A.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}b.extend(!0,a,this);a.superclass=this; +a.fn=a.prototype=this();a.fn.constructor=a;a.sub=this.sub;a.fn.init=function(d,e){e&&(e instanceof b&&!(e instanceof a))&&(e=a(e));return b.fn.init.call(this,d,e,c)};a.fn.init.prototype=a.fn;var c=a(r);return a},browser:{}});b.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){Ea["[object "+b+"]"]=b.toLowerCase()});y=b.uaMatch(y);y.browser&&(b.browser[y.browser]=!0,b.browser.version=y.version);b.browser.webkit&&(b.browser.safari=!0);k.test("\u00a0")&&(h=/^[\s\xA0]+/, +l=/[\s\xA0]+$/);f=b(r);r.addEventListener?H=function(){r.removeEventListener("DOMContentLoaded",H,!1);b.ready()}:r.attachEvent&&(H=function(){"complete"===r.readyState&&(r.detachEvent("onreadystatechange",H),b.ready())});return b}(),ra={};c.Callbacks=function(a){a=a?ra[a]||$(a):{};var b=[],d=[],e,f,g,k,h,l,p=function(d){var e,f,g,k;e=0;for(f=d.length;e=g&&a&&c.isFunction(a.promise)?a:c.Deferred(),p=l.promise();if(1e)return null; +a=k?e:0;for(d=k?e+1:g.length;af.indexOf(":")?"on"+f:"",d){if(a.result=m,a.target||(a.target=d),b=null!=b?c.makeArray(b):[],b.unshift(a),l=c.event.special[f]||{},!(l.trigger&&!1===l.trigger.apply(d, +b))){q=[[d,l.bindType||f]];if(!e&&!l.noBubble&&!c.isWindow(d)){p=l.delegateType||f;g=La.test(p+f)?d:d.parentNode;for(h=null;g;g=g.parentNode)q.push([g,p]),h=g;h&&h===d.ownerDocument&&q.push([h.defaultView||h.parentWindow||s,p])}for(h=0;hd&&k.push({elem:this,matches:b.slice(d)});for(h=0;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e="sizcache"+(Math.random()+"").replace(".",""),f=0,g=Object.prototype.toString,k=!1,h=!0,l=/\\/g, +p=/\r\n/g,q=/\W/;[0,0].sort(function(){h=!1;return 0});var n=function(a,b,c,e){c=c||[];var f=b=b||r;if(1!==b.nodeType&&9!==b.nodeType)return[];if(!a||"string"!==typeof a)return c;var h,k,l,p,q,m=!0,s=n.isXML(b),u=[],w=a;do if(d.exec(""),h=d.exec(w))if(w=h[3],u.push(h[1]),h[2]){p=h[3];break}while(h);if(1":function(a,b){var c,d="string"===typeof b,e=0,f=a.length;if(d&&!q.test(b))for(b=b.toLowerCase();ec[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a, +b,c,d){var e=b[1],f=t.filters[e];if(f)return f(a,c,b,d);if("contains"===e)return 0<=(a.textContent||a.innerText||s([a])||"").indexOf(b[3]);if("not"===e){b=b[3];c=0;for(d=b.length;c";c.insertBefore(a,c.firstChild);r.getElementById(b)&&(t.find.ID=function(a,b,c){if("undefined"!==typeof b.getElementById&& +!c)return(b=b.getElementById(a[1]))?b.id===a[1]||"undefined"!==typeof b.getAttributeNode&&b.getAttributeNode("id").nodeValue===a[1]?[b]:m:[]},t.filter.ID=function(a,b){var c="undefined"!==typeof a.getAttributeNode&&a.getAttributeNode("id");return 1===a.nodeType&&c&&c.nodeValue===b});c.removeChild(a);c=a=null})();(function(){var a=r.createElement("div");a.appendChild(r.createComment(""));0

";if(!(b.querySelectorAll&&0===b.querySelectorAll(".TEST").length)){n=function(b,c,d,e){c=c||r;if(!e&&!n.isXML(c)){var f=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b); +if(f&&(1===c.nodeType||9===c.nodeType)){if(f[1])return A(c.getElementsByTagName(b),d);if(f[2]&&t.find.CLASS&&c.getElementsByClassName)return A(c.getElementsByClassName(f[2]),d)}if(9===c.nodeType){if("body"===b&&c.body)return A([c.body],d);if(f&&f[3]){var g=c.getElementById(f[3]);if(g&&g.parentNode){if(g.id===f[3])return A([g],d)}else return A([],d)}try{return A(c.querySelectorAll(b),d)}catch(k){}}else if(1===c.nodeType&&"object"!==c.nodeName.toLowerCase()){var f=c,h=(g=c.getAttribute("id"))||"__sizzle__", +l=c.parentNode,p=/^\s*[+~]/.test(b);g?h=h.replace(/'/g,"\\$&"):c.setAttribute("id",h);p&&l&&(c=c.parentNode);try{if(!p||l)return A(c.querySelectorAll("[id='"+h+"'] "+b),d)}catch(q){}finally{g||f.removeAttribute("id")}}}return a(b,c,d,e)};for(var c in a)n[c]=a[c];b=null}}();(function(){var a=r.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var c=!b.call(r.createElement("div"),"div"),d=!1;try{b.call(r.documentElement,"[test!='']:sizzle")}catch(e){d= +!0}n.matchesSelector=function(a,e){e=e.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!n.isXML(a))try{if(d||!t.match.PSEUDO.test(e)&&!/!=/.test(e)){var f=b.call(a,e);if(f||!c||a.document&&11!==a.document.nodeType)return f}}catch(g){}return 0
";a.getElementsByClassName&&0!==a.getElementsByClassName("e").length&&(a.lastChild.className.baseVal="e",1!==a.getElementsByClassName("e").length&& +(t.order.splice(1,0,"CLASS"),t.find.CLASS=function(a,b,c){if("undefined"!==typeof b.getElementsByClassName&&!c)return b.getElementsByClassName(a[1])},a=null))})();n.contains=r.documentElement.contains?function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:r.documentElement.compareDocumentPosition?function(a,b){return!!(a.compareDocumentPosition(b)&16)}:function(){return!1};n.isXML=function(a){return(a=(a?a.ownerDocument||a:0).documentElement)?"HTML"!==a.nodeName:!1};var B=function(a,b,c){var d, +e=[],f="";for(b=b.nodeType?[b]:b;d=t.match.PSEUDO.exec(a);)f+=d[0],a=a.replace(t.match.PSEUDO,"");a=t.relative[a]?a+"*":a;d=0;for(var g=b.length;d]*)\/>/ig,Pa=/<([\w:]+)/, +Db=/]","i"),Ra=/checked\s*(?:[^=]|=\s*.checked.)/i,Sa=/\/(java|ecma)script/i,Hb=/^\s*",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"], +area:[1,"",""],_default:[0,"",""]},pa=y(r);D.optgroup=D.option;D.tbody=D.tfoot=D.colgroup=D.caption=D.thead;D.th=D.td;c.support.htmlSerialize||(D._default=[1,"div
","
"]);c.fn.extend({text:function(a){return c.access(this,function(a){return a===m?c.text(this):this.empty().append((this[0]&&this[0].ownerDocument||r).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapAll(a.call(this,b))});if(this[0]){var b= +c(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var a=this;a.firstChild&&1===a.firstChild.nodeType;)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return c.isFunction(a)?this.each(function(b){c(this).wrapInner(a.call(this,b))}):this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){var b=c.isFunction(a);return this.each(function(d){c(this).wrapAll(b?a.call(this,d): +a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){1===this.nodeType&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){1===this.nodeType&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a= +c.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c.clean(arguments));return a}},remove:function(a,b){for(var d=0,e;null!=(e=this[d]);d++)if(!a||c.filter(a,[e]).length)!b&&1===e.nodeType&&(c.cleanData(e.getElementsByTagName("*")), +c.cleanData([e])),e.parentNode&&e.parentNode.removeChild(e);return this},empty:function(){for(var a=0,b;null!=(b=this[a]);a++)for(1===b.nodeType&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);return this},clone:function(a,b){a=null==a?!1:a;b=null==b?a:b;return this.map(function(){return c.clone(this,a,b)})},html:function(a){return c.access(this,function(a){var d=this[0]||{},e=0,f=this.length;if(a===m)return 1===d.nodeType?d.innerHTML.replace(Cb,""):null;if("string"=== +typeof a&&!Fb.test(a)&&(c.support.leadingWhitespace||!oa.test(a))&&!D[(Pa.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Oa,"<$1>");try{for(;e