From 10ed46576f2f9fc2f9ae81f04ab869c579965a6c Mon Sep 17 00:00:00 2001 From: Houman Hafez Date: Mon, 15 Jul 2024 01:39:00 +0200 Subject: [PATCH] Updated the ui, formatted the files and removed unnecessary migrations folder --- expense_tracker/settings.py | 47 ++-- expense_tracker/urls.py | 6 +- expenses/forms.py | 7 +- expenses/migrations/0001_initial.py | 32 --- expenses/migrations/0002_expense_currency.py | 18 -- .../migrations/0003_alter_expense_currency.py | 18 -- expenses/migrations/__init__.py | 0 expenses/models.py | 6 +- expenses/templates/expenses/add_expense.html | 99 +++++--- expenses/templates/expenses/edit_expense.html | 102 +++++--- expenses/templates/expenses/index.html | 219 ++++++++++-------- .../templates/expenses/search_results.html | 97 ++++---- expenses/urls.py | 10 +- expenses/views.py | 40 ++-- 14 files changed, 378 insertions(+), 323 deletions(-) delete mode 100644 expenses/migrations/0001_initial.py delete mode 100644 expenses/migrations/0002_expense_currency.py delete mode 100644 expenses/migrations/0003_alter_expense_currency.py delete mode 100644 expenses/migrations/__init__.py diff --git a/expense_tracker/settings.py b/expense_tracker/settings.py index ce01798..478cfd4 100644 --- a/expense_tracker/settings.py +++ b/expense_tracker/settings.py @@ -20,38 +20,37 @@ # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'expenses', - 'tailwind', - 'django_browser_reload', - 'allauth', - 'allauth.account', - 'allauth.socialaccount', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "expenses", + "tailwind", + "django_browser_reload", + "allauth", + "allauth.account", + "allauth.socialaccount", ] AUTHENTICATION_BACKENDS = [ - 'django.contrib.auth.backends.ModelBackend', - 'allauth.account.auth_backends.AuthenticationBackend', + "django.contrib.auth.backends.ModelBackend", + "allauth.account.auth_backends.AuthenticationBackend", ] SITE_ID = 1 MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'django_browser_reload.middleware.BrowserReloadMiddleware', - 'allauth.account.middleware.AccountMiddleware', - + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "django_browser_reload.middleware.BrowserReloadMiddleware", + "allauth.account.middleware.AccountMiddleware", ] ROOT_URLCONF = "expense_tracker.urls" diff --git a/expense_tracker/urls.py b/expense_tracker/urls.py index 4973dae..a428a65 100644 --- a/expense_tracker/urls.py +++ b/expense_tracker/urls.py @@ -19,8 +19,8 @@ from django.urls import path, include urlpatterns = [ - path('admin/', admin.site.urls), - path('', include('expenses.urls')), - path('accounts/', include('allauth.urls')), + path("admin/", admin.site.urls), + path("", include("expenses.urls")), + path("accounts/", include("allauth.urls")), path("__reload__/", include("django_browser_reload.urls")), ] diff --git a/expenses/forms.py b/expenses/forms.py index 5bf8d96..1668052 100644 --- a/expenses/forms.py +++ b/expenses/forms.py @@ -1,13 +1,14 @@ from django import forms from .models import Expense + class ExpenseForm(forms.ModelForm): class Meta: model = Expense - fields = ['date', 'category', 'amount', 'currency'] + fields = ["date", "category", "amount", "currency"] widgets = { - 'currency': forms.TextInput(attrs={'placeholder': 'e.g., USD, EUR'}), + "currency": forms.TextInput(attrs={"placeholder": "e.g., USD, EUR"}), } initial = { - 'currency': 'EUR', + "currency": "EUR", } diff --git a/expenses/migrations/0001_initial.py b/expenses/migrations/0001_initial.py deleted file mode 100644 index 9a163a2..0000000 --- a/expenses/migrations/0001_initial.py +++ /dev/null @@ -1,32 +0,0 @@ -# Generated by Django 5.0.7 on 2024-07-12 11:35 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='Month', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=50)), - ], - ), - migrations.CreateModel( - name='Expense', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('date', models.DateField()), - ('category', models.CharField(max_length=100)), - ('amount', models.DecimalField(decimal_places=2, max_digits=10)), - ('month', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='expenses', to='expenses.month')), - ], - ), - ] diff --git a/expenses/migrations/0002_expense_currency.py b/expenses/migrations/0002_expense_currency.py deleted file mode 100644 index 3cc0038..0000000 --- a/expenses/migrations/0002_expense_currency.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.0.7 on 2024-07-14 01:38 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('expenses', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='expense', - name='currency', - field=models.CharField(default='USD', max_length=3), - ), - ] diff --git a/expenses/migrations/0003_alter_expense_currency.py b/expenses/migrations/0003_alter_expense_currency.py deleted file mode 100644 index 08c2a82..0000000 --- a/expenses/migrations/0003_alter_expense_currency.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.0.7 on 2024-07-14 14:37 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('expenses', '0002_expense_currency'), - ] - - operations = [ - migrations.AlterField( - model_name='expense', - name='currency', - field=models.CharField(blank=True, default='USD', max_length=3, null=True), - ), - ] diff --git a/expenses/migrations/__init__.py b/expenses/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/expenses/models.py b/expenses/models.py index ec954f3..9d22807 100644 --- a/expenses/models.py +++ b/expenses/models.py @@ -6,7 +6,7 @@ class Month(models.Model): @property def total_expenses(self): - return self.expenses.aggregate(total=models.Sum('amount'))['total'] or 0 + return self.expenses.aggregate(total=models.Sum("amount"))["total"] or 0 def __str__(self): return self.name @@ -16,8 +16,8 @@ class Expense(models.Model): date = models.DateField() category = models.CharField(max_length=100) amount = models.DecimalField(max_digits=10, decimal_places=2) - month = models.ForeignKey(Month, related_name='expenses', on_delete=models.CASCADE) - currency = models.CharField(max_length=3, default='EUR', blank=True, null=True) + month = models.ForeignKey(Month, related_name="expenses", on_delete=models.CASCADE) + currency = models.CharField(max_length=3, default="EUR", blank=True, null=True) def __str__(self): return f"{self.category} - {self.amount} {self.currency or 'EUR'}" diff --git a/expenses/templates/expenses/add_expense.html b/expenses/templates/expenses/add_expense.html index 593cce5..f6a54b6 100644 --- a/expenses/templates/expenses/add_expense.html +++ b/expenses/templates/expenses/add_expense.html @@ -1,40 +1,71 @@ - - - - + + + Add Expense - - - + + +
-

Add Expense

-
- {% csrf_token %} -
- - -
-
- - -
-
- - -
- -
- - -
- - -
- Back to Expense Tracker -
+

Add Expense

+
+ {% csrf_token %} +
+ + +
+
+ + +
+
+ + +
- +
+ + +
- \ No newline at end of file + +
+ Back to Expense Tracker + + + diff --git a/expenses/templates/expenses/edit_expense.html b/expenses/templates/expenses/edit_expense.html index b3ab0b5..6b839d1 100644 --- a/expenses/templates/expenses/edit_expense.html +++ b/expenses/templates/expenses/edit_expense.html @@ -1,40 +1,74 @@ - - - - + + + Edit Expense - - - + + +
-

Edit Expense

-
- {% csrf_token %} -
- - -
-
- - -
-
- - -
- -
- - -
- - -
- Back to Expense Tracker -
+

Edit Expense

+
+ {% csrf_token %} +
+ + +
+
+ + +
+
+ + +
- +
+ + +
- \ No newline at end of file + +
+ Back to Expense Tracker + + + diff --git a/expenses/templates/expenses/index.html b/expenses/templates/expenses/index.html index 9f00c0a..89a87cf 100644 --- a/expenses/templates/expenses/index.html +++ b/expenses/templates/expenses/index.html @@ -1,106 +1,139 @@ - - - - + + + Expense Tracker - + + - - + +
-

Expense Tracker

- Add Expense - -
- +
+

Expense Tracker

+ Add Expense +
+ +
+ +
+ + + + +
- -
-
-

Expenses by Category

- -
-
-

Expenses by Month

- -
+
+ +
+
+

Expenses by Category

+
- -
- {% for month, expenses in grouped_expenses.items %} -

{{ month }}

-
    - {% for expense in expenses %} -
  • -
    -
    -

    {{ expense.date }}

    -

    {{ expense.category }}

    -
    -
    -

    {{ expense.amount }} {{ expense.currency|default:"EUR" }}

    - Edit -
    -
    -
  • - {% endfor %} -
- {% endfor %} +
+

Expenses by Month

+
+
+ +
+ {% for month, expenses in grouped_expenses.items %} +

{{ month }}

+
    + {% for expense in expenses %} +
  • +
    +
    +

    {{ expense.date }}

    +

    {{ expense.category }}

    +
    +
    +

    + {{ expense.amount }} {{ expense.currency|default:"EUR" }} +

    + Edit +
    +
    +
  • + {% endfor %} +
+ {% endfor %} +
- + // Chart for expenses by category + const ctxCategory = document.getElementById('expensesByCategoryChart').getContext('2d'); + new Chart(ctxCategory, { + type: 'pie', + data: { + labels: Object.keys(expensesByCategoryData), + datasets: [{ + data: Object.values(expensesByCategoryData), + backgroundColor: [ + 'rgba(255, 99, 132, 0.2)', + 'rgba(54, 162, 235, 0.2)', + 'rgba(255, 206, 86, 0.2)', + 'rgba(75, 192, 192, 0.2)', + 'rgba(153, 102, 255, 0.2)', + 'rgba(255, 159, 64, 0.2)' + ], + borderColor: [ + 'rgba(255, 99, 132, 1)', + 'rgba(54, 162, 235, 1)', + 'rgba(255, 206, 86, 1)', + 'rgba(75, 192, 192, 1)', + 'rgba(153, 102, 255, 1)', + 'rgba(255, 159, 64, 1)' + ], + borderWidth: 1 + }] + } + }); - \ No newline at end of file + // Chart for expenses by month + const ctxMonth = document.getElementById('expensesByMonthChart').getContext('2d'); + new Chart(ctxMonth, { + type: 'bar', + data: { + labels: Object.keys(expensesByMonthData), + datasets: [{ + label: 'Expenses by Month', + data: Object.values(expensesByMonthData), + backgroundColor: 'rgba(54, 162, 235, 0.2)', + borderColor: 'rgba(54, 162, 235, 1)', + borderWidth: 1 + }] + } + + }); + + + diff --git a/expenses/templates/expenses/search_results.html b/expenses/templates/expenses/search_results.html index eb1be45..c8f74a2 100644 --- a/expenses/templates/expenses/search_results.html +++ b/expenses/templates/expenses/search_results.html @@ -1,47 +1,66 @@ - - - + + + Search Results - - - + + +
-

Search Results

- -
- - -
+

Search Results

- {% if results %} -
    - {% for expense in results %} -
  • -
    -
    -

    {{ expense.date }}

    -

    {{ expense.category }}

    -

    {{ expense.description }}

    -
    -
    -

    {{ expense.amount }}

    -

    {{ expense.currency }}

    - Edit -
    -
    -
  • - {% endfor %} -
- {% else %} -

No expenses found matching your search criteria.

- {% endif %} - - +
+ + +
+ + {% if results %} +
    + {% for expense in results %} +
  • +
    +
    +

    {{ expense.date }}

    +

    {{ expense.category }}

    +

    {{ expense.description }}

    +
    +
    +

    {{ expense.amount }}

    +

    {{ expense.currency }}

    + Edit +
    +
    +
  • + {% endfor %} +
+ {% else %} +

+ No expenses found matching your search criteria. +

+ {% endif %} + +
- + diff --git a/expenses/urls.py b/expenses/urls.py index 4ce91b3..1fa8a74 100644 --- a/expenses/urls.py +++ b/expenses/urls.py @@ -4,9 +4,9 @@ urlpatterns = [ - path('', views.index, name='index'), - path('add_expense/', views.add_expense, name='add_expense'), - path('expense-data/', views.expense_data, name='expense_data'), - path('edit//', edit_expense, name='edit_expense'), - path('search/', views.search_expenses, name='search_expenses'), + path("", views.index, name="index"), + path("add_expense/", views.add_expense, name="add_expense"), + path("expense-data/", views.expense_data, name="expense_data"), + path("edit//", edit_expense, name="edit_expense"), + path("search/", views.search_expenses, name="search_expenses"), ] diff --git a/expenses/views.py b/expenses/views.py index 439a942..8875459 100644 --- a/expenses/views.py +++ b/expenses/views.py @@ -1,5 +1,4 @@ from django.shortcuts import render, redirect, get_object_or_404 -from django.utils.dateparse import parse_date from django.db.models import Q from django.http import JsonResponse from .models import Expense, Month @@ -9,28 +8,28 @@ def index(request): - expenses = Expense.objects.all().order_by('-date') - + expenses = Expense.objects.all().order_by("-date") + expenses_by_category = defaultdict(float) expenses_by_month = defaultdict(float) for expense in expenses: expenses_by_category[expense.category] += float(expense.amount) - expenses_by_month[expense.date.strftime('%B %Y')] += float(expense.amount) + expenses_by_month[expense.date.strftime("%B %Y")] += float(expense.amount) grouped_expenses = {} for expense in expenses: - month_name = expense.date.strftime('%B %Y') + month_name = expense.date.strftime("%B %Y") if month_name not in grouped_expenses: grouped_expenses[month_name] = [] grouped_expenses[month_name].append(expense) context = { - 'grouped_expenses': grouped_expenses, - 'expenses_by_category': json.dumps(expenses_by_category), - 'expenses_by_month': json.dumps(expenses_by_month) + "grouped_expenses": grouped_expenses, + "expenses_by_category": json.dumps(expenses_by_category), + "expenses_by_month": json.dumps(expenses_by_month), } - return render(request, 'expenses/index.html', context) + return render(request, "expenses/index.html", context) def add_expense(request): @@ -50,7 +49,6 @@ def add_expense(request): return render(request, "expenses/add_expense.html", {"form": form}) - def edit_expense(request, expense_id): expense = get_object_or_404(Expense, id=expense_id) if request.method == "POST": @@ -77,11 +75,19 @@ def expense_data(request): def search_expenses(request): - query = request.GET.get('query') - results = Expense.objects.filter( - Q(category__icontains=query) | - Q(currency__icontains=query) | - Q(amount__icontains=query) - ) - return render(request, 'expenses/search_results.html', {'results': results}) + query = request.GET.get("query", "") + + if query: + results = Expense.objects.filter( + Q(category__icontains=query) + | Q(currency__icontains=query) + | Q(amount__icontains=query) + ) + else: + results = Expense.objects.none() + context = { + "results": results, + "query": query, + } + return render(request, "expenses/search_results.html", context)