diff options
Diffstat (limited to 'src/expense/tracker')
-rw-r--r-- | src/expense/tracker/__init__.py | 5 | ||||
-rw-r--r-- | src/expense/tracker/blueprint.py | 5 | ||||
-rw-r--r-- | src/expense/tracker/cat.py | 29 | ||||
-rw-r--r-- | src/expense/tracker/category.py | 43 | ||||
-rw-r--r-- | src/expense/tracker/create.py | 61 | ||||
-rw-r--r-- | src/expense/tracker/details.py | 22 | ||||
-rw-r--r-- | src/expense/tracker/index.py | 24 | ||||
-rw-r--r-- | src/expense/tracker/templates/cat.html | 8 | ||||
-rw-r--r-- | src/expense/tracker/templates/category.html | 16 | ||||
-rw-r--r-- | src/expense/tracker/templates/create.html | 17 | ||||
-rw-r--r-- | src/expense/tracker/templates/details.html | 10 | ||||
-rw-r--r-- | src/expense/tracker/templates/index.html | 11 | ||||
-rw-r--r-- | src/expense/tracker/templates/trans.html | 17 | ||||
-rw-r--r-- | src/expense/tracker/trans.py | 56 |
14 files changed, 324 insertions, 0 deletions
diff --git a/src/expense/tracker/__init__.py b/src/expense/tracker/__init__.py new file mode 100644 index 0000000..d19e89c --- /dev/null +++ b/src/expense/tracker/__init__.py @@ -0,0 +1,5 @@ +""" +The Expense Tracker +""" + +from .blueprint import tracker diff --git a/src/expense/tracker/blueprint.py b/src/expense/tracker/blueprint.py new file mode 100644 index 0000000..960301e --- /dev/null +++ b/src/expense/tracker/blueprint.py @@ -0,0 +1,5 @@ +from flask import Blueprint + +tracker = Blueprint("tracker", __name__) + +from . import create, index, trans, category, cat, details diff --git a/src/expense/tracker/cat.py b/src/expense/tracker/cat.py new file mode 100644 index 0000000..50aa02d --- /dev/null +++ b/src/expense/tracker/cat.py @@ -0,0 +1,29 @@ +from flask import ( + render_template, +) +from flask_login import current_user, login_required +from sqlalchemy import func +from .blueprint import tracker +from ..model import Category, Expense +from .. import db + + +@tracker.route("/cat/<int:id>") +@login_required +def cat(id: int) -> str: + """ + The per category view of the the user expense. + """ + category = db.one_or_404(db.select(Category).where(Category.id == id)) + + total = db.session.scalars( + db.select(func.coalesce(func.sum(Expense.amount), 0)).where( + Expense.user == current_user, Expense.category == category + ) + ).all() + table = db.session.scalars( + db.select(Expense).where( + Expense.user == current_user, Expense.category == category + ) + ).all() + return render_template("tracker/cat.html", table=table, total=total, category=category) diff --git a/src/expense/tracker/category.py b/src/expense/tracker/category.py new file mode 100644 index 0000000..a36f737 --- /dev/null +++ b/src/expense/tracker/category.py @@ -0,0 +1,43 @@ +from typing import Union +from flask import ( + redirect, + render_template, + url_for, +) +from flask_login import login_required, current_user +from werkzeug import Response + +from .. import db + +from flask_wtf import FlaskForm +from wtforms import StringField, SubmitField +from wtforms.validators import InputRequired + +from ..model import Category +from .blueprint import tracker + + +class CategoryForm(FlaskForm): + """Add Category""" + category = StringField("Add Category", validators=[InputRequired()]) # type: ignore + submit = SubmitField() + + +@tracker.route("/category", methods=["GET", "POST"]) +@login_required +def category() -> Union[str, Response]: + """ + To manage the list of user defined capabilities + """ + form = CategoryForm() + if form.validate_on_submit(): + category = form.category.data + cat = db.session.scalars( + db.select(Category).where(Category.name == category) + ).one_or_none() + if cat is None: + cat = Category(name=category) + current_user.categories.append(cat) # type: ignore + db.session.commit() + return redirect(url_for("index")) + return render_template("tracker/category.html", form=form) diff --git a/src/expense/tracker/create.py b/src/expense/tracker/create.py new file mode 100644 index 0000000..c6f8308 --- /dev/null +++ b/src/expense/tracker/create.py @@ -0,0 +1,61 @@ +from datetime import date +from flask import ( + redirect, + render_template, + url_for, +) +from flask_login import login_required, current_user + +from .. import db + +from flask_wtf import FlaskForm +from wtforms import ( + DateField, + TextAreaField, + DecimalField, + SubmitField, + SelectField, +) +from wtforms.widgets import NumberInput +from wtforms.validators import InputRequired + +from ..model import Category, Expense +from .blueprint import tracker + + +class CreateForm(FlaskForm): + """ + The Form to create a entry + """ + description = TextAreaField() + date = DateField( + validators=[InputRequired()], format="%Y-%m-%d", default=date.today + ) + amount = DecimalField(places=2, validators=[InputRequired()], widget=NumberInput(), default=0) # type: ignore + category = SelectField(coerce=int) # type: ignore + submit = SubmitField() + + def __init__(self, **kwargs) -> None: + super().__init__(**kwargs) + self.category.choices = [(cat.id, cat.name) for cat in current_user.categories] # type: ignore + + +@tracker.route("/create", methods=["GET", "POST"]) +@login_required +def create(): + """The view to handle new entry""" + form = CreateForm() + if form.validate_on_submit(): + cat = db.session.scalars( + db.select(Category).where(Category.id == form.category.data) + ).one() + expense = Expense( + description=form.description.data, + date=form.date.data, + amount=form.amount.data, + category=cat, + ) # type: ignore + current_user.expenses.append(expense) # type: ignore + db.session.commit() + return redirect(url_for("index")) + return render_template("tracker/create.html", form=form) diff --git a/src/expense/tracker/details.py b/src/expense/tracker/details.py new file mode 100644 index 0000000..3849a81 --- /dev/null +++ b/src/expense/tracker/details.py @@ -0,0 +1,22 @@ +from flask import ( + render_template, +) +from flask_login import current_user, login_required +from sqlalchemy import func +from .blueprint import tracker +from ..model import Expense +from .. import db + + +@tracker.route("/details") +@login_required +def details() -> str: + """ + The detail table of all the expenses + """ + total = db.session.scalars( + db.select(func.coalesce(func.sum(Expense.amount), 0)).where( + Expense.user == current_user + ) + ).all() + return render_template("tracker/details.html", total=total) diff --git a/src/expense/tracker/index.py b/src/expense/tracker/index.py new file mode 100644 index 0000000..946ef67 --- /dev/null +++ b/src/expense/tracker/index.py @@ -0,0 +1,24 @@ +from flask import render_template, jsonify +from werkzeug import Response +from werkzeug.local import LocalProxy +from flask_login import current_user, login_required +from .blueprint import tracker + + +@tracker.route("/") +@login_required +def index() -> str: + """ + The dashboard + """ + return render_template("tracker/index.html") + + +@tracker.route("/summary.json") +@login_required +def summary() -> Response: + """ + The Summary Api + Returns the list of all of the user expenses in json + """ + return jsonify(current_user.expenses) # type: ignore diff --git a/src/expense/tracker/templates/cat.html b/src/expense/tracker/templates/cat.html new file mode 100644 index 0000000..ee35193 --- /dev/null +++ b/src/expense/tracker/templates/cat.html @@ -0,0 +1,8 @@ +{% from 'macros.html' import expense_table %} +{% extends 'base.html' %} + + +{% block content %} +<h2>{{ category.name }}</h2> +{{ expense_table(table, total[0]) }} +{% endblock %}
\ No newline at end of file diff --git a/src/expense/tracker/templates/category.html b/src/expense/tracker/templates/category.html new file mode 100644 index 0000000..50c4a11 --- /dev/null +++ b/src/expense/tracker/templates/category.html @@ -0,0 +1,16 @@ +{% from 'macros.html' import label_field %} +{% extends 'base.html' %} + +{% block content %} +<h2>Manage Categories</h2> +<form method="post"> + <div> + {{ form.hidden_tag() }} + {% for cat in current_user.categories %} + <p>{{ cat.name }}</p> + {% endfor %} + </div> + {{ label_field(form.category) }} + {{ form.submit }} +</form> +{% endblock %} diff --git a/src/expense/tracker/templates/create.html b/src/expense/tracker/templates/create.html new file mode 100644 index 0000000..e010455 --- /dev/null +++ b/src/expense/tracker/templates/create.html @@ -0,0 +1,17 @@ +{% from 'macros.html' import label_field %} +{% extends 'base.html' %} + +{% block header %} +<h2>Create New Entry</h2> +{% endblock %} + +{% block content %} +<form method="post"> + {{ form.hidden_tag() }} + {{ label_field(form.date) }} + {{ label_field(form.category) }} + {{ label_field(form.description) }} + {{ label_field(form.amount) }} + {{ form.submit }} +</form> +{% endblock %} diff --git a/src/expense/tracker/templates/details.html b/src/expense/tracker/templates/details.html new file mode 100644 index 0000000..fa7eb1e --- /dev/null +++ b/src/expense/tracker/templates/details.html @@ -0,0 +1,10 @@ +{% from 'macros.html' import expense_table %} +{% extends 'base.html' %} + +{% block header %} + <h2>Details</h2> +{% endblock%} + +{% block content %} + {{ expense_table(current_user.expenses, total[0]) }} +{% endblock %}
\ No newline at end of file diff --git a/src/expense/tracker/templates/index.html b/src/expense/tracker/templates/index.html new file mode 100644 index 0000000..ce7bc92 --- /dev/null +++ b/src/expense/tracker/templates/index.html @@ -0,0 +1,11 @@ +{% from 'macros.html' import expense_table %} +{% extends 'base.html' %} + +{% block header %} +<h2>Summary</h2> +{% endblock %} + +{% block content %} +<script src="{{ url_for('static', filename='script.js') }}" type="module"></script> +<div id="root" ></div> +{% endblock %}
\ No newline at end of file diff --git a/src/expense/tracker/templates/trans.html b/src/expense/tracker/templates/trans.html new file mode 100644 index 0000000..449aa99 --- /dev/null +++ b/src/expense/tracker/templates/trans.html @@ -0,0 +1,17 @@ +{% from 'macros.html' import label_field %} +{% extends 'base.html' %} + +{% block content %} +<form method="post"> + <h2>Edit Entry</h2> + {{ form.hidden_tag() }} + {{ label_field(form.date) }} + {{ label_field(form.category) }} + {{ label_field(form.description) }} + {{ label_field(form.amount) }} + <div class="button-group"> + {{ form.submit }} + {{ form.delete }} + </div> +</form> +{% endblock %}
\ No newline at end of file diff --git a/src/expense/tracker/trans.py b/src/expense/tracker/trans.py new file mode 100644 index 0000000..5caecb8 --- /dev/null +++ b/src/expense/tracker/trans.py @@ -0,0 +1,56 @@ +from typing import Union +from flask import ( + redirect, + render_template, + url_for, +) +from flask_login import login_required, current_user +from werkzeug import Response + +from .. import db +from ..model import Category, Expense +from .blueprint import tracker +from .create import CreateForm + +from wtforms import SubmitField + + +class TransForm(CreateForm): + """ + Editing the transaction. + Adds the delete button + """ + delete = SubmitField() + + +@tracker.route("/trans/<int:id>", methods=["GET", "POST"]) +@login_required +def trans(id: int) -> Union[str, Response]: + """ + Edits the transaction. It also has the capability of deleting it. + """ + expense: Expense = db.one_or_404(db.select(Expense).where(Expense.id == id, Expense.user == current_user)) # type: ignore + form: TransForm = TransForm( + description=expense.description, + date=expense.date, + amount=expense.amount, + category_id=expense.category.id, + ) + form.category.choices = [(cat.id, cat.name) for cat in current_user.categories] # type: ignore + if form.validate_on_submit(): + if form.delete.data: + db.session.delete(expense) + elif form.submit.data: + cat = db.session.scalars( + db.select(Category).where(Category.id == form.category.data) + ).one() + expense.description = form.description.data + expense.date = form.date.data # type: ignore + expense.amount = form.amount.data # type: ignore + expense.category = cat + + current_user.expenses.append(expense) # type: ignore + + db.session.commit() + return redirect(url_for("index")) + return render_template("tracker/trans.html", form=form) |