mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-03-28 04:11:34 +01:00
insights (wip)
This commit is contained in:
@@ -3,5 +3,6 @@ from django.urls import path
|
|||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
path("insights/", views.index, name="insights_index"),
|
||||||
path("insights/sankey/", views.sankey, name="sankey"),
|
path("insights/sankey/", views.sankey, name="sankey"),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ from apps.transactions.models import Transaction
|
|||||||
from apps.insights.utils.sankey import generate_sankey_data
|
from apps.insights.utils.sankey import generate_sankey_data
|
||||||
|
|
||||||
|
|
||||||
|
def index(request):
|
||||||
|
return render(request, "insights/pages/index.html")
|
||||||
|
|
||||||
|
|
||||||
def sankey(request):
|
def sankey(request):
|
||||||
# Get filtered transactions
|
# Get filtered transactions
|
||||||
transactions = Transaction.objects.filter(date__year=2025)
|
transactions = Transaction.objects.filter(date__year=2025)
|
||||||
|
|||||||
@@ -5,69 +5,27 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
function setupSankeyChart(data, chartId = 'sankeyChart') {
|
function setupSankeyChart(data, chartId = 'sankeyChart') {
|
||||||
// Color generation function
|
|
||||||
function generateColors(nodes) {
|
|
||||||
const colorMap = {};
|
|
||||||
const incomeColor = '#4CAF50'; // Green
|
|
||||||
const expenseColor = '#F44336'; // Red
|
|
||||||
const savingsColor = '#4CAF50'; // Green (same as income)
|
|
||||||
const accountColor = '#2196F3'; // Blue
|
|
||||||
|
|
||||||
nodes.forEach((node) => {
|
|
||||||
if (node.name.includes('(')) {
|
|
||||||
const [category, currency] = node.name.split(' (');
|
|
||||||
if (category.toLowerCase() === 'savings') {
|
|
||||||
colorMap[node.name] = savingsColor;
|
|
||||||
} else if (data.flows.some(flow => flow.from_node === node.name)) {
|
|
||||||
colorMap[node.name] = incomeColor;
|
|
||||||
} else if (data.flows.some(flow => flow.to_node === node.name)) {
|
|
||||||
colorMap[node.name] = expenseColor;
|
|
||||||
} else {
|
|
||||||
colorMap[node.name] = accountColor;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
colorMap[node.name] = accountColor;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return colorMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format currency value
|
// Format currency value
|
||||||
function formatCurrency(value, currency) {
|
function formatCurrency(value, currency) {
|
||||||
return new Intl.NumberFormat('pt-BR', {
|
return new Intl.NumberFormat(undefined, {
|
||||||
minimumFractionDigits: currency.decimal_places,
|
minimumFractionDigits: currency.decimal_places,
|
||||||
maximumFractionDigits: currency.decimal_places
|
maximumFractionDigits: currency.decimal_places
|
||||||
}).format(value);
|
}).format(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate colors for nodes
|
|
||||||
const colorMap = generateColors(data.nodes);
|
|
||||||
|
|
||||||
// Create a mapping of node names to indices
|
|
||||||
const nodeIndices = {};
|
const nodeIndices = {};
|
||||||
const nodeSizes = {};
|
|
||||||
data.nodes.forEach((node, index) => {
|
data.nodes.forEach((node, index) => {
|
||||||
nodeIndices[node.name] = index;
|
nodeIndices[node.name] = index;
|
||||||
nodeSizes[node.name] = node.size;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(data.flows.map(flow => ({
|
|
||||||
from: nodeIndices[flow.from_node],
|
|
||||||
to: nodeIndices[flow.to_node],
|
|
||||||
flow: flow.flow
|
|
||||||
})),);
|
|
||||||
console.log(nodeSizes)
|
|
||||||
// Format data for Chart.js
|
// Format data for Chart.js
|
||||||
const chartData = {
|
const chartData = {
|
||||||
datasets: [{
|
datasets: [{
|
||||||
data: data.flows.map(flow => ({
|
data: data.flows.map(flow => ({
|
||||||
from: nodeIndices[flow.from_node],
|
from: flow.from_node,
|
||||||
to: nodeIndices[flow.to_node],
|
to: flow.to_node,
|
||||||
flow: flow.flow
|
flow: flow.flow
|
||||||
})),
|
})),
|
||||||
colorFrom: (c) => colorMap[data.nodes[c.dataset.data[c.dataIndex].from].name],
|
|
||||||
colorTo: (c) => colorMap[data.nodes[c.dataset.data[c.dataIndex].to].name],
|
|
||||||
colorMode: 'to',
|
colorMode: 'to',
|
||||||
labels: data.nodes.map(node => node.name),
|
labels: data.nodes.map(node => node.name),
|
||||||
size: 'max',
|
size: 'max',
|
||||||
|
|||||||
23
app/templates/insights/pages/index.html
Normal file
23
app/templates/insights/pages/index.html
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{% extends "layouts/base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row mx-3 mt-3">
|
||||||
|
<div class="card shadow w-auto">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="btn-group" role="group" aria-label="Basic radio toggle button group" _="on change log 'oi'">
|
||||||
|
<input type="radio" class="btn-check" name="btnradio" id="btnradio1" autocomplete="off" checked>
|
||||||
|
<label class="btn btn-sm btn-outline-primary" for="btnradio1">{% translate 'Month' %}</label>
|
||||||
|
|
||||||
|
<input type="radio" class="btn-check" name="btnradio" id="btnradio2" autocomplete="off">
|
||||||
|
<label class="btn btn-sm btn-outline-primary" for="btnradio2">{% translate 'Year' %}</label>
|
||||||
|
|
||||||
|
<input type="radio" class="btn-check" name="btnradio" id="btnradio3" autocomplete="off">
|
||||||
|
<label class="btn btn-sm btn-outline-primary" for="btnradio3">{% translate 'Range' %}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user