Browse Source

DIV-1152: Add API to upload documents

pull/170/head
ariannedee 5 years ago
parent
commit
43d24eea05
10 changed files with 162 additions and 72 deletions
  1. +33
    -0
      edivorce/apps/core/migrations/0021_document.py
  2. +61
    -6
      edivorce/apps/core/models.py
  3. +27
    -1
      edivorce/apps/core/serializer.py
  4. +1
    -2
      edivorce/apps/core/tests/test_storage.py
  5. +7
    -2
      edivorce/apps/core/urls.py
  6. +15
    -3
      edivorce/apps/core/views/api.py
  7. +16
    -0
      edivorce/apps/poc/migrations/0010_delete_document.py
  8. +0
    -56
      edivorce/apps/poc/models.py
  9. +1
    -1
      edivorce/apps/poc/views.py
  10. +1
    -1
      vue/src/components/Uploader/Uploader.vue

+ 33
- 0
edivorce/apps/core/migrations/0021_document.py View File

@ -0,0 +1,33 @@
# Generated by Django 2.2.15 on 2020-09-24 00:37
from django.db import migrations, models
import django.db.models.deletion
import edivorce.apps.core.redis
class Migration(migrations.Migration):
dependencies = [
('core', '0020_auto_20200903_2328'),
]
operations = [
migrations.CreateModel(
name='Document',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('filename', models.CharField(max_length=128, null=True)),
('size', models.IntegerField(default=0)),
('file', models.FileField(storage=edivorce.apps.core.redis.RedisStorage(), upload_to=edivorce.apps.core.redis.generate_unique_filename)),
('doc_type', models.CharField(blank=True, max_length=4, null=True)),
('party_code', models.IntegerField(default=0)),
('sort_order', models.IntegerField(default=1)),
('rotation', models.IntegerField(default=0)),
('date_uploaded', models.DateTimeField(auto_now_add=True)),
('bceid_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='uploads', to='core.BceidUser')),
],
options={
'unique_together': {('bceid_user', 'doc_type', 'party_code', 'filename', 'size')},
},
),
]

+ 61
- 6
edivorce/apps/core/models.py View File

@ -3,6 +3,8 @@ from django.db import models
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible from django.utils.encoding import python_2_unicode_compatible
from edivorce.apps.core import redis
@python_2_unicode_compatible @python_2_unicode_compatible
class BceidUser(models.Model): class BceidUser(models.Model):
@ -109,6 +111,58 @@ class UserResponse(models.Model):
return '%s -> %s' % (self.bceid_user, self.question.key) return '%s -> %s' % (self.bceid_user, self.question.key)
class Document(models.Model):
"""
This is only a POC model and should not be loaded on a production system.
"""
filename = models.CharField(max_length=128, null=True) # saving the original filename separately
""" File name and extension """
size = models.IntegerField(default=0)
""" Size of the file (size and name uniquely identify each file on the input) """
file = models.FileField(upload_to=redis.generate_unique_filename, storage=redis.RedisStorage())
""" File temporarily stored in Redis """
doc_type = models.CharField(max_length=4, null=True, blank=True)
""" CEIS Document Type Code (2-4 letters) """
party_code = models.IntegerField(default=0)
""" 1 = You, 2 = Your Spouse, 0 = Shared """
sort_order = models.IntegerField(default=1)
""" file order (page number in the PDF) """
rotation = models.IntegerField(default=0)
""" 0, 90, 180 or 270 """
bceid_user = models.ForeignKey(BceidUser, related_name='uploads', on_delete=models.CASCADE)
""" User who uploaded the attachment """
date_uploaded = models.DateTimeField(auto_now_add=True)
""" Date the record was last updated """
class Meta:
unique_together = ("bceid_user", "doc_type", "party_code", "filename", "size")
def save(self, *args, **kwargs):
self.filename = self.file.name
self.size = self.file.size
super(Document, self).save(*args, **kwargs)
def delete(self, **kwargs):
"""
Override delete so we can delete the Redis object when this instance is deleted.
"""
self.file.delete(save=False)
super(Document, self).delete(**kwargs)
def str(self):
return f'User {self.bceid_user.display_name}: {self.filename} ({self.doc_type} - {self.party_code})'
class DontLog: class DontLog:
def log_addition(self, *args): def log_addition(self, *args):
return return
@ -120,7 +174,11 @@ class DontLog:
return return
class UserResponseAdmin(DontLog, admin.ModelAdmin):
class BaseAdmin(DontLog, admin.ModelAdmin):
pass
class UserResponseAdmin(BaseAdmin):
list_display = ['get_user_name', 'question', 'value'] list_display = ['get_user_name', 'question', 'value']
def get_user_name(self, obj): def get_user_name(self, obj):
@ -130,10 +188,7 @@ class UserResponseAdmin(DontLog, admin.ModelAdmin):
get_user_name.short_description = 'User' get_user_name.short_description = 'User'
class QuestionAdmin(DontLog, admin.ModelAdmin):
pass
admin.site.register(BceidUser) admin.site.register(BceidUser)
admin.site.register(Question, QuestionAdmin)
admin.site.register(Question, BaseAdmin)
admin.site.register(UserResponse, UserResponseAdmin) admin.site.register(UserResponse, UserResponseAdmin)
admin.site.register(Document, BaseAdmin)

+ 27
- 1
edivorce/apps/core/serializer.py View File

@ -1,5 +1,8 @@
from django.db import IntegrityError
from rest_framework import serializers from rest_framework import serializers
from .models import UserResponse
from rest_framework.exceptions import ValidationError
from .models import Document, UserResponse
class UserResponseSerializer(serializers.ModelSerializer): class UserResponseSerializer(serializers.ModelSerializer):
@ -20,4 +23,27 @@ class UserResponseSerializer(serializers.ModelSerializer):
instance.save() instance.save()
class DocumentSerializer(serializers.ModelSerializer):
doc_type = serializers.CharField()
party_code = serializers.IntegerField()
file = serializers.FileField()
filename = serializers.CharField(read_only=True)
size = serializers.IntegerField(read_only=True)
rotation = serializers.IntegerField(read_only=True)
sort_order = serializers.IntegerField(read_only=True)
class Meta:
model = Document
fields = ('file', 'doc_type', 'party_code', 'filename', 'size', 'rotation', 'sort_order')
def create(self, validated_data):
filename = validated_data['file'].name
size = validated_data['file'].size
user = self.context['request'].user
order = Document.objects.filter(bceid_user=user, doc_type=validated_data['doc_type'], party_code=validated_data['party_code']).count() + 1
response = Document(bceid_user=user, filename=filename, size=size, sort_order=order, **validated_data)
try:
response.save()
except IntegrityError:
raise ValidationError("You have already uploaded that file")
return response

+ 1
- 2
edivorce/apps/core/tests/test_storage.py View File

@ -4,9 +4,8 @@ from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TransactionTestCase from django.test import TransactionTestCase
from redis.exceptions import ConnectionError from redis.exceptions import ConnectionError
from edivorce.apps.core.models import BceidUser
from edivorce.apps.core.models import BceidUser, Document
from edivorce.apps.core.redis import generate_unique_filename from edivorce.apps.core.redis import generate_unique_filename
from edivorce.apps.poc.models import Document
class UploadStorageTests(TransactionTestCase): class UploadStorageTests(TransactionTestCase):


+ 7
- 2
edivorce/apps/core/urls.py View File

@ -1,7 +1,8 @@
from django.conf.urls import url from django.conf.urls import url
from rest_framework.routers import DefaultRouter
from .views import main, system, pdf, api, localdev from .views import main, system, pdf, api, localdev
from .views.api import DocumentViewSet
urlpatterns = [ urlpatterns = [
# url(r'^guide$', styleguide.guide), # url(r'^guide$', styleguide.guide),
@ -32,4 +33,8 @@ urlpatterns = [
url(r'^question/(?P<step>.*)$', main.question, name="question_steps"), url(r'^question/(?P<step>.*)$', main.question, name="question_steps"),
url(r'^current$', system.current, name="current"), url(r'^current$', system.current, name="current"),
url(r'^$', main.home, name="home"), url(r'^$', main.home, name="home"),
]
]
router = DefaultRouter()
router.register(r'api/documents', DocumentViewSet, basename='document')
urlpatterns += router.urls

+ 15
- 3
edivorce/apps/core/views/api.py View File

@ -1,9 +1,10 @@
from rest_framework import status
from rest_framework import permissions, status
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from ..models import Question
from ..serializer import UserResponseSerializer
from ..models import Document, Question
from ..serializer import DocumentSerializer, UserResponseSerializer
from ..utils.question_step_mapping import question_step_mapping from ..utils.question_step_mapping import question_step_mapping
from ..utils.user_response import save_to_session, save_to_db from ..utils.user_response import save_to_session, save_to_db
@ -47,3 +48,14 @@ class UserResponseHandler(APIView):
return Response(status=status.HTTP_500_INTERNAL_ERROR) return Response(status=status.HTTP_500_INTERNAL_ERROR)
return Response(status=status.HTTP_200_OK) return Response(status=status.HTTP_200_OK)
class DocumentViewSet(ModelViewSet):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
queryset = Document.objects.all()
serializer_class = DocumentSerializer
def get_queryset(self):
if self.request.user.is_anonymous:
return Document.objects.none()
return Document.objects.filter(bceid_user=self.request.user)

+ 16
- 0
edivorce/apps/poc/migrations/0010_delete_document.py View File

@ -0,0 +1,16 @@
# Generated by Django 2.2.15 on 2020-09-24 00:37
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('poc', '0009_auto_20200922_1033'),
]
operations = [
migrations.DeleteModel(
name='Document',
),
]

+ 0
- 56
edivorce/apps/poc/models.py View File

@ -1,56 +0,0 @@
from django.db import models
from django.conf import settings
from edivorce.apps.core.models import BceidUser
from edivorce.apps.core import redis
class Document(models.Model):
"""
This is only a POC model and should not be loaded on a production system.
"""
filename = models.CharField(max_length=128, null=True) # saving the original filename separately
""" File name and extension """
size = models.IntegerField(default=0)
""" Size of the file (size and name uniquely identify each file on the input) """
file = models.FileField(upload_to=redis.generate_unique_filename, storage=redis.RedisStorage())
""" File temporarily stored in Redis """
doc_type = models.CharField(max_length=4, null=True, blank=True)
""" CEIS Document Type Code (2-4 letters) """
party_code = models.IntegerField(default=0)
""" 1 = You, 2 = Your Spouse, 0 = Shared """
sort_order = models.IntegerField(default=1)
""" file order (page number in the PDF) """
rotation = models.IntegerField(default=0)
""" 0, 90, 180 or 270 """
bceid_user = models.ForeignKey(BceidUser, related_name='uploads', on_delete=models.CASCADE)
""" User who uploaded the attachment """
date_uploaded = models.DateTimeField(auto_now_add=True)
""" Date the record was last updated """
class Meta:
unique_together = ("bceid_user", "doc_type", "party_code", "filename", "size")
def save(self, *args, **kwargs):
self.filename = self.file.name
self.size = self.file.size
super(Document, self).save(*args, **kwargs)
def delete(self, **kwargs):
"""
Override delete so we can delete the Redis object when this instance is deleted.
:param kwargs:
:return:
"""
self.file.delete(save=False)
super(Document, self).delete(**kwargs)

+ 1
- 1
edivorce/apps/poc/views.py View File

@ -10,7 +10,7 @@ from django.conf import settings
from edivorce.apps.core.efilinghub import EFilingHub, PACKAGE_PARTY_FORMAT from edivorce.apps.core.efilinghub import EFilingHub, PACKAGE_PARTY_FORMAT
from edivorce.apps.core.validators import file_scan_validation from edivorce.apps.core.validators import file_scan_validation
from edivorce.apps.poc.models import Document
from edivorce.apps.core.models import Document
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)


+ 1
- 1
vue/src/components/Uploader/Uploader.vue View File

@ -126,7 +126,7 @@ export default {
return FormDefinitions[this.docType]; return FormDefinitions[this.docType];
}, },
postAction() { postAction() {
return this.$parent.proxyRootPath + "poc/storage"
return this.$parent.proxyRootPath + "api/documents/"
}, },
uniqueId() { uniqueId() {
if (this.party === 0) { if (this.party === 0) {


Loading…
Cancel
Save