Plot, also caching

master
q3k 2015-01-03 15:10:44 +01:00
parent 41e53c2178
commit 98f8dbf90f
6 changed files with 129 additions and 93 deletions

View File

@ -1,3 +1,4 @@
import memcache
import requests
from flask import Flask
@ -9,6 +10,8 @@ app.config.from_object("config.CurrentConfig")
db = SQLAlchemy(app)
login_manager = LoginManager()
login_manager.init_app(app)
mc = memcache.Client(app.config['MEMCACHE_SERVERS'], debug=0)
import webapp.models

View File

@ -6,13 +6,14 @@ from sqlalchemy import and_
from flask import request, abort, Response
from webapp import models, app
from webapp import models, app, mc
class APIError(Exception):
def __init__(self, message, code=500):
self.message = message
self.code = code
def _public_api_method(path):
"""A decorator that adds a public, GET based method at /api/<path>.json.
@ -29,6 +30,7 @@ def _public_api_method(path):
code = e.code
status = "error"
except Exception as e:
raise
content = "Internal server error."
code = 500
status = "error"
@ -130,6 +132,11 @@ def api_member():
return response
def _stats_for_month(year, month):
cache_key = 'kasownik-stats_for_month-{}-{}'.format(year, month)
cache_data = mc.get(cache_key)
if cache_data:
cache_data = json.loads(cache_data)
return cache_data[0], cache_data[1]
# TODO: export this to the config
money_required = 4800
money_paid = 0
@ -139,7 +146,7 @@ def _stats_for_month(year, month):
amount_all = mt.transfer.amount
amount = amount_all / len(mt.transfer.member_transfers)
money_paid += amount
mc.set(cache_key, json.dumps([money_required, money_paid/100]))
return money_required, money_paid/100
@_public_api_method("month/<year>/<month>")
@ -156,6 +163,10 @@ def api_manamana(year=None, month=None):
@_public_api_method("months_due/<membername>")
def api_months_due(membername):
cache_key = 'kasownik-months_due-{}'.format(membername)
cache_data = mc.get(cache_key)
if cache_data:
return cache_data
member = models.Member.query.filter_by(username=membername).first()
if not member:
raise APIError("No such member.", 404)
@ -168,16 +179,23 @@ def api_months_due(membername):
#now = datetime.datetime.now()
#then_timestamp = year * 12 + (month-1)
#now_timestamp = now.year * 12 + (now.month-1)
mc.set(cache_key, due)
return due
@_public_api_method("cashflow/<int:year>/<int:month>")
def api_cashflow(year, month):
start = datetime.date(year=year, month=month, day=1)
month += 1
if month > 12:
month = 1
year += 1
end = datetime.date(year=year, month=month, day=1)
transfers = models.Transfer.query.filter(and_(models.Transfer.date >= start, models.Transfer.date < end)).all()
amount_in = sum(t.amount for t in transfers)
cache_key = 'kasownik-cashflow-{}-{}'.format(year, month)
cache_data = mc.get(cache_key)
if cache_data:
amount_in = cache_data
else:
start = datetime.date(year=year, month=month, day=1)
month += 1
if month > 12:
month = 1
year += 1
end = datetime.date(year=year, month=month, day=1)
transfers = models.Transfer.query.filter(and_(models.Transfer.date >= start, models.Transfer.date < end)).all()
amount_in = sum(t.amount for t in transfers)
mc.set(cache_key, amount_in)
return {"in": amount_in/100, "out": -1}

View File

@ -7,7 +7,20 @@ body {
}
.stats {
padding: 40px 15px;
}
.stats h1 {
text-align: center;
}
.stats h4 {
text-align: center;
}
#plot {
margin-left: 10%;
width: 80%;
}
#legend {

View File

@ -1,89 +1,92 @@
$(function() {
$(window).load(function() {
var required = [], paid = [], influx = [];
var today = new Date(),
year = 1900 + today.getYear(),
month = today.getMonth() + 1,
urlBase = 'https://kasownik.hackerspace.pl/api/',
urlBase = '/api/',
modified;
for(var i = 0; i < 28; ++i) {
var xhr = new XMLHttpRequest();
xhr.open("GET", urlBase + 'month/'+ year + '/' + month + '.json', false);
xhr.send();
var data = JSON.parse(xhr.response),
res = data.content,
date = new Date(year, month, 1);
modified = modified || data.modified;
required.unshift({ x: date.getTime() / 1000, y: res.required });
paid.unshift({ x: date.getTime() / 1000, y: res.paid });
// This is a hack so that the page loads before we try to load the plot
// (which seems to make chrome sloooow)
// Also I'm not a web developer.
setTimeout(function(){
for(var i = 0; i < 28; ++i) {
var xhr = new XMLHttpRequest();
xhr.open("GET", urlBase + 'month/'+ year + '/' + month + '.json', false);
xhr.send();
var data = JSON.parse(xhr.response),
res = data.content,
date = new Date(year, month, 1);
modified = modified || data.modified;
required.unshift({ x: date.getTime() / 1000, y: res.required });
paid.unshift({ x: date.getTime() / 1000, y: res.paid });
xhr = new XMLHttpRequest();
xhr.open("GET", urlBase + 'cashflow/'+ year + '/' + month + '.json', false);
xhr.send();
res = JSON.parse(xhr.response).content,
influx.unshift({ x: date.getTime() / 1000, y: res.in });
month -= 1;
if(month == 0) {
month = 12;
year -= 1;
xhr = new XMLHttpRequest();
xhr.open("GET", urlBase + 'cashflow/'+ year + '/' + month + '.json', false);
xhr.send();
res = JSON.parse(xhr.response).content,
influx.unshift({ x: date.getTime() / 1000, y: res.in });
month -= 1;
if(month == 0) {
month = 12;
year -= 1;
}
}
}
console.log(required, paid);
var lastmod = document.getElementById("lastmod");
lastmod.innerHTML = modified;
var lastmod = document.getElementById("lastmod");
lastmod.innerHTML = "Last Modified " + modified;
var palette = new Rickshaw.Color.Palette( { scheme: 'munin' } );
var graph = new Rickshaw.Graph({
element: document.getElementById("plot"),
width: 1300,
height: 600,
renderer: 'line',
series: [
{
color: palette.color(),
data: required,
name: 'Required',
},
{
color: palette.color(),
data: paid,
name: 'Paid',
},
{
color: palette.color(),
data: influx,
name: 'Cash Influx',
},
]
});
graph.render();
var palette = new Rickshaw.Color.Palette( { scheme: 'munin' } );
var graph = new Rickshaw.Graph({
element: document.getElementById("plot"),
width: $("#plot").width(),
height: $("#plot").width()*0.4,
renderer: 'line',
series: [
{
color: palette.color(),
data: required,
name: 'Required',
},
{
color: palette.color(),
data: paid,
name: 'Paid',
},
{
color: palette.color(),
data: influx,
name: 'Cash Influx',
},
]
});
graph.render();
var yAxis = new Rickshaw.Graph.Axis.Y({
graph: graph,
});
yAxis.render();
var yAxis = new Rickshaw.Graph.Axis.Y({
graph: graph,
});
yAxis.render();
var xAxis = new Rickshaw.Graph.Axis.Time({
graph: graph,
});
xAxis.render();
var xAxis = new Rickshaw.Graph.Axis.Time({
graph: graph,
});
xAxis.render();
var legend = new Rickshaw.Graph.Legend({
element: document.getElementById("legend"),
graph: graph,
});
var legend = new Rickshaw.Graph.Legend({
element: document.getElementById("legend"),
graph: graph,
});
var hoverDetail = new Rickshaw.Graph.HoverDetail( {
graph: graph,
xFormatter: function(x) {
var date = new Date(x * 1000);
return (1900 + date.getYear()) + '/' + date.getMonth();
}
});
var hoverDetail = new Rickshaw.Graph.HoverDetail( {
graph: graph,
xFormatter: function(x) {
var date = new Date(x * 1000);
return (1900 + date.getYear()) + '/' + date.getMonth();
}
});
}, 200);
});

View File

@ -16,6 +16,7 @@
<!-- Custom styles for this template -->
<link href="/static/css/main.css" rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
{% block extraheader %}
{% endblock %}
</head>
@ -72,7 +73,6 @@
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
{% block extrajs %}
{% endblock %}

View File

@ -3,17 +3,16 @@
{% block title %}Stats{% endblock %}
{% block extraheader %}
<link rel="stylesheet" href="/static/css/rickshaw.css">
{% endblock %}
{% block extrajs %}
<script src="/static/js/d3.min.js"></script>
<script src="/static/js/d3.layout.min.js"></script>
<script src="/static/js/rickshaw.min.js"></script>
<script src="/static/js/plot.js"></script>
{% endblock %}
{% block content %}
<div class="stats">
<div id="legend"></div>
<div id="plot"></div>
Last modified: <span id="lastmod">-</span>
</div>
<div class="container-fluid stats">
<h1>Payment Stats</h1>
<h4 id="lastmod">Loding...</h4>
<div id="legend"></div>
<div id="plot"></div>
</div>
{% endblock %}