refactor for slice and rule functionality. general cleanup

This commit is contained in:
DJ Gillespie 2020-02-24 19:41:19 -07:00
parent 1e00f31022
commit 9ce52a7cc1
19 changed files with 308 additions and 36 deletions

View File

@ -1,8 +1,8 @@
from django.contrib.auth.models import Group
from django.contrib.auth import get_user_model
from rest_framework import serializers
from qrtr_account.models import Account, Bank, Institution, Transaction
from connection.models import Connection
from qrtr_account.models import Account, Bank, Institution, Transaction, Slice, Rule
from connection.models import Connection, ConnectionType
class UserSerializer(serializers.HyperlinkedModelSerializer):
@ -21,20 +21,92 @@ class GroupSerializer(serializers.HyperlinkedModelSerializer):
class AccountSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Account
fields = ['url','owner', 'name', 'admin_users', 'view_users']
fields = ['url', 'owner', 'name', 'admin_users', 'view_users']
class ConnectionTypeSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = ConnectionType
fields = ['url', 'name', 'filename']
extra_kwargs = {
'name': {'read_only': True},
'filename': {'read_only': True}
}
class ConnectionSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Connection
fields = ['url', 'name', 'type', 'credentials']
extra_kwargs = {
'type': {'write_only': True},
'credentials': {'write_only': True}
}
class BankSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Bank
fields = ['url','qrtr_account', 'connection', 'institution', 'nickname',
'balance', 'ac_type', 'ac_subtype']
fields = [
'url',
'qrtr_account',
'connection',
'institution',
'nickname',
'balance',
'ac_type',
'ac_subtype',
]
extra_kwargs = {
'balance': {'read_only': True},
'connection': {'read_only': True},
'institution': {'read_only': True},
'ac_type': {'read_only': True},
'ac_subtype': {'read_only': True}
}
class BankSerializerPOST(BankSerializer):
"""Separate Serializer for POST requests to create a new bank. This adds
a new field called connection_details that is used to create a new
connection record to go with the new Bank. This field is only allowed on
POST because we don't want to expose this information to the user, or allow
them to change it b/c that could lead to an integrity problem, breaking
their bank functionality.
"""
# connection_details = serializers.JSONField(
# write_only=True,
# required=True,
# initial={
# "type": f"{' OR '.join(ConnectionType.objects.all().values_list('name', flat=True))}",
# "credentials": {}})
class Meta:
model = Bank
fields = [
'url',
'qrtr_account',
'connection',
'institution',
'nickname',
'balance',
'ac_type',
'ac_subtype',
# 'connection_details'
]
extra_kwargs = {
'balance': {'read_only': True},
# 'connection': {'read_only': True},
'institution': {'read_only': True},
'ac_type': {'read_only': True},
'ac_subtype': {'read_only': True}
}
class InstitutionSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Institution
fields = ['url','name']
fields = ['url', 'name']
class TransactionSerializer(serializers.HyperlinkedModelSerializer):
@ -43,7 +115,20 @@ class TransactionSerializer(serializers.HyperlinkedModelSerializer):
fields = ['url', 'datetime', 'Bank', 'details']
class ConnectionSerializer(serializers.HyperlinkedModelSerializer):
class SliceSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Connection
fields = ['url', 'name']
model = Slice
fields = ['url', 'name', 'icon', 'budget', 'slice_of']
class RuleSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Rule
fields = [
'url',
'name',
'kind',
'when_to_run',
'amount',
'source',
'destination']

View File

@ -1,6 +1,10 @@
from django.contrib import admin
from .models import Connection
from .models import Connection, ConnectionType
@admin.register(Connection)
class ConnectionAdmin(admin.ModelAdmin):
pass
@admin.register(ConnectionType)
class ConnectionTypeAdmin(admin.ModelAdmin):
pass

View File

@ -2,9 +2,20 @@ from django.db import models
import jsonfield
class ConnectionType(models.Model):
name = models.CharField(max_length=255)
filename = models.CharField(max_length=255, unique=True)
def __str__(self):
return f"{self.name}"
class Connection(models.Model):
name = models.CharField(max_length=255)
connection_path = models.CharField(max_length=255)
type = models.ForeignKey(
ConnectionType,
on_delete=models.CASCADE,
null=True)
credentials = jsonfield.JSONField()
def __str__(self):

View File

@ -1,3 +1,31 @@
from django.shortcuts import render
from rest_framework import status, viewsets
from rest_framework.response import Response
from .models import Connection
from .serializers import ConnectionSerializer
from rest_framework.decorators import action
import plaid
# Create your views here.
class ConnectionViewSet(viewsets.ModelViewSet):
"""API endpoint that allows connections to be seen or created
"""
queryset = Connection.objects.all()
serializer_class = ConnectionSerializer
# Make connections somewhat immutable from the users perspective
http_method_names = [
'get',
'post',
'delete',
'options']
@action(detail=False, methods=['post'], url_path='oauth/plaid')
def oauth(self, request, public_token=None):
if public_token is None:
return Response(
status=status.HTTP_400_BAD_REQUEST,
data="ERROR: missing public_token")
print(request)
return Response(200)

Binary file not shown.

View File

@ -25,7 +25,7 @@ SECRET_KEY = 'jc@r$_x4$mp-b84&+m3s@hm7kpl$br-wa&50*&xjx^^fddg6q$'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
ALLOWED_HOSTS = ['*']
# Application definition

View File

@ -23,7 +23,9 @@ from qrtr_account.views import (AccountViewSet,
BankViewSet,
InstitutionViewSet,
TransactionViewSet,
ConnectionViewSet)
SliceViewSet,
ConnectionViewSet,
ConnectionTypeViewSet)
router = routers.DefaultRouter()
@ -33,7 +35,9 @@ router.register(r'accounts',AccountViewSet)
router.register(r'banks',BankViewSet)
router.register(r'institutions',InstitutionViewSet)
router.register(r'transactions',TransactionViewSet)
router.register(r'connections',ConnectionViewSet)
router.register(r'slices',SliceViewSet)
#router.register(r'connections',ConnectionViewSet)
router.register(r'connectiontypes',ConnectionTypeViewSet)
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
@ -42,6 +46,7 @@ apipatterns = [
path('', include(router.urls)),
path('auth/', include('rest_framework.urls', namespace='rest_framework'), name='auth'),
path('auth/registration/', include('rest_auth.registration.urls'), name='register'),
path('connection/', include('connection.urls'), name='Connection Settings'),
]
urlpatterns = [

Binary file not shown.

View File

@ -1,3 +1,27 @@
from django.contrib import admin
from .models import Account, Institution, Bank, Transaction, Slice
# Register your models here.
@admin.register(Account)
class AccountAdmin(admin.ModelAdmin):
pass
@admin.register(Institution)
class InstitutionAdmin(admin.ModelAdmin):
pass
@admin.register(Bank)
class BankAdmin(admin.ModelAdmin):
pass
@admin.register(Transaction)
class TransactionAdmin(admin.ModelAdmin):
pass
@admin.register(Slice)
class SliceAdmin(admin.ModelAdmin):
pass

View File

@ -2,4 +2,4 @@ from django.apps import AppConfig
class QrtrAccountConfig(AppConfig):
name = 'qrtr_account'
name = 'QRTR Account'

View File

@ -1,5 +1,7 @@
from django.db import models
from user.models import User
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
import jsonfield
@ -12,13 +14,21 @@ class Account(models.Model):
blank=True)
name = models.CharField(max_length=250)
@property
def qid(self):
return f"A{self.pk}"
def __str__(self):
return f"{self.owner}"
return f"{self.name}"
class Institution(models.Model):
name = models.CharField(max_length=255)
@property
def qid(self):
return f"I{self.pk}"
def __str__(self):
return f"{self.name}"
@ -34,15 +44,77 @@ class Bank(models.Model):
ac_type = models.CharField(max_length=250, blank=True)
ac_subtype = models.CharField(max_length=250, blank=True)
@property
def qid(self):
return f"B{self.pk}"
def __str__(self):
return f"{self.nickname}"
class Slice(models.Model):
name = models.CharField(max_length=250)
icon = models.CharField(max_length=250)
budget = models.DecimalField(decimal_places=3, max_digits=100)
avail_parents = models.Q(
app_label='qrtr_account',
model='bank') | models.Q(
app_label='qrtr_account',
model='slice')
parent_type = models.ForeignKey(
ContentType,
limit_choices_to=avail_parents,
on_delete=models.CASCADE)
parent_id = models.PositiveIntegerField()
slice_of = GenericForeignKey('parent_type', 'parent_id')
@property
def qid(self):
return f"S{self.pk}"
def __str__(self):
return f"{self.name}"
class Schedule(models.Model):
name = models.CharField(max_length=255)
# TODO: Hook this up to an events system for Payday scheduling
class Rule(models.Model):
kinds = [("refill", "Refill"), ("increase", "Increase"), ("goal", "Goal")]
kind = models.CharField(choices=kinds, max_length=255)
when_to_run = models.ForeignKey(Schedule, on_delete=models.CASCADE)
amount_type = models.CharField(
choices=[
("quantity",
"Quantity"),
("round",
"Round"),
("percent",
"Percent")],
default="quantity",
max_length=20)
amount = models.DecimalField(decimal_places=3, max_digits=100)
source = models.ForeignKey(
Slice,
on_delete=models.CASCADE,
related_name="rule_source_set")
destination = models.ForeignKey(
Slice,
on_delete=models.CASCADE,
related_name="rule_destination_set")
class Transaction(models.Model):
datetime = models.DateTimeField()
Bank = models.ForeignKey(Bank, on_delete=models.CASCADE,
related_name='transactions')
details = jsonfield.JSONField()
@property
def qid(self):
return f"T{self.pk}"
def __str__(self):
return f"{self.Bank} - {self.datetime}"

View File

@ -1,12 +1,15 @@
from django.shortcuts import render
from rest_framework import viewsets
from .models import Account, Bank, Institution, Transaction
from connection.models import Connection
from rest_framework import viewsets, mixins
from .models import Account, Bank, Institution, Transaction, Slice, Rule
from connection.models import Connection, ConnectionType
from api.serializers import (AccountSerializer,
BankSerializer,
BankSerializer, BankSerializerPOST,
InstitutionSerializer,
TransactionSerializer,
ConnectionSerializer)
ConnectionSerializer,
ConnectionTypeSerializer,
SliceSerializer,
RuleSerializer)
class AccountViewSet(viewsets.ModelViewSet):
@ -20,20 +23,55 @@ class BankViewSet(viewsets.ModelViewSet):
"""API endpoint that allows Banks to be viewed or edited
"""
queryset = Bank.objects.all()
serializer_class = BankSerializer
# serializer_class = BankSerializer
class InstitutionViewSet(viewsets.ModelViewSet):
"""API endpoint that allows Banks to be viewed or edited
def get_serializer_class(self):
if self.action == 'create':
return BankSerializerPOST
return BankSerializer
class InstitutionViewSet(viewsets.ReadOnlyModelViewSet):
"""API endpoint that allows Banks to be viewed.
"""
queryset = Institution.objects.all()
serializer_class = InstitutionSerializer
class TransactionViewSet(viewsets.ModelViewSet):
"""API endpoint that allows Banks to be viewed or edited
class TransactionViewSet(viewsets.ReadOnlyModelViewSet):
"""API endpoint that allows Banks to be viewed.
"""
queryset = Transaction.objects.all()
serializer_class = TransactionSerializer
class ConnectionTypeViewSet(viewsets.ModelViewSet):
queryset = ConnectionType.objects.all()
serializer_class = ConnectionTypeSerializer
class ConnectionViewSet(viewsets.ModelViewSet):
"""API endpoint that allows connections to be seen or created
"""
queryset = Connection.objects.all()
serializer_class = ConnectionSerializer
# Make connections somewhat immutable from the users perspective
http_method_names = [
'get',
'post',
'delete',
'options']
class SliceViewSet(viewsets.ReadOnlyModelViewSet):
"""API endpoint that allows Banks to be viewed.
"""
queryset = Slice.objects.all()
serializer_class = SliceSerializer
class RuleViewSet(viewsets.ReadOnlyModelViewSet):
"""API endpoint that allows Banks to be viewed.
"""
queryset = Rule.objects.all()
serializer_class = RuleSerializer

View File

@ -3,3 +3,4 @@ django-rest-framework==0.1.0
djangorestframework==3.10.3
pytz==2019.3
sqlparse==0.3.0
plaid-python>=3.0.0

View File

@ -4,5 +4,9 @@ from django.db import models
class User(AbstractUser):
name = models.CharField(blank=True, max_length=255)
@property
def qid(self):
return f"U{self.pk}"
def __str__(self):
return self.email

View File

@ -12,7 +12,7 @@ class UserViewSet(viewsets.ModelViewSet):
serializer_class = UserSerializer
class GroupViewSet(viewsets.ModelViewSet):
class GroupViewSet(viewsets.ReadOnlyModelViewSet):
"""
API endpoint that allows groups to be viewed or edited.
"""