Added category, added pie chart on categories, removed buggy ba-resize plugin.

This commit is contained in:
Alexis Lahouze 2013-01-12 22:11:48 +01:00
parent 6f36d7970f
commit 868cf3138a
5 changed files with 155 additions and 77 deletions

View File

@ -17,7 +17,7 @@ class EntryAPI extends RestAPI {
$account=$this->_request['account']; $account=$this->_request['account'];
$statement=$connection->prepare("select id, value_date, operation_date, label, value, account_id, sold, pointedSold from (select *, sum(value) over(order by value_date, operation_date, label desc, value desc) as sold, sum(value) over(partition by operation_date is not null order by value_date, operation_date, label desc, value desc) as pointedSold from entry where account_id=:account order by value_date desc, operation_date desc, label, value) as e where date_trunc('month', e.value_date) = :day "); $statement=$connection->prepare("select id, value_date, operation_date, label, value, account_id, sold, pointedsold, category from (select *, sum(value) over(order by value_date, operation_date, label desc, value desc) as sold, sum(value) over(partition by operation_date is not null order by value_date, operation_date, label desc, value desc) as pointedSold from entry where account_id=:account order by value_date desc, operation_date desc, label, value) as e where date_trunc('month', e.value_date) = :day ");
$statement->bindParam("day", $day); $statement->bindParam("day", $day);
$statement->bindParam("account", $account); $statement->bindParam("account", $account);
@ -36,9 +36,9 @@ class EntryAPI extends RestAPI {
$connection=$this->get_db_connection(); $connection=$this->get_db_connection();
if($entry['id'] != null) { if($entry['id'] != null) {
$statement=$connection->prepare("update entry set value_date=:value_date, operation_date=:operation_date, label=:label, value=:value, account_id=:account where id=:id"); $statement=$connection->prepare("update entry set value_date=:value_date, operation_date=:operation_date, label=:label, value=:value, account_id=:account, category=:category where id=:id");
} else { } else {
$statement=$connection->prepare("insert into entry (value_date, operation_date, label, value, account_id) values (:value_date, :operation_date, :label, :value, :account)"); $statement=$connection->prepare("insert into entry (value_date, operation_date, label, value, account_id) values (:value_date, :operation_date, :label, :value, :account, :category)");
} }
$statement->bindParam("value_date", $entry['value_date']); $statement->bindParam("value_date", $entry['value_date']);
@ -47,6 +47,7 @@ class EntryAPI extends RestAPI {
$statement->bindParam("value", $entry['value']); $statement->bindParam("value", $entry['value']);
$statement->bindParam("account", $entry['account']); $statement->bindParam("account", $entry['account']);
$statement->bindParam("id", $entry['id']); $statement->bindParam("id", $entry['id']);
$statement->bindParam("category", $entry['category']);
$return=$statement->execute(); $return=$statement->execute();

View File

@ -33,20 +33,28 @@
</div> </div>
</div> </div>
<div class="content" style="margin-top: 116px; margin-bottom: 41px"> <div class="row" style="margin-top: 116px"></div>
<div class="container-fluid"> <div class="row">
<div class="span8"> <div class="span8">
<div id="chart-placeholder" data-bind="chart: $root.entriesChart"></div> <div id="entries-chart-placeholder" data-bind="chart: $root.entriesChart"></div>
</div> </div>
<div class="span4"></div> <div class="span4">
<div id="categories-chart-placeholder" data-bind="pieChart: $root.categoriesChart"></div>
</div>
</div> </div>
<div class="row">
<div id="message-placeholder"></div> <div id="message-placeholder"></div>
</div>
<div class="row">
<a class="btn btn-primary" data-bind="click: $root.add" href="#" title="Add entry"><i class="icon-plus"></i>&nbsp;Ajouter une entr&eacute;e</a> <a class="btn btn-primary" data-bind="click: $root.add" href="#" title="Add entry"><i class="icon-plus"></i>&nbsp;Ajouter une entr&eacute;e</a>
</div>
<div class="row">
<table class="table table-striped table-condensed table-hover"> <table class="table table-striped table-condensed table-hover">
<thead> <thead>
<tr> <tr>
@ -56,6 +64,7 @@
<th style="width: 50px">Montant</th> <th style="width: 50px">Montant</th>
<th style="width: 50px">Solde</th> <th style="width: 50px">Solde</th>
<th style="width: 50px">Solde point&eacute;</th> <th style="width: 50px">Solde point&eacute;</th>
<th style="width: 100px">Cat&eacute;gorie</th>
<th style="width: 60px">Actions</th> <th style="width: 60px">Actions</th>
</tr> </tr>
</thead> </thead>
@ -64,6 +73,8 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<div style="margin-bottom: 21px"></div>
</div> </div>
</div> </div>
</div> </div>
@ -89,6 +100,7 @@
<td data-bind="text: value, css: {'text-error': value() < 0 }"></td> <td data-bind="text: value, css: {'text-error': value() < 0 }"></td>
<td data-bind="text: sold, css: {'text-error': sold() < 0 }"></td> <td data-bind="text: sold, css: {'text-error': sold() < 0 }"></td>
<td data-bind="text: pointedSold, css: {'text-error': pointedSold() < 0 }"></td> <td data-bind="text: pointedSold, css: {'text-error': pointedSold() < 0 }"></td>
<td data-bind="text: category"></td>
<td class="buttons"> <td class="buttons">
<a class="btn btn-mini" data-bind="click: $root.edit" href="#" title="edit"><i class="icon-edit"></i></a> <a class="btn btn-mini" data-bind="click: $root.edit" href="#" title="edit"><i class="icon-edit"></i></a>
<a class="btn btn-mini" data-bind="click: $root.remove" href="#" title="remove"><i class="icon-trash"></i></a> <a class="btn btn-mini" data-bind="click: $root.remove" href="#" title="remove"><i class="icon-trash"></i></a>
@ -104,6 +116,7 @@
<td><input type="text" class="input-mini" data-bind="value: value"/></td> <td><input type="text" class="input-mini" data-bind="value: value"/></td>
<td data-bind="text: sold"></td> <td data-bind="text: sold"></td>
<td data-bind="text: pointedSold"></td> <td data-bind="text: pointedSold"></td>
<td><input type="text" class="input-small" data-bind="value: category"/></td>
<td class="buttons"> <td class="buttons">
<a class="btn btn-mini btn-success" data-bind="click: $root.save" href="#" title="save"><i class="icon-ok"></i></a> <a class="btn btn-mini btn-success" data-bind="click: $root.save" href="#" title="save"><i class="icon-ok"></i></a>
<a class="btn btn-mini" data-bind="click: $root.cancel" href="#" title="cancel"><i class="icon-ban-circle"></i></a> <a class="btn btn-mini" data-bind="click: $root.cancel" href="#" title="cancel"><i class="icon-ban-circle"></i></a>
@ -112,7 +125,6 @@
</script> </script>
<script type="text/javascript" src="jquery/jquery.js"></script> <script type="text/javascript" src="jquery/jquery.js"></script>
<script type="text/javascript" src="jquery/jquery.ba-resize.min.js"></script>
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script> <script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript" src="datepicker/js/bootstrap-datepicker.js"></script> <script type="text/javascript" src="datepicker/js/bootstrap-datepicker.js"></script>
<script type="text/javascript" src="knockout/knockout.js"></script> <script type="text/javascript" src="knockout/knockout.js"></script>
@ -122,6 +134,7 @@
<script type="text/javascript" src="jqplot/plugins/jqplot.canvasTextRenderer.min.js"></script> <script type="text/javascript" src="jqplot/plugins/jqplot.canvasTextRenderer.min.js"></script>
<script type="text/javascript" src="jqplot/plugins/jqplot.canvasAxisTickRenderer.min.js"></script> <script type="text/javascript" src="jqplot/plugins/jqplot.canvasAxisTickRenderer.min.js"></script>
<script type="text/javascript" src="jqplot/plugins/jqplot.ohlcRenderer.min.js"></script> <script type="text/javascript" src="jqplot/plugins/jqplot.ohlcRenderer.min.js"></script>
<script type="text/javascript" src="jqplot/plugins/jqplot.pieRenderer.min.js"></script>
<script type="text/javascript" src="jqplot/plugins/jqplot.highlighter.min.js"></script> <script type="text/javascript" src="jqplot/plugins/jqplot.highlighter.min.js"></script>
<script type="text/javascript" src="js/entries.js"></script> <script type="text/javascript" src="js/entries.js"></script>

View File

@ -1,9 +0,0 @@
/*
* jQuery resize event - v1.1 - 3/14/2010
* http://benalman.com/projects/jquery-resize-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function($,h,c){var a=$([]),e=$.resize=$.extend($.resize,{}),i,k="setTimeout",j="resize",d=j+"-special-event",b="delay",f="throttleWindow";e[b]=250;e[f]=true;$.event.special[j]={setup:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.add(l);$.data(this,d,{w:l.width(),h:l.height()});if(a.length===1){g()}},teardown:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.not(l);l.removeData(d);if(!a.length){clearTimeout(i)}},add:function(l){if(!e[f]&&this[k]){return false}var n;function m(s,o,p){var q=$(this),r=$.data(this,d);r.w=o!==c?o:q.width();r.h=p!==c?p:q.height();n.apply(this,arguments)}if($.isFunction(l)){n=l;return m}else{n=l.handler;l.handler=m}}};function g(){i=h[k](function(){a.each(function(){var n=$(this),m=n.width(),l=n.height(),o=$.data(this,d);if(m!==o.w||l!==o.h){n.trigger(j,[o.w=m,o.h=l])}});g()},e[b])}})(jQuery,this);

View File

@ -7,6 +7,7 @@ function entry(data){
var account = ko.utils.unwrapObservable(data.account); var account = ko.utils.unwrapObservable(data.account);
var sold = ko.utils.unwrapObservable(data.sold); var sold = ko.utils.unwrapObservable(data.sold);
var pointedSold = ko.utils.unwrapObservable(data.pointedSold); var pointedSold = ko.utils.unwrapObservable(data.pointedSold);
var category = ko.utils.unwrapObservable(data.category);
this.id=ko.observable(id ? id : null); this.id=ko.observable(id ? id : null);
this.value_date=ko.observable(value_date ? value_date : null); this.value_date=ko.observable(value_date ? value_date : null);
@ -16,6 +17,7 @@ function entry(data){
this.account=ko.observable(account ? account : null); this.account=ko.observable(account ? account : null);
this.sold=ko.observable(sold ? sold : null); this.sold=ko.observable(sold ? sold : null);
this.pointedSold=ko.observable(pointedSold ? pointedSold : null); this.pointedSold=ko.observable(pointedSold ? pointedSold : null);
this.category=ko.observable(category ? category : null);
} }
function message(alertType, title, message) { function message(alertType, title, message) {
@ -34,17 +36,41 @@ var ListViewModel = function() {
self.months = ko.observableArray(); self.months = ko.observableArray();
self.month = ko.observable(); self.month = ko.observable();
self.pointedSold = ko.observable();
self.futureSold = ko.observable();
self.currentSold = ko.observable();
self.selectedItem = ko.observable(); self.selectedItem = ko.observable();
self.savedItem = ko.observable(); self.savedItem = ko.observable();
self.itemToRemove = ko.observable(); self.itemToRemove = ko.observable();
self.chart = null; self.chart = null;
self.categoriesChart = ko.computed(function() {
var entries=ko.utils.unwrapObservable(self.entries);
var chartValues = [];
var chartValuesTmp = {};
$.each(entries, function(index, entry) {
var category = entry.category();
var value = entry.value() ? Number(entry.value()) : null;
if(category && value) {
var oldValue = 0.0;
if(chartValuesTmp[category]) {
oldValue = chartValuesTmp[category];
}
chartValuesTmp[category] = oldValue - value
}
});
$.each(chartValuesTmp, function(key, value) {
chartValues.push([key, value]);
});
return chartValues;
});
self.entriesChart = ko.computed(function() { self.entriesChart = ko.computed(function() {
var entries = self.entries().slice().reverse(); var entries = ko.utils.unwrapObservable(self.entries).slice().reverse();
var chartValues = []; var chartValues = [];
var chartValuesTmp = {}; var chartValuesTmp = {};
@ -113,7 +139,8 @@ var ListViewModel = function() {
value: element.value, value: element.value,
account: element.account_id, account: element.account_id,
sold: element.sold, sold: element.sold,
pointedSold: element.operation_date ? element.pointedsold : '' pointedSold: element.operation_date ? element.pointedsold : '',
category: element.category
})); }));
}); });
@ -357,29 +384,25 @@ ko.bindingHandlers.dateValue = {
}; };
drawChart = function(entries, element) { drawChart = function(entries, element) {
if(entries && entries.length > 0) { // clear previous chart
var firstDate, lastDate; $(element).html("");
var chartValues = [[], []];
if(entries && entries.length > 0) {
var day = 24 * 60 * 60 * 1000; var day = 24 * 60 * 60 * 1000;
lastDate = new Date(Date.parse(entries[entries.length -1][0]).valueOf() + 1 * day); var lastDate = new Date(Date.parse(entries[entries.length -1][0]).valueOf() + 1 * day);
firstDate = new Date(Date.parse(entries[0][0]).valueOf() - 1 * day); var firstDate = new Date(Date.parse(entries[0][0]).valueOf() - 1 * day);
var chartValues = [[], []];
// For the '0' line. // For the '0' line.
chartValues[0].push( chartValues[0].push(
[new Date(firstDate.valueOf()).toString(), 0], [new Date(firstDate.valueOf()).toString(), 0],
[new Date(lastDate.valueOf()).toString(), 0]); [new Date(lastDate.valueOf()).toString(), 0]);
// Push last entry
chartValues[1] = entries; chartValues[1] = entries;
// clear previous chart
$(element).html("");
$(element).resize();
// plot chart // plot chart
jqplot = $.jqplot(element.id, chartValues, { window.chart = $.jqplot(element.id, chartValues, {
axes:{ axes:{
xaxis:{ xaxis:{
renderer:$.jqplot.DateAxisRenderer, renderer:$.jqplot.DateAxisRenderer,
@ -387,12 +410,10 @@ drawChart = function(entries, element) {
}, },
yaxis: { yaxis: {
autoscale: true, autoscale: true,
//tickOptions: {formatString: "%f"}
} }
}, },
highlighter: { highlighter: {
show:true, show:true,
//tooltipAxes: 'y',
yvalues: 4, yvalues: 4,
formatString:'<table class="jqplot-highlighter"><tr><td>date:</td><td>%s</td></tr><tr><td>open:</td><td>%s</td></tr><tr><td>hi:</td><td>%s</td></tr><tr><td>low:</td><td>%s</td></tr><tr><td>close:</td><td>%s</td></tr></table>' formatString:'<table class="jqplot-highlighter"><tr><td>date:</td><td>%s</td></tr><tr><td>open:</td><td>%s</td></tr><tr><td>hi:</td><td>%s</td></tr><tr><td>low:</td><td>%s</td></tr><tr><td>close:</td><td>%s</td></tr></table>'
}, },
@ -407,12 +428,38 @@ drawChart = function(entries, element) {
color: "blue", color: "blue",
lineWidth: 3, lineWidth: 3,
rendererOptions:{ rendererOptions:{
//candleStick: true
}}], }}],
});//*/ });
}
};
$(element).resize(function() { drawPieChart = function(entries, element) {
jqplot.replot({resetAxes: true}); // clear previous chart
$(element).html("");
if(entries && entries.length > 0) {
var chartValues = [[]];
chartValues[0] = entries;
// plot chart
window.pieChart = $.jqplot(element.id, chartValues, {
seriesDefaults: {
renderer: $.jqplot.PieRenderer,
rendererOptions: {
showDataLabels: true
}
},
legend: {
show: true,
location: 'e'
},
highlighter: {
show: true,
formatString:'%s: %s',
tooltipLocation:'sw',
useAxesFormatters:false
}
}); });
} }
}; };
@ -432,10 +479,35 @@ ko.bindingHandlers.chart = {
} }
}; };
ko.bindingHandlers.pieChart = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
// empty - left as placeholder if needed later
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var unwrap = ko.utils.unwrapObservable;
var dataSource = valueAccessor();
//var entries = dataSource ? unwrap(dataSource) : null;
var entries = dataSource ? unwrap(dataSource) : null;
drawPieChart(entries, element);
}
};
$(document).ajaxError(function(event, xhr, settings) { $(document).ajaxError(function(event, xhr, settings) {
message("error", "Error.", xhr.statusText); message("error", "Error.", xhr.statusText);
}); });
$(window).resize(function() {
if(window.chart) {
window.chart.replot({resetAxes: true});
}
if(window.chart) {
window.pieChart.replot({resetAxes: true});
}
});
var viewModel = new ListViewModel(); var viewModel = new ListViewModel();
ko.applyBindings(viewModel); ko.applyBindings(viewModel);

View File

@ -12,7 +12,8 @@ CREATE TABLE "entry" (
"label" VARCHAR(500) NOT NULL, "label" VARCHAR(500) NOT NULL,
"comment" VARCHAR(500), "comment" VARCHAR(500),
"value" NUMERIC(15,4) NOT NULL, "value" NUMERIC(15,4) NOT NULL,
"account_id" INTEGER NOT NULL REFERENCES "account"("id") "account_id" INTEGER NOT NULL REFERENCES "account"("id"),
"category" VARCHAR(100),
); );
CREATE INDEX on entry(value_date); CREATE INDEX on entry(value_date);