about summary refs log tree commit diff
path: root/users/wpcarro/ynabsql/dataviz/index.js
diff options
context:
space:
mode:
authorWilliam Carroll <wpcarro@gmail.com>2023-01-06T18·16-0800
committerwpcarro <wpcarro@gmail.com>2023-01-08T22·49+0000
commit899828adeb68bad5bf528bc3319ab2a66a2e5553 (patch)
tree2d83ab43f48a31f7a6e6a38e36f92db59d71e859 /users/wpcarro/ynabsql/dataviz/index.js
parent1d59d3ba8f0f77d33e246984447c0ae5b3889316 (diff)
feat(wpcarro/ynab): Proof-of-concept viz for finances r/5630
After experimenting with existing "data engineering solutions" like
datasette, periscope, I think rolling my own dataviz for this project might be
easiest (surprisingly).

**Wish List:**
- Benthos job to dump my financial transactions into a SQL table.
- Scatter plot of expenses (or just transactions generally).
  - Support filtering the data using "Simple Select" query language.
- Stacked histogram of income/expenses with a line overlaying the "idealized"
  savings.

Change-Id: Iec2948641dba8c4c6d5ad19a0e1ea142b81198af
Reviewed-on: https://cl.tvl.fyi/c/depot/+/7784
Tested-by: BuildkiteCI
Reviewed-by: wpcarro <wpcarro@gmail.com>
Diffstat (limited to 'users/wpcarro/ynabsql/dataviz/index.js')
-rw-r--r--users/wpcarro/ynabsql/dataviz/index.js72
1 files changed, 72 insertions, 0 deletions
diff --git a/users/wpcarro/ynabsql/dataviz/index.js b/users/wpcarro/ynabsql/dataviz/index.js
new file mode 100644
index 000000000000..cc58591d7ff8
--- /dev/null
+++ b/users/wpcarro/ynabsql/dataviz/index.js
@@ -0,0 +1,72 @@
+const colors = {
+  red: 'rgb(255, 99, 132)',
+  orange: 'rgb(255, 159, 64)',
+  yellow: 'rgb(255, 205, 86)',
+  green: 'rgb(75, 192, 192)',
+  blue: 'rgb(54, 162, 235)',
+  purple: 'rgb(153, 102, 255)',
+  grey: 'rgb(201, 203, 207)'
+};
+
+function randomExpense() {
+  // 10/1000     expenses are O(1,000)
+  // 100/2000    expenses are O(100)
+  // 1,000/2000  expenses are O(10)
+  // 10,000/2000 expenses are O(1)
+  const r = Math.random();
+
+  if (r <= 0.02) {
+    return Math.floor(Math.random() * 5000);
+  } else if (r <= 0.1) {
+    return Math.floor(Math.random() * 1000);
+  } else if (r <= 0.5) {
+    return Math.floor(Math.random() * 100);
+  } else {
+    return Math.floor(Math.random() * 10);
+  }
+}
+
+// Browser starts to choke around 10,000 data points.
+function generateData() {
+  return Array(2000).fill(0).map(x => ({
+    // select a random day [0, 365]
+    x: Math.floor(Math.random() * 365),
+    // select a random USD amount in the range [1, 5,000]
+    y: randomExpense(),
+    // TODO(wpcarro): Attach transaction to `metadata` key.
+    metadata: { foo: 'bar' },
+  }));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Main
+////////////////////////////////////////////////////////////////////////////////
+
+const mount = document.getElementById('mount');
+
+new Chart(mount, {
+  type: 'scatter',
+  data: {
+    datasets: [
+      {
+        label: 'Expenses',
+        data: generateData(),
+        backgroundColor: colors.red,
+      }
+    ],
+  },
+  options: {
+    plugins: {
+      tooltip: {
+        callbacks: {
+          title: function (x) {
+            return `$${x[0].raw.y}`;
+          },
+          label: function (x) {
+            return JSON.stringify(x.raw.metadata);
+          },
+        },
+      },
+    },
+  },
+})