seperate subjects/transaction on a user basis
This commit is contained in:
@@ -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
|
||||
|
||||
33
core/migrations/0002_auto_20210118_2334.py
Normal file
33
core/migrations/0002_auto_20210118_2334.py
Normal 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',
|
||||
),
|
||||
]
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"))
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user