From c076e1b9634fdd0a1d237461efe66660fb53a957 Mon Sep 17 00:00:00 2001 From: Peter Date: Mon, 9 Oct 2023 04:03:52 +0800 Subject: [PATCH] add log categories and fix notifications --- expenses/admin.py | 25 +++++++- ...category_historicallogcategory_and_more.py | 60 +++++++++++++++++++ expenses/models.py | 15 +++++ expenses/static/js/todo.js | 4 +- expenses/static/logs.css | 13 ++-- 5 files changed, 108 insertions(+), 9 deletions(-) create mode 100644 expenses/migrations/0011_logcategory_historicallogcategory_and_more.py diff --git a/expenses/admin.py b/expenses/admin.py index 8d09dd8..5e0b6a8 100644 --- a/expenses/admin.py +++ b/expenses/admin.py @@ -9,6 +9,7 @@ from .models import ( Log, Expense, ExpenseCategory, + LogCategory, Timesheet, TimesheetRate, Todo, @@ -91,6 +92,7 @@ class LogsAdmin(AdminBase): "truncated_title", "date", "goodness_value", + "category", ) list_display_links = ( "title", @@ -99,6 +101,11 @@ class LogsAdmin(AdminBase): search_fields = ( "title", "date", + "category", + ) + list_filter = ( + "date", + # "category", ) form = LogsAdminForm @@ -352,4 +359,20 @@ class ExpenseAdmin(AdminBase, AdminChartMixin, ImportExportModelAdmin): @admin.register(ExpenseCategory) class ExpenseCategoryAdmin(AdminBase): - search_fields = ("name",) + list_display = ( + "name", + "description", + ) + search_fields = list_display + + +@admin.register(LogCategory) +class LogCategoryAdmin(AdminBase): + list_display = ( + "name", + "description", + ) + search_fields = list_display + + class Media: + css = {"all": ("/static/logs.css",)} diff --git a/expenses/migrations/0011_logcategory_historicallogcategory_and_more.py b/expenses/migrations/0011_logcategory_historicallogcategory_and_more.py new file mode 100644 index 0000000..bbf0d82 --- /dev/null +++ b/expenses/migrations/0011_logcategory_historicallogcategory_and_more.py @@ -0,0 +1,60 @@ +# Generated by Django 4.2.5 on 2023-10-08 14:39 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import simple_history.models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('expenses', '0010_todo_historicaltodo'), + ] + + operations = [ + migrations.CreateModel( + name='LogCategory', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('deleted', models.BooleanField(default=False)), + ('name', models.CharField(blank=True, max_length=256)), + ('description', models.TextField(blank=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='HistoricalLogCategory', + fields=[ + ('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), + ('deleted', models.BooleanField(default=False)), + ('name', models.CharField(blank=True, max_length=256)), + ('description', models.TextField(blank=True)), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField(db_index=True)), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'historical log category', + 'verbose_name_plural': 'historical log categorys', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': ('history_date', 'history_id'), + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.AddField( + model_name='historicallog', + name='category', + field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='expenses.logcategory'), + ), + migrations.AddField( + model_name='log', + name='category', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='logs', to='expenses.logcategory'), + ), + ] diff --git a/expenses/models.py b/expenses/models.py index 07cc0ba..988e8df 100644 --- a/expenses/models.py +++ b/expenses/models.py @@ -41,6 +41,14 @@ class Todo(DeletableModel): return f"{self.title} [{self.due_date}]" +class LogCategory(DeletableModel): + name = models.CharField(max_length=256, blank=True) + description = models.TextField(blank=True) + + def __str__(self): + return truncate_string(self.name, 48) + + class Log(DeletableModel): # TODO: Use Markdown formatting! title = models.CharField(max_length=256, blank=True) @@ -53,6 +61,13 @@ class Log(DeletableModel): ), ) information = models.TextField(blank=True) + category = models.ForeignKey( + LogCategory, + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name="logs", + ) def __str__(self): return f"{self.date}: {self.title}" diff --git a/expenses/static/js/todo.js b/expenses/static/js/todo.js index d0517f4..b1f24b4 100644 --- a/expenses/static/js/todo.js +++ b/expenses/static/js/todo.js @@ -63,7 +63,9 @@ function parseDateString(dateString) { } const alerted = new Set(); -const deletedParam = urlParams.get("deleted__exact"); +const url = new URL(window.location.href); +const urlParams = new URLSearchParams(url.search); +const deletedParam = urlParams.get("deleted__exact") || false; // DO NOT NOTIFY FOR DELETED ITEMS if (deletedParam !== "1") { diff --git a/expenses/static/logs.css b/expenses/static/logs.css index 798bda4..65118d0 100644 --- a/expenses/static/logs.css +++ b/expenses/static/logs.css @@ -1,11 +1,10 @@ /* Hide title of log when mouse is not hovering over it for privacy */ -.field-title:not(:hover) > a { - color: transparent; - text-decoration: none; - cursor: pointer; -} - -.field-goodness_value:not(:hover) { +.field-title:not(:hover) > a, +.field-goodness_value:not(:hover), +.field-name:not(:hover) > a, +.field-category:not(:hover), +.field-description:not(:hover) { + transition: none; color: transparent; text-decoration: none; cursor: pointer;