Late night fixing
This commit is contained in:
parent
f9967bad71
commit
d9951b69ca
10 changed files with 730 additions and 29 deletions
14
helper.py
14
helper.py
|
@ -6,7 +6,8 @@ import re
|
|||
from xdg import XDG_CONFIG_HOME, XDG_DATA_HOME
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from models import Category
|
||||
import models
|
||||
import locale
|
||||
|
||||
try:
|
||||
from yaml import CLoader as Loader
|
||||
|
@ -18,8 +19,13 @@ CONFIG_DIR = os.path.join(XDG_CONFIG_HOME, "schmeckels")
|
|||
DATA_DIR = os.path.join(XDG_DATA_HOME, "schmeckels")
|
||||
|
||||
|
||||
def format_amount(cents):
|
||||
amount = cents/100
|
||||
return f"{amount:,.2f}"
|
||||
|
||||
|
||||
def get_list_of_bookable_categories(session):
|
||||
categories = session.query(Category).all()
|
||||
categories = session.query(models.Category).all()
|
||||
return [c for c in categories if c.is_child()]
|
||||
|
||||
|
||||
|
@ -85,9 +91,9 @@ def get_rules(profile_name):
|
|||
|
||||
|
||||
def create_category(name, parent, profile, session):
|
||||
c = session.query(Category).filter(Category.name == name).first()
|
||||
c = session.query(models.Category).filter(models.Category.name == name).first()
|
||||
if not c:
|
||||
c = Category(name=name, parent_id=parent)
|
||||
c = models.Category(name=name, parent_id=parent)
|
||||
session.add(c)
|
||||
session.commit()
|
||||
return c.id
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey
|
||||
from sqlalchemy.orm import relationship, backref
|
||||
from helper import format_amount
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
|
@ -20,7 +21,7 @@ class Transaction(Base):
|
|||
return self.amount > 0
|
||||
|
||||
def pretty_amount(self):
|
||||
return "{0:.2f}".format(self.amount / 100)
|
||||
return format_amount(self.amount)
|
||||
|
||||
def get_date(self, format):
|
||||
if format == "iso":
|
||||
|
|
16
serve.py
16
serve.py
|
@ -1,10 +1,10 @@
|
|||
#! /usr/bin/env python3
|
||||
from bottle import route, run, template, redirect, request, post
|
||||
from models import Category, Transaction
|
||||
from helper import get_session
|
||||
from helper import get_session, format_amount
|
||||
from datetime import datetime
|
||||
from sqlalchemy import func
|
||||
import click
|
||||
from models import Transaction, Category
|
||||
|
||||
session = None
|
||||
|
||||
|
@ -14,7 +14,7 @@ def index():
|
|||
fom = datetime.today().replace(day=1)
|
||||
sum_of_categories = (
|
||||
session.query(Transaction.category_id, func.sum(Transaction.amount).label("total"))
|
||||
.filter(Transaction.date < fom)
|
||||
.filter(Transaction.date > fom)
|
||||
.group_by(Transaction.category_id)
|
||||
.all()
|
||||
)
|
||||
|
@ -22,10 +22,10 @@ def index():
|
|||
for s in sum_of_categories:
|
||||
if s.category_id:
|
||||
categories.append(
|
||||
{"name": session.query(Category).get(s.category_id).full_name(), "amount": s.total / 100,}
|
||||
{"name": session.query(Category).get(s.category_id).full_name(), "amount": format_amount(s.total)}
|
||||
)
|
||||
else:
|
||||
categories.append({"name": "Unsorted", "amount": s.total / 100})
|
||||
categories.append({"name": "Unsorted", "amount": format_amount(s.total)})
|
||||
|
||||
categories = sorted(categories, key=lambda i: i["name"])
|
||||
return template("index", categories=categories)
|
||||
|
@ -39,6 +39,12 @@ def categories():
|
|||
return template("categories", categories=categories)
|
||||
|
||||
|
||||
@route("/graph")
|
||||
def graph():
|
||||
data = []
|
||||
return template("graph", data=data)
|
||||
|
||||
|
||||
@route("/category/<name>")
|
||||
def category(name):
|
||||
c = session.query(Category).filter(Category.name == name).first()
|
||||
|
|
8
sort.py
8
sort.py
|
@ -16,7 +16,7 @@ def command(profile):
|
|||
category_lookup = {c.full_name(): c.id for c in categories}
|
||||
category_names = FuzzyWordCompleter([c.full_name() for c in categories])
|
||||
|
||||
unsorted = session.query(Transaction).filter(Transaction.category_id is None).all()
|
||||
unsorted = session.query(Transaction).filter(Transaction.category_id == None).all()
|
||||
print("Found {} unsorted transcations".format(len(unsorted)))
|
||||
|
||||
for t in unsorted:
|
||||
|
@ -29,10 +29,14 @@ def command(profile):
|
|||
print("Goodbye")
|
||||
sys.exit(0)
|
||||
|
||||
if select == "":
|
||||
print("Skipping")
|
||||
continue
|
||||
|
||||
cat_id = category_lookup.get(select, None)
|
||||
if not cat_id:
|
||||
print(f"Creating new category '{select}'")
|
||||
cat_id = add_category(select, profile)
|
||||
cat_id = add_category(select, profile, session)
|
||||
# Update the list of categories
|
||||
categories = get_list_of_bookable_categories(session)
|
||||
category_lookup = {c.full_name(): c.id for c in categories}
|
||||
|
|
653
static/apexcharts.css
Normal file
653
static/apexcharts.css
Normal file
|
@ -0,0 +1,653 @@
|
|||
.apexcharts-canvas {
|
||||
position: relative;
|
||||
user-select: none;
|
||||
/* cannot give overflow: hidden as it will crop tooltips which overflow outside chart area */
|
||||
}
|
||||
|
||||
|
||||
/* scrollbar is not visible by default for legend, hence forcing the visibility */
|
||||
.apexcharts-canvas ::-webkit-scrollbar {
|
||||
-webkit-appearance: none;
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.apexcharts-canvas ::-webkit-scrollbar-thumb {
|
||||
border-radius: 4px;
|
||||
background-color: rgba(0, 0, 0, .5);
|
||||
box-shadow: 0 0 1px rgba(255, 255, 255, .5);
|
||||
-webkit-box-shadow: 0 0 1px rgba(255, 255, 255, .5);
|
||||
}
|
||||
|
||||
.apexcharts-canvas.apexcharts-theme-dark {
|
||||
background: #343F57;
|
||||
}
|
||||
|
||||
.apexcharts-inner {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.apexcharts-text tspan {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.legend-mouseover-inactive {
|
||||
transition: 0.15s ease all;
|
||||
opacity: 0.20;
|
||||
}
|
||||
|
||||
.apexcharts-series-collapsed {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip {
|
||||
border-radius: 5px;
|
||||
box-shadow: 2px 2px 6px -4px #999;
|
||||
cursor: default;
|
||||
font-size: 14px;
|
||||
left: 62px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
z-index: 12;
|
||||
transition: 0.15s ease all;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip.apexcharts-theme-light {
|
||||
border: 1px solid #e3e3e3;
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
}
|
||||
|
||||
.apexcharts-tooltip.apexcharts-theme-dark {
|
||||
color: #fff;
|
||||
background: rgba(30, 30, 30, 0.8);
|
||||
}
|
||||
|
||||
.apexcharts-tooltip * {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip.apexcharts-active {
|
||||
opacity: 1;
|
||||
transition: 0.15s ease all;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip-title {
|
||||
padding: 6px;
|
||||
font-size: 15px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip.apexcharts-theme-light .apexcharts-tooltip-title {
|
||||
background: #ECEFF1;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip.apexcharts-theme-dark .apexcharts-tooltip-title {
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
border-bottom: 1px solid #333;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip-text-value,
|
||||
.apexcharts-tooltip-text-z-value {
|
||||
display: inline-block;
|
||||
font-weight: 600;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip-text-z-label:empty,
|
||||
.apexcharts-tooltip-text-z-value:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip-text-value,
|
||||
.apexcharts-tooltip-text-z-value {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip-marker {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
position: relative;
|
||||
top: 0px;
|
||||
margin-right: 10px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip-series-group {
|
||||
padding: 0 10px;
|
||||
display: none;
|
||||
text-align: left;
|
||||
justify-content: left;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip-series-group.apexcharts-active .apexcharts-tooltip-marker {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip-series-group.apexcharts-active,
|
||||
.apexcharts-tooltip-series-group:last-child {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip-series-group-hidden {
|
||||
opacity: 0;
|
||||
height: 0;
|
||||
line-height: 0;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip-y-group {
|
||||
padding: 6px 0 5px;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip-candlestick {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip-candlestick>div {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip-candlestick span.value {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip-rangebar {
|
||||
padding: 5px 8px;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip-rangebar .category {
|
||||
font-weight: 600;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip-rangebar .series-name {
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.apexcharts-xaxistooltip {
|
||||
opacity: 0;
|
||||
padding: 9px 10px;
|
||||
pointer-events: none;
|
||||
color: #373d3f;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
border-radius: 2px;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
background: #ECEFF1;
|
||||
border: 1px solid #90A4AE;
|
||||
transition: 0.15s ease all;
|
||||
}
|
||||
|
||||
.apexcharts-xaxistooltip.apexcharts-theme-dark {
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.apexcharts-xaxistooltip:after,
|
||||
.apexcharts-xaxistooltip:before {
|
||||
left: 50%;
|
||||
border: solid transparent;
|
||||
content: " ";
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.apexcharts-xaxistooltip:after {
|
||||
border-color: rgba(236, 239, 241, 0);
|
||||
border-width: 6px;
|
||||
margin-left: -6px;
|
||||
}
|
||||
|
||||
.apexcharts-xaxistooltip:before {
|
||||
border-color: rgba(144, 164, 174, 0);
|
||||
border-width: 7px;
|
||||
margin-left: -7px;
|
||||
}
|
||||
|
||||
.apexcharts-xaxistooltip-bottom:after,
|
||||
.apexcharts-xaxistooltip-bottom:before {
|
||||
bottom: 100%;
|
||||
}
|
||||
|
||||
.apexcharts-xaxistooltip-top:after,
|
||||
.apexcharts-xaxistooltip-top:before {
|
||||
top: 100%;
|
||||
}
|
||||
|
||||
.apexcharts-xaxistooltip-bottom:after {
|
||||
border-bottom-color: #ECEFF1;
|
||||
}
|
||||
|
||||
.apexcharts-xaxistooltip-bottom:before {
|
||||
border-bottom-color: #90A4AE;
|
||||
}
|
||||
|
||||
.apexcharts-xaxistooltip-bottom.apexcharts-theme-dark:after {
|
||||
border-bottom-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.apexcharts-xaxistooltip-bottom.apexcharts-theme-dark:before {
|
||||
border-bottom-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.apexcharts-xaxistooltip-top:after {
|
||||
border-top-color: #ECEFF1
|
||||
}
|
||||
|
||||
.apexcharts-xaxistooltip-top:before {
|
||||
border-top-color: #90A4AE;
|
||||
}
|
||||
|
||||
.apexcharts-xaxistooltip-top.apexcharts-theme-dark:after {
|
||||
border-top-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.apexcharts-xaxistooltip-top.apexcharts-theme-dark:before {
|
||||
border-top-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.apexcharts-xaxistooltip.apexcharts-active {
|
||||
opacity: 1;
|
||||
transition: 0.15s ease all;
|
||||
}
|
||||
|
||||
.apexcharts-yaxistooltip {
|
||||
opacity: 0;
|
||||
padding: 4px 10px;
|
||||
pointer-events: none;
|
||||
color: #373d3f;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
border-radius: 2px;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
background: #ECEFF1;
|
||||
border: 1px solid #90A4AE;
|
||||
}
|
||||
|
||||
.apexcharts-yaxistooltip.apexcharts-theme-dark {
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.apexcharts-yaxistooltip:after,
|
||||
.apexcharts-yaxistooltip:before {
|
||||
top: 50%;
|
||||
border: solid transparent;
|
||||
content: " ";
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.apexcharts-yaxistooltip:after {
|
||||
border-color: rgba(236, 239, 241, 0);
|
||||
border-width: 6px;
|
||||
margin-top: -6px;
|
||||
}
|
||||
|
||||
.apexcharts-yaxistooltip:before {
|
||||
border-color: rgba(144, 164, 174, 0);
|
||||
border-width: 7px;
|
||||
margin-top: -7px;
|
||||
}
|
||||
|
||||
.apexcharts-yaxistooltip-left:after,
|
||||
.apexcharts-yaxistooltip-left:before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.apexcharts-yaxistooltip-right:after,
|
||||
.apexcharts-yaxistooltip-right:before {
|
||||
right: 100%;
|
||||
}
|
||||
|
||||
.apexcharts-yaxistooltip-left:after {
|
||||
border-left-color: #ECEFF1;
|
||||
}
|
||||
|
||||
.apexcharts-yaxistooltip-left:before {
|
||||
border-left-color: #90A4AE;
|
||||
}
|
||||
|
||||
.apexcharts-yaxistooltip-left.apexcharts-theme-dark:after {
|
||||
border-left-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.apexcharts-yaxistooltip-left.apexcharts-theme-dark:before {
|
||||
border-left-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.apexcharts-yaxistooltip-right:after {
|
||||
border-right-color: #ECEFF1;
|
||||
}
|
||||
|
||||
.apexcharts-yaxistooltip-right:before {
|
||||
border-right-color: #90A4AE;
|
||||
}
|
||||
|
||||
.apexcharts-yaxistooltip-right.apexcharts-theme-dark:after {
|
||||
border-right-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.apexcharts-yaxistooltip-right.apexcharts-theme-dark:before {
|
||||
border-right-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.apexcharts-yaxistooltip.apexcharts-active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.apexcharts-yaxistooltip-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.apexcharts-xcrosshairs,
|
||||
.apexcharts-ycrosshairs {
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transition: 0.15s ease all;
|
||||
}
|
||||
|
||||
.apexcharts-xcrosshairs.apexcharts-active,
|
||||
.apexcharts-ycrosshairs.apexcharts-active {
|
||||
opacity: 1;
|
||||
transition: 0.15s ease all;
|
||||
}
|
||||
|
||||
.apexcharts-ycrosshairs-hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.apexcharts-selection-rect {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.svg_select_points,
|
||||
.svg_select_points_rot {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.svg_select_points_l,
|
||||
.svg_select_points_r {
|
||||
cursor: ew-resize;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
fill: #888;
|
||||
}
|
||||
|
||||
.apexcharts-canvas.apexcharts-zoomable .hovering-zoom {
|
||||
cursor: crosshair
|
||||
}
|
||||
|
||||
.apexcharts-canvas.apexcharts-zoomable .hovering-pan {
|
||||
cursor: move
|
||||
}
|
||||
|
||||
.apexcharts-zoom-icon,
|
||||
.apexcharts-zoomin-icon,
|
||||
.apexcharts-zoomout-icon,
|
||||
.apexcharts-reset-icon,
|
||||
.apexcharts-pan-icon,
|
||||
.apexcharts-selection-icon,
|
||||
.apexcharts-menu-icon,
|
||||
.apexcharts-toolbar-custom-icon {
|
||||
cursor: pointer;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
line-height: 24px;
|
||||
color: #6E8192;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.apexcharts-zoom-icon svg,
|
||||
.apexcharts-zoomin-icon svg,
|
||||
.apexcharts-zoomout-icon svg,
|
||||
.apexcharts-reset-icon svg,
|
||||
.apexcharts-menu-icon svg {
|
||||
fill: #6E8192;
|
||||
}
|
||||
|
||||
.apexcharts-selection-icon svg {
|
||||
fill: #444;
|
||||
transform: scale(0.76)
|
||||
}
|
||||
|
||||
.apexcharts-theme-dark .apexcharts-zoom-icon svg,
|
||||
.apexcharts-theme-dark .apexcharts-zoomin-icon svg,
|
||||
.apexcharts-theme-dark .apexcharts-zoomout-icon svg,
|
||||
.apexcharts-theme-dark .apexcharts-reset-icon svg,
|
||||
.apexcharts-theme-dark .apexcharts-pan-icon svg,
|
||||
.apexcharts-theme-dark .apexcharts-selection-icon svg,
|
||||
.apexcharts-theme-dark .apexcharts-menu-icon svg,
|
||||
.apexcharts-theme-dark .apexcharts-toolbar-custom-icon svg {
|
||||
fill: #f3f4f5;
|
||||
}
|
||||
|
||||
.apexcharts-canvas .apexcharts-zoom-icon.apexcharts-selected svg,
|
||||
.apexcharts-canvas .apexcharts-selection-icon.apexcharts-selected svg,
|
||||
.apexcharts-canvas .apexcharts-reset-zoom-icon.apexcharts-selected svg {
|
||||
fill: #008FFB;
|
||||
}
|
||||
|
||||
.apexcharts-theme-light .apexcharts-selection-icon:not(.apexcharts-selected):hover svg,
|
||||
.apexcharts-theme-light .apexcharts-zoom-icon:not(.apexcharts-selected):hover svg,
|
||||
.apexcharts-theme-light .apexcharts-zoomin-icon:hover svg,
|
||||
.apexcharts-theme-light .apexcharts-zoomout-icon:hover svg,
|
||||
.apexcharts-theme-light .apexcharts-reset-icon:hover svg,
|
||||
.apexcharts-theme-light .apexcharts-menu-icon:hover svg {
|
||||
fill: #333;
|
||||
}
|
||||
|
||||
.apexcharts-selection-icon,
|
||||
.apexcharts-menu-icon {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.apexcharts-reset-icon {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.apexcharts-zoom-icon,
|
||||
.apexcharts-reset-icon,
|
||||
.apexcharts-menu-icon {
|
||||
transform: scale(0.85);
|
||||
}
|
||||
|
||||
.apexcharts-zoomin-icon,
|
||||
.apexcharts-zoomout-icon {
|
||||
transform: scale(0.7)
|
||||
}
|
||||
|
||||
.apexcharts-zoomout-icon {
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.apexcharts-pan-icon {
|
||||
transform: scale(0.62);
|
||||
position: relative;
|
||||
left: 1px;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
.apexcharts-pan-icon svg {
|
||||
fill: #fff;
|
||||
stroke: #6E8192;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.apexcharts-pan-icon.apexcharts-selected svg {
|
||||
stroke: #008FFB;
|
||||
}
|
||||
|
||||
.apexcharts-pan-icon:not(.apexcharts-selected):hover svg {
|
||||
stroke: #333;
|
||||
}
|
||||
|
||||
.apexcharts-toolbar {
|
||||
position: absolute;
|
||||
z-index: 11;
|
||||
max-width: 176px;
|
||||
text-align: right;
|
||||
border-radius: 3px;
|
||||
padding: 0px 6px 2px 6px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.apexcharts-menu {
|
||||
background: #fff;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 3px;
|
||||
padding: 3px;
|
||||
right: 10px;
|
||||
opacity: 0;
|
||||
min-width: 110px;
|
||||
transition: 0.15s ease all;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.apexcharts-menu.apexcharts-menu-open {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
transition: 0.15s ease all;
|
||||
}
|
||||
|
||||
.apexcharts-menu-item {
|
||||
padding: 6px 7px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.apexcharts-theme-light .apexcharts-menu-item:hover {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
.apexcharts-theme-dark .apexcharts-menu {
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.apexcharts-canvas:hover .apexcharts-toolbar {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.apexcharts-datalabel.apexcharts-element-hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.apexcharts-pie-label,
|
||||
.apexcharts-datalabels,
|
||||
.apexcharts-datalabel,
|
||||
.apexcharts-datalabel-label,
|
||||
.apexcharts-datalabel-value {
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.apexcharts-pie-label-delay {
|
||||
opacity: 0;
|
||||
animation-name: opaque;
|
||||
animation-duration: 0.3s;
|
||||
animation-fill-mode: forwards;
|
||||
animation-timing-function: ease;
|
||||
}
|
||||
|
||||
.apexcharts-canvas .apexcharts-element-hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.apexcharts-hide .apexcharts-series-points {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.apexcharts-gridline,
|
||||
.apexcharts-annotation-rect,
|
||||
.apexcharts-tooltip .apexcharts-marker,
|
||||
.apexcharts-area-series .apexcharts-area,
|
||||
.apexcharts-line,
|
||||
.apexcharts-zoom-rect,
|
||||
.apexcharts-toolbar svg,
|
||||
.apexcharts-annotations-rects,
|
||||
.apexcharts-area-series .apexcharts-series-markers .apexcharts-marker.no-pointer-events,
|
||||
.apexcharts-line-series .apexcharts-series-markers .apexcharts-marker.no-pointer-events,
|
||||
.apexcharts-radar-series path,
|
||||
.apexcharts-radar-series polygon {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
/* markers */
|
||||
|
||||
.apexcharts-marker {
|
||||
transition: 0.15s ease all;
|
||||
}
|
||||
|
||||
@keyframes opaque {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Resize generated styles */
|
||||
|
||||
@keyframes resizeanim {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.resize-triggers {
|
||||
animation: 1ms resizeanim;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.resize-triggers,
|
||||
.resize-triggers>div,
|
||||
.contract-trigger:before {
|
||||
content: " ";
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.resize-triggers>div {
|
||||
background: #eee;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.contract-trigger:before {
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
}
|
14
static/apexcharts.min.js
vendored
Normal file
14
static/apexcharts.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -19,12 +19,18 @@
|
|||
<li class="mr-2">
|
||||
<a class="text-blue-500 hover:text-blue-800" href="/transactions">Transactions</a>
|
||||
</li>
|
||||
<li class="mr-2">
|
||||
<a class="text-blue-500 hover:text-blue-800" href="/transactions?unsorted=1">Unsorted Transactions</a>
|
||||
</li>
|
||||
<li class="mr-2">
|
||||
<a class="text-blue-500 hover:text-blue-800" href="/categories">Categories</a>
|
||||
</li>
|
||||
<li class="mr-2">
|
||||
<a class="text-blue-500 hover:text-blue-800" href="/graph">Monthly Graph</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="container mx-auto">
|
||||
<div class="pt-2 container mx-auto">
|
||||
|
||||
{{!base}}
|
||||
</div>
|
||||
|
|
4
views/graph.tpl
Normal file
4
views/graph.tpl
Normal file
|
@ -0,0 +1,4 @@
|
|||
% rebase("base.tpl")
|
||||
|
||||
<h1 class="text-2xl font-bold text-indigo-500">Last 12 months</h1>
|
||||
|
|
@ -2,18 +2,25 @@
|
|||
|
||||
<h1 class="text-2xl font-bold text-indigo-500">Monatsübersicht</h1>
|
||||
|
||||
<table class="table-auto border-none">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-4 py-2">Categorie</th>
|
||||
<th class="px-4 py-2 text-right">Summe</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
% for c in categories:
|
||||
<tr>
|
||||
<td class="border px-4 py-2">{{ c["name"] }}</td>
|
||||
<td class="border px-4 py-2 text-right {{ 'text-green-500' if c['amount'] > 0 else 'text-red-500' }}">{{ c["amount"] }} €</td>
|
||||
</tr>
|
||||
|
||||
% if len(categories) > 0:
|
||||
<table class="table-auto border-none">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-4 py-2">Categorie</th>
|
||||
<th class="px-4 py-2 text-right">Summe</th>
|
||||
</tr>kk
|
||||
</thead>
|
||||
<tbody>
|
||||
% for c in categories:
|
||||
<tr>
|
||||
<td class="border px-4 py-2">{{ c["name"] }}</td>
|
||||
<td class="border px-4 py-2 text-right {{ 'text-red-500' if c['amount'][0] == "-" else 'text-green-500' }}">{{ c["amount"] }} €</td>
|
||||
</tr>
|
||||
% end
|
||||
</tbody>
|
||||
% else:
|
||||
|
||||
No transactions this month
|
||||
|
||||
% end
|
||||
</tbody>
|
|
@ -14,7 +14,7 @@
|
|||
<th class="px-4 py-2">Date</th>
|
||||
<th class="px-4 py-2">Sender/Empfänger</th>
|
||||
<th class="px-4 py-2">Verwendungszweck</th>
|
||||
<th class="px-4 py-2">Betrag</th>
|
||||
<th class="px-4 py-2 text-right">Betrag</th>
|
||||
<th class="px-4 py-2">Kategorie</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -25,7 +25,7 @@
|
|||
<td class="border px-4 py-2">{{ t.get_date("de") }}</td>
|
||||
<td class="border px-4 py-2">{{ t.name }}<br/> <span class="text-gray-500">{{t.iban}}</span></td>
|
||||
<td class="border px-4 py-2">{{ t.description }}</td>
|
||||
<td class="border px-4 py-2 {{ 'text-green-500' if t.is_positive() else 'text-red-500' }}">{{ t.pretty_amount() }}</td>
|
||||
<td class="border px-4 py-2 text-right {{ 'text-green-500' if t.is_positive() else 'text-red-500' }}">{{ t.pretty_amount() }} €</td>
|
||||
% if t.category:
|
||||
<td class="border px-4 py-2"> <a href="/category/{{ t.category.name }}"> {{ t.category.full_name()}}</a></td>
|
||||
% else:
|
||||
|
|
Loading…
Add table
Reference in a new issue