Browse Source

Add repeated transactions

Add flot resize library
Update to more recent flot git repo
Closes #5
master
Chris Smith 13 years ago
parent
commit
0f78f8a3b0
4 changed files with 107 additions and 4 deletions
  1. 1
    1
      .gitmodules
  2. 87
    0
      analyser.js
  3. 1
    1
      externals/flot
  4. 18
    2
      index.html

+ 1
- 1
.gitmodules View File

@@ -1,6 +1,6 @@
1 1
 [submodule "externals/flot"]
2 2
 	path = externals/flot
3
-	url = https://github.com/FooBarWidget/flot.git
3
+	url = https://github.com/tback/flot.git 
4 4
 [submodule "externals/jquery.history"]
5 5
 	path = externals/jquery.history
6 6
 	url = https://github.com/tkyk/jquery-history-plugin.git

+ 87
- 0
analyser.js View File

@@ -188,6 +188,28 @@ Number.prototype.toCurrency = function() {
188 188
  return this.toFixed(2).replace(/([0-9])(?=([0-9]{3})+\.)/g, '$1,');
189 189
 };
190 190
 
191
+/**
192
+ * Computes the arithmatic mean, variance and deviation for the given array.
193
+ *
194
+ * @param a Array of numbers to be averaged
195
+ * @return A map containing the mean, variance and deviation
196
+ */
197
+function getAverage(a){
198
+ var r = {mean: 0, variance: 0, deviation: 0};
199
+ var length = a.length;
200
+
201
+ // Sum the array
202
+ for (var sum = 0, i = length; i--; sum += a[i]);
203
+
204
+ var mean = r.mean = sum / length
205
+
206
+ // Sum the squares of the differences from the mean
207
+ for (var i = length, sum = 0; i--; sum += Math.pow(a[i] - mean, 2));
208
+
209
+ r.deviation = Math.sqrt(r.variance = sum / length)
210
+ return r;
211
+}
212
+
191 213
 /**
192 214
  * Adds an 'alt' class to every other visible row in the specified table.
193 215
  *
@@ -309,6 +331,70 @@ function drawCategoryPieChart(included, incoming) {
309 331
  });
310 332
 }
311 333
 
334
+/**
335
+ * Calculates repeat transactions within the specified data.
336
+ *
337
+ * @param data The data to be analysed
338
+ */
339
+function calculateRepeatTransactions(data) {
340
+ $('#repeats').show();
341
+ $('#repeats tr.data').remove();
342
+ var table = $('#repeats table');
343
+
344
+ var descs = {};
345
+
346
+ $.each(data, function() {
347
+  if (!descs[this.Description]) { descs[this.Description] = []; }
348
+  descs[this.Description].push(this);
349
+ });
350
+
351
+ var monthTotal = 0;
352
+ $.each(descs, function(desc) {
353
+  // We only care if there are at least more than 2
354
+  if (this.length < 3) { return; }
355
+
356
+  var lastTime = 0;
357
+  var differences = [];
358
+  var amounts = [];
359
+
360
+  $.each(this, function() {
361
+   var time = new Date(this.Date.date).getTime();
362
+   lastTime > 0 && differences.push(time - lastTime);
363
+   lastTime = time;
364
+   amounts.push(this.Amount);
365
+  });
366
+
367
+  var average = getAverage(differences);
368
+  var averageAmount = getAverage(amounts);
369
+
370
+  // I may have just made this metric up. Sue me.
371
+  var stability = average.deviation / average.mean;
372
+  var periodInDays = average.mean / (1000 * 60 * 60 * 24);
373
+
374
+  if (stability < 0.5) {
375
+   // Seems quite reliable...
376
+   if ((periodInDays >= 5 && periodInDays <= 9) || (periodInDays >= 27 && periodInDays <= 32)) {
377
+    // Roughly weekly or monthly
378
+    var monthValue = (periodInDays <= 9 ? 4 : 1) * averageAmount.mean;
379
+
380
+    var tr = $('<tr class="data"/>').appendTo(table);
381
+    $('<td/>').text(desc).appendTo(tr);
382
+    $('<td/>').text(this[0].Category ? this[0].Category : 'Unsorted').appendTo(tr);
383
+    $('<td/>').text(periodInDays <= 9 ? 'Weekly' : 'Monthly').appendTo(tr);
384
+    $('<td class="amount"/>').text(averageAmount.mean.toCurrency()).appendTo(tr);
385
+    $('<td class="amount"/>').text(monthValue.toCurrency()).appendTo(tr);
386
+
387
+    monthTotal += monthValue;
388
+   }
389
+  }
390
+ });
391
+
392
+ colourTableRows(table);
393
+ var tr = $('<tr/>').addClass('data total').appendTo(table);
394
+ $('<th colspan="4" class="total">Total</th>').appendTo(tr);
395
+ $('<td class="amount"></td>').text(monthTotal.toCurrency()).appendTo(tr);
396
+}
397
+
312 398
 /**
313 399
  * Displays transactions and draws a category pie chart for the specified
314 400
  * date range. Note that dates have a granularity of a month.
@@ -385,6 +471,7 @@ function showSelectedMonths(start, end, incoming, outgoing, categoryFilter, expa
385 471
 
386 472
  colourTableRows(table);
387 473
  drawCategoryPieChart(included, incoming);
474
+ calculateRepeatTransactions(included);
388 475
 }
389 476
 
390 477
 $(function() {

+ 1
- 1
externals/flot

@@ -1 +1 @@
1
-Subproject commit 171538ec32e26a6167d8e43b9722ec2bf612115b
1
+Subproject commit 6328221ea9d507b2150e59f09d9eb9265e9dc127

+ 18
- 2
index.html View File

@@ -5,6 +5,7 @@
5 5
   <script src="externals/flot/jquery.js" type="text/javascript"></script>
6 6
   <script src="externals/flot/jquery.flot.js" type="text/javascript"></script>
7 7
   <script src="externals/flot/jquery.flot.pie.js" type="text/javascript"></script>
8
+  <script src="externals/flot/jquery.flot.resize.js" type="text/javascript"></script>
8 9
   <script src="externals/flot/jquery.flot.selection.js" type="text/javascript"></script>
9 10
   <script src="externals/flot/jquery.flot.stack.js" type="text/javascript"></script>
10 11
   <script src="externals/jquery.history/jquery.history.js" type="text/javascript"></script>
@@ -28,6 +29,7 @@
28 29
    #historytable tr.collapsed { opacity: 0.6; }
29 30
    table tr.total td, table tr.total th { background-color: #666; color: #fff; }
30 31
    .amount { text-align: right; }
32
+   .datatable { width: 100%; }
31 33
   </style>
32 34
  </head>
33 35
  <body>
@@ -39,13 +41,27 @@
39 41
   <div class="left">
40 42
    <h2>Transaction History</h2>
41 43
    <div id="history" style="width: 100%; height: 300px;" class="graph"></div>
42
-   <div id="historytable" style="display: none;"><h3>Transactions for ??</h3><table><tr><th>Date</th><th>Type</th><th>Category</th><th>Description</th><th>Amount</th></tr></table></div>
44
+   <div id="historytable" style="display: none;">
45
+    <h3>Transactions for ??</h3>
46
+    <table class="datatable"><tr><th>Date</th><th>Type</th><th>Category</th><th>Description</th><th>Amount</th></tr></table>
47
+   </div>
43 48
   </div>
44 49
   <div class="right">
45 50
    <h2>Categories</h2>
46 51
    <div id="expense" style="width: 100%; height: 300px;" class="graph"></div>
47 52
    <h2>Expense Categories</h2>
48
-   <div id="cathistory" style="width: 100%; height: 600px;" class="graph"></div>
53
+   <!-- The tick labels on the X-Axis extend a bit beyond the edge of the
54
+        graph, so make the graph 50px smaller so the page doesn't scroll -->
55
+   <div style="padding-right: 50px;">
56
+    <div id="cathistory" style="width: 100%; height: 600px;" class="graph"></div>
57
+   </div>
58
+   <div id="repeats" style="display: none;">
59
+    <h2>Repeat Transactions</h2>
60
+    <table class="datatable">
61
+     <tr><th rowspan="2">Transaction</th><th rowspan="2">Category</th><th rowspan="2">Frequency</th><th colspan="2">Average amount</th></tr>
62
+     <tr><th>Per trans</th><th>Per month</th></tr>
63
+    </table>
64
+   </div>
49 65
   </div>
50 66
  </body>
51 67
 </html>

Loading…
Cancel
Save