1
1

seperate subjects/transaction on a user basis

This commit is contained in:
2021-01-18 23:35:10 +01:00
parent 49ef36fbce
commit 0447f9f6ef
7 changed files with 107 additions and 97 deletions

View File

@@ -1,21 +1,43 @@
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from core.models import Subject, Transaction
User = get_user_model()
class CustomAdminSite(admin.AdminSite):
pass
admin_site = CustomAdminSite()
admin_site.register(User, UserAdmin)
@admin.register(Subject, site=admin_site)
class SubjectAdmin(admin.ModelAdmin):
pass
list_display = ("name",)
search_fields = ("name",)
exclude = ("user",)
def save_model(self, request, obj, form, change):
if not change:
obj.user = request.user
return super().save_model(request, obj, form, change)
def get_search_results(self, request, queryset, search_term):
queryset, use_distinct = super().get_search_results(request, queryset, search_term)
return queryset.filter(user=request.user), use_distinct
@admin.register(Transaction, site=admin_site)
class TransactionAdmin(admin.ModelAdmin):
list_display = ("subject", "amount", "booking_date",)
ordering = ("-booking_date",)
search_fields = ("subject__name",)
autocomplete_fields = ("subject",)
def get_search_results(self, request, queryset, search_term):
queryset, use_distinct = super().get_search_results(request, queryset, search_term)
return queryset.filter(subject__user=request.user), use_distinct

View File

@@ -0,0 +1,33 @@
# Generated by Django 3.1.5 on 2021-01-18 22:34
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('core', '0001_initial'),
]
operations = [
migrations.DeleteModel(
name='Balance',
),
migrations.AddField(
model_name='subject',
name='user',
field=models.ForeignKey(default=1, editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='subjects', related_query_name='subject', to='auth.user'),
preserve_default=False,
),
migrations.AlterUniqueTogether(
name='subject',
unique_together={('user', 'name')},
),
migrations.RemoveField(
model_name='subject',
name='created_time',
),
]

View File

@@ -1,7 +1,9 @@
from decimal import Decimal
from django.contrib.auth import get_user_model
from django.db import models
from django.utils import timezone
User = get_user_model()
class PriceField(models.DecimalField):
@@ -22,8 +24,14 @@ class PriceField(models.DecimalField):
class Subject(models.Model):
class Meta:
unique_together = ("user", "name")
user = models.ForeignKey(
to=User, on_delete=models.CASCADE, editable=False,
related_name="subjects", related_query_name="subject",
)
name = models.CharField(max_length=64)
created_time = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.name
@@ -39,11 +47,3 @@ class Transaction(models.Model):
to=Subject, on_delete=models.CASCADE,
related_name="transactions", related_query_name="transaction",
)
class Balance(models.Model):
class Meta:
get_latest_by = "date"
amount = PriceField()
date = models.DateField()

View File

@@ -150,8 +150,8 @@ def get_color_for_amount(amount: Decimal, minimum: Decimal, maximum: Decimal):
)
def predict_balance(start_balance=Decimal("0")):
prediction_list = predict_all(Subject.objects.order_by("name"))
def predict_balance(subjects: Iterable[Subject], start_balance=Decimal("0")):
prediction_list = predict_all(subjects)
today = timezone.now().date()
future_transactions = []
minimum, maximum = Decimal(), Decimal()

View File

@@ -1,32 +1,37 @@
from datetime import date
from django.contrib.auth import get_user_model
from django.test import TestCase
from core.models import Subject, Transaction
from core.prediction import predict_transactions
User = get_user_model()
class PredictionTestCase(TestCase):
def setUp(self):
self.user = User.objects.create(username="dummy")
self.subject = Subject.objects.create(user=self.user, name="rent")
def test_not_enough_data(self):
subject = Subject.objects.create(name="rent")
predicted_transactions, prediction_info = predict_transactions(subject)
predicted_transactions, prediction_info = predict_transactions(self.subject)
self.assertEqual(predicted_transactions, [])
self.assertIsNone(prediction_info)
Transaction.objects.create(amount=-333, booking_date=date(2020, 6, 2), subject=subject)
predicted_transactions, prediction_info = predict_transactions(subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 6, 2), subject=self.subject)
predicted_transactions, prediction_info = predict_transactions(self.subject)
self.assertEqual(predicted_transactions, [])
self.assertIsNone(prediction_info)
Transaction.objects.create(amount=-333, booking_date=date(2020, 7, 2), subject=subject)
predicted_transactions, prediction_info = predict_transactions(subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 7, 2), subject=self.subject)
predicted_transactions, prediction_info = predict_transactions(self.subject)
self.assertNotEqual(predicted_transactions, [])
self.assertIsNotNone(prediction_info)
def test_every_month(self):
subject = Subject.objects.create(name="rent")
Transaction.objects.create(amount=-333, booking_date=date(2020, 6, 2), subject=subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 7, 2), subject=subject)
predicted_transactions, prediction_info = predict_transactions(subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 6, 2), subject=self.subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 7, 2), subject=self.subject)
predicted_transactions, prediction_info = predict_transactions(self.subject)
first, second, last = predicted_transactions[0], predicted_transactions[1], predicted_transactions[-1]
self.assertEqual(first.booking_date, date(2020, 8, 2))
self.assertEqual(second.booking_date, date(2020, 9, 2))
@@ -39,10 +44,9 @@ class PredictionTestCase(TestCase):
)
def test_every_3_months(self):
subject = Subject.objects.create(name="rent")
Transaction.objects.create(amount=-333, booking_date=date(2020, 6, 2), subject=subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 9, 2), subject=subject)
predicted_transactions, prediction_info = predict_transactions(subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 6, 2), subject=self.subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 9, 2), subject=self.subject)
predicted_transactions, prediction_info = predict_transactions(self.subject)
first, second, last = predicted_transactions[0], predicted_transactions[1], predicted_transactions[-1]
self.assertEqual(first.booking_date, date(2020, 12, 2))
self.assertEqual(second.booking_date, date(2021, 3, 2))
@@ -55,10 +59,9 @@ class PredictionTestCase(TestCase):
)
def test_every_year(self):
subject = Subject.objects.create(name="rent")
Transaction.objects.create(amount=-333, booking_date=date(2019, 6, 2), subject=subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 6, 2), subject=subject)
predicted_transactions, prediction_info = predict_transactions(subject)
Transaction.objects.create(amount=-333, booking_date=date(2019, 6, 2), subject=self.subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 6, 2), subject=self.subject)
predicted_transactions, prediction_info = predict_transactions(self.subject)
self.assertEqual(len(predicted_transactions), 1)
trans = predicted_transactions[0]
self.assertEqual(trans.booking_date, date(2021, 6, 2))
@@ -69,12 +72,11 @@ class PredictionTestCase(TestCase):
)
def test_monthly_varying_begin_end(self):
subject = Subject.objects.create(name="rent")
Transaction.objects.create(amount=-333, booking_date=date(2020, 1, 31), subject=subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 3, 1), subject=subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 4, 1), subject=subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 4, 30), subject=subject)
predicted_transactions, prediction_info = predict_transactions(subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 1, 31), subject=self.subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 3, 1), subject=self.subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 4, 1), subject=self.subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 4, 30), subject=self.subject)
predicted_transactions, prediction_info = predict_transactions(self.subject)
first, second, last = predicted_transactions[0], predicted_transactions[1], predicted_transactions[-1]
self.assertEqual(first.booking_date, date(2020, 5, 30))
self.assertEqual(second.booking_date, date(2020, 6, 30))
@@ -87,12 +89,11 @@ class PredictionTestCase(TestCase):
)
def test_no_monthly_pattern(self):
subject = Subject.objects.create(name="rent")
Transaction.objects.create(amount=-333, booking_date=date(2020, 1, 15), subject=subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 2, 10), subject=subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 4, 25), subject=subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 5, 5), subject=subject)
predicted_transactions, prediction_info = predict_transactions(subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 1, 15), subject=self.subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 2, 10), subject=self.subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 4, 25), subject=self.subject)
Transaction.objects.create(amount=-333, booking_date=date(2020, 5, 5), subject=self.subject)
predicted_transactions, prediction_info = predict_transactions(self.subject)
first, second, last = predicted_transactions[0], predicted_transactions[1], predicted_transactions[-1]
self.assertEqual(first.booking_date, date(2020, 6, 11))
self.assertEqual(second.booking_date, date(2020, 7, 18))
@@ -105,10 +106,9 @@ class PredictionTestCase(TestCase):
)
def test_amount_change(self):
subject = Subject.objects.create(name="rent")
Transaction.objects.create(amount=1337, booking_date=date(2020, 6, 2), subject=subject)
Transaction.objects.create(amount=1312, booking_date=date(2020, 7, 2), subject=subject)
Transaction.objects.create(amount=404, booking_date=date(2020, 8, 2), subject=subject)
predicted_transactions, prediction_info = predict_transactions(subject)
Transaction.objects.create(amount=1337, booking_date=date(2020, 6, 2), subject=self.subject)
Transaction.objects.create(amount=1312, booking_date=date(2020, 7, 2), subject=self.subject)
Transaction.objects.create(amount=404, booking_date=date(2020, 8, 2), subject=self.subject)
predicted_transactions, prediction_info = predict_transactions(self.subject)
for tr in predicted_transactions:
self.assertEqual(tr.amount, 404)

View File

@@ -1,47 +0,0 @@
from decimal import Decimal, InvalidOperation
from django.test import TestCase
from django.utils import timezone
from core.models import Balance
def _create_and_reload_balance(amount):
bal = Balance.objects.create(amount=amount, date=timezone.now().date())
bal.refresh_from_db()
return bal
class PriceFieldTestCase(TestCase):
def test_normal_decimal(self):
bal = _create_and_reload_balance(Decimal("13.37"))
self.assertEqual(bal.amount, Decimal("13.37"))
def test_too_big_decimal(self):
with self.assertRaises(InvalidOperation):
_create_and_reload_balance(Decimal("13371337.00"))
def test_rounding_decimal(self):
bal = _create_and_reload_balance(Decimal("1.337"))
self.assertEqual(bal.amount, Decimal("1.34"))
def test_normal_int(self):
bal = _create_and_reload_balance(1337)
self.assertEqual(bal.amount, Decimal("1337.00"))
def test_too_big_int(self):
with self.assertRaises(InvalidOperation):
_create_and_reload_balance(13371337)
def test_normal_float(self):
bal = _create_and_reload_balance(13.37)
self.assertEqual(bal.amount, Decimal("13.37"))
def test_too_big_float(self):
with self.assertRaises(InvalidOperation):
_create_and_reload_balance(13371337.00)
def test_rounding_float(self):
bal = _create_and_reload_balance(1.337)
self.assertEqual(bal.amount, Decimal("1.34"))

View File

@@ -11,6 +11,7 @@ class BalanceView(TemplateView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
subjects = Subject.objects.filter(user=self.request.user).order_by("name")
amount, amount_error = Decimal(), False
if amount_query := self.request.GET.get("amount"):
try:
@@ -19,7 +20,7 @@ class BalanceView(TemplateView):
amount_error = True
context["amount"] = round(amount, 2)
context["amount_error"] = amount_error
context["future_transactions"] = predict_balance(amount)
context["future_transactions"] = predict_balance(subjects, amount)
return context
@@ -28,5 +29,6 @@ class SubjectsView(TemplateView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["prediction_list"] = predict_all(Subject.objects.order_by("name"))
subjects = Subject.objects.filter(user=self.request.user).order_by("name")
context["prediction_list"] = predict_all(subjects)
return context