From 3c9d4a79f8f8fd06c0c77a34a78ae0daf0c555b2 Mon Sep 17 00:00:00 2001 From: Marc Pervaz Boocha Date: Sun, 30 Jul 2023 15:24:43 +0530 Subject: Initial Commit Signed-off-by: Marc Pervaz Boocha --- src/expense/tracker/__init__.py | 5 +++ src/expense/tracker/blueprint.py | 5 +++ src/expense/tracker/cat.py | 29 ++++++++++++++ src/expense/tracker/category.py | 43 ++++++++++++++++++++ src/expense/tracker/create.py | 61 +++++++++++++++++++++++++++++ src/expense/tracker/details.py | 22 +++++++++++ src/expense/tracker/index.py | 24 ++++++++++++ src/expense/tracker/templates/cat.html | 8 ++++ src/expense/tracker/templates/category.html | 16 ++++++++ src/expense/tracker/templates/create.html | 17 ++++++++ src/expense/tracker/templates/details.html | 10 +++++ src/expense/tracker/templates/index.html | 11 ++++++ src/expense/tracker/templates/trans.html | 17 ++++++++ src/expense/tracker/trans.py | 56 ++++++++++++++++++++++++++ 14 files changed, 324 insertions(+) create mode 100644 src/expense/tracker/__init__.py create mode 100644 src/expense/tracker/blueprint.py create mode 100644 src/expense/tracker/cat.py create mode 100644 src/expense/tracker/category.py create mode 100644 src/expense/tracker/create.py create mode 100644 src/expense/tracker/details.py create mode 100644 src/expense/tracker/index.py create mode 100644 src/expense/tracker/templates/cat.html create mode 100644 src/expense/tracker/templates/category.html create mode 100644 src/expense/tracker/templates/create.html create mode 100644 src/expense/tracker/templates/details.html create mode 100644 src/expense/tracker/templates/index.html create mode 100644 src/expense/tracker/templates/trans.html create mode 100644 src/expense/tracker/trans.py (limited to 'src/expense/tracker') 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/") +@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 %} +

{{ category.name }}

+{{ 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 %} +

Manage Categories

+
+
+ {{ form.hidden_tag() }} + {% for cat in current_user.categories %} +

{{ cat.name }}

+ {% endfor %} +
+ {{ label_field(form.category) }} + {{ form.submit }} +
+{% 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 %} +

Create New Entry

+{% endblock %} + +{% block content %} +
+ {{ form.hidden_tag() }} + {{ label_field(form.date) }} + {{ label_field(form.category) }} + {{ label_field(form.description) }} + {{ label_field(form.amount) }} + {{ form.submit }} +
+{% 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 %} +

Details

+{% 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 %} +

Summary

+{% endblock %} + +{% block content %} + +
+{% 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 %} +
+

Edit Entry

+ {{ form.hidden_tag() }} + {{ label_field(form.date) }} + {{ label_field(form.category) }} + {{ label_field(form.description) }} + {{ label_field(form.amount) }} +
+ {{ form.submit }} + {{ form.delete }} +
+
+{% 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/", 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) -- cgit v1.2.3-70-g09d2