diff --git a/penparse/webui/migrations/0004_imagememo_status.py b/penparse/webui/migrations/0004_imagememo_status.py
new file mode 100644
index 0000000..b101864
--- /dev/null
+++ b/penparse/webui/migrations/0004_imagememo_status.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.2.16 on 2024-12-09 14:12
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('webui', '0003_imagememo_image_mimetype_alter_imagememo_image'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='imagememo',
+ name='status',
+ field=models.CharField(choices=[('pending', 'Pending'), ('processing', 'Processing'), ('done', 'Done'), ('error', 'Error')], default='pending', max_length=10),
+ ),
+ ]
diff --git a/penparse/webui/models.py b/penparse/webui/models.py
index 0b5f4b2..750e42c 100644
--- a/penparse/webui/models.py
+++ b/penparse/webui/models.py
@@ -38,6 +38,12 @@ class UserManager(BaseUserManager):
return self._create_user(email, password, **extra_fields)
+class MemoStatus(models.TextChoices):
+ Pending = "pending"
+ Processing = "processing"
+ Done = "done"
+ Error = "error"
+
class ImageMemo(models.Model):
"""Model definition for ImageMemo."""
@@ -54,6 +60,9 @@ class ImageMemo(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
+ status = models.CharField(max_length=10, choices=MemoStatus.choices, default=MemoStatus.Pending)
+
+
class Meta:
ordering = ["-created_at"]
diff --git a/penparse/webui/templates/dashboard.html b/penparse/webui/templates/dashboard.html
index eb0aeef..e1f2df7 100644
--- a/penparse/webui/templates/dashboard.html
+++ b/penparse/webui/templates/dashboard.html
@@ -13,11 +13,27 @@
{% for document in documents %}
-
-
-
{{ document.title }}
+
+
+
+
{{ document.title }}
+
+ {{ document.status|title }}
+
+
- Analyzed on: {{ document.analysis_date }}
+ Created: {{ document.created_at }}
+
+
+ Last Updated: {{ document.updated_at }}
{% empty %}
@@ -47,15 +70,31 @@
Upload a New Document
-
diff --git a/penparse/webui/test/test_delete_doc_view.py b/penparse/webui/test/test_delete_doc_view.py
new file mode 100644
index 0000000..e69de29
diff --git a/penparse/webui/test/test_delete_document.py b/penparse/webui/test/test_delete_document.py
new file mode 100644
index 0000000..850238c
--- /dev/null
+++ b/penparse/webui/test/test_delete_document.py
@@ -0,0 +1,74 @@
+import pytest
+import uuid
+from django.core.files.uploadedfile import SimpleUploadedFile
+from django.urls import reverse
+from django.core.files.storage import default_storage
+
+from ..models import ImageMemo, User
+
+
+@pytest.mark.django_db
+class TestDeleteDocument:
+
+ @pytest.fixture
+ def setup_users(self):
+ self.user1 = User.objects.create_user(
+ email="user1@test.com", password="password1"
+ )
+ self.user2 = User.objects.create_user(
+ email="user2@test.com", password="password2"
+ )
+
+ @pytest.fixture
+ def setup_document(self, setup_users):
+ img_content = b"fake image content"
+ test_image = SimpleUploadedFile(
+ name="test_image.jpg", content=img_content, content_type="image/jpeg"
+ )
+ self.image_memo = ImageMemo.objects.create(
+ author=self.user1,
+ image=test_image,
+ )
+
+ def test_delete_document_success(self, client, setup_document):
+ client.force_login(self.user1)
+ url = reverse("delete_document", kwargs={"pk": str(self.image_memo.id)})
+ response = client.post(url)
+
+ assert response.status_code == 302
+ assert response.url == reverse("dashboard")
+ assert not ImageMemo.objects.filter(id=self.image_memo.id).exists()
+ assert not default_storage.exists(self.image_memo.image.name)
+
+ def test_delete_nonexistent_document(self, client, setup_users):
+ client.force_login(self.user1)
+ url = reverse(
+ "delete_document", kwargs={"pk": "facade00-0000-4000-a000-000000000000"}
+ )
+ response = client.post(url)
+
+ assert response.status_code == 404
+ assert response.content == b"Document not found"
+
+ def test_delete_other_users_document(self, client, setup_document):
+ client.force_login(self.user2)
+ url = reverse("delete_document", kwargs={"pk": str(self.image_memo.id)})
+ response = client.post(url)
+
+ assert response.status_code == 404
+ assert response.content == b"Document not found"
+ assert ImageMemo.objects.filter(id=self.image_memo.id).exists()
+
+ def test_delete_document_missing_file(self, client, setup_document):
+ # Remove the file manually
+ default_storage.delete(self.image_memo.image.name)
+
+ client.force_login(self.user1)
+ url = reverse("delete_document", kwargs={"pk": str(self.image_memo.id)})
+ response = client.post(url)
+
+ assert response.status_code == 302
+ assert response.url == reverse("dashboard")
+ assert not ImageMemo.objects.filter(id=self.image_memo.id).exists()
+
+ # ...
diff --git a/penparse/webui/urls.py b/penparse/webui/urls.py
index 29d02a7..cbe6e83 100644
--- a/penparse/webui/urls.py
+++ b/penparse/webui/urls.py
@@ -9,10 +9,13 @@ urlpatterns = [
path("documents/upload", views.upload_document, name="upload_document"),
path("documents/", views.view_document, name="view_document"),
path(
- "documents//thumbnail", views.document_thumbnail, name="document_thumbnail"
+ "documents//thumbnail",
+ views.document_thumbnail,
+ name="document_thumbnail",
),
path(
"documents//download", views.download_document, name="download_document"
),
+ path("documents//delete", views.delete_document, name="delete_document"),
path("auth/register", views.register, name="register"),
]
diff --git a/penparse/webui/views/__init__.py b/penparse/webui/views/__init__.py
index 2facf45..3c6d87f 100644
--- a/penparse/webui/views/__init__.py
+++ b/penparse/webui/views/__init__.py
@@ -16,6 +16,7 @@ logger = logging.getLogger(__name__)
from .thumbnail import document_thumbnail
from .register import register
from .upload import upload_document
+from .delete import delete_document
def index(request):
@@ -32,6 +33,7 @@ __all__ = [
"view_document",
"download_document",
"upload_document",
+ "delete_document",
]
diff --git a/penparse/webui/views/delete.py b/penparse/webui/views/delete.py
index f405024..ef98d8e 100644
--- a/penparse/webui/views/delete.py
+++ b/penparse/webui/views/delete.py
@@ -1,5 +1,4 @@
-import logging
-import os
+from loguru import logger
from django.contrib import messages
from django.shortcuts import redirect
@@ -12,9 +11,6 @@ from django.http import HttpRequest, HttpResponse
from django.contrib.auth.decorators import login_required
-logger = logging.getLogger(__name__)
-
-
@login_required
def delete_document(request: HttpRequest, pk: str):
@@ -26,6 +22,14 @@ def delete_document(request: HttpRequest, pk: str):
return HttpResponse(content="Document not found", status=404)
# delete file from storage
- default_storage.delete(document.image.name)
+ if default_storage.exists(document.image.name):
+ default_storage.delete(document.image.name)
+ else:
+ logger.warning(
+ "File {document.image.name} associated with doc {document.id} did not exist when we tried to delete it"
+ )
+
+ # delete document from database
+ document.delete()
return redirect("dashboard")