vmi-virtual-memorial/vmi-wd-backend / 3241eb7

Browse files

prep for service deploy

Authored by espadonne
SHA
3241eb7ad43cc49f2e15bc418b374c1df7df74a4
Parents
71bd215
Tree
92723b7

8 changed files

StatusFile+-
M .gitignore 1 0
A Procfile 1 0
A memorial/serializers.py 58 0
A memorial/urls.py 11 0
M memorial/views.py 55 2
A requirements.txt 19 0
A runtime.txt 1 0
M vmi_wardead_be/urls.py 5 20
.gitignoremodified
@@ -3,3 +3,4 @@ __pycache__/
3
 *.pyc
3
 *.pyc
4
 .env
4
 .env
5
 db.sqlite3
5
 db.sqlite3
6
+staticfiles/
Procfileadded
@@ -0,0 +1,1 @@
1
+web: python manage.py migrate && python manage.py collectstatic --noinput && gunicorn vmi_wardead_be.wsgi
memorial/serializers.pyadded
@@ -0,0 +1,58 @@
1
+from rest_framework import serializers
2
+from .models import Conflict, Person
3
+
4
+
5
+class PersonListSerializer(serializers.ModelSerializer):
6
+    """Lightweight serializer for listing people"""
7
+    display_name = serializers.ReadOnlyField()
8
+    
9
+    class Meta:
10
+        model = Person
11
+        fields = ['id', 'display_name', 'rank', 'unit']
12
+
13
+
14
+class PersonDetailSerializer(serializers.ModelSerializer):
15
+    """Full details for individual person"""
16
+    display_name = serializers.ReadOnlyField()
17
+    conflict_name = serializers.CharField(source='conflict.name', read_only=True)
18
+    pdf_url = serializers.SerializerMethodField()
19
+    
20
+    class Meta:
21
+        model = Person
22
+        fields = [
23
+            'id', 'first_name', 'middle_name', 'last_name', 'suffix',
24
+            'display_name', 'rank', 'unit', 'date_of_death',
25
+            'conflict', 'conflict_name', 'pdf_key', 'pdf_url'
26
+        ]
27
+    
28
+    def get_pdf_url(self, obj):
29
+        """Generate S3 URL or placeholder"""
30
+        if obj.pdf_key:
31
+            # TODO: Generate actual S3 presigned URL
32
+            return f"/api/memorial/persons/{obj.id}/pdf/"
33
+        return None
34
+
35
+
36
+class ConflictSerializer(serializers.ModelSerializer):
37
+    """Conflict with casualty count"""
38
+    casualty_count = serializers.ReadOnlyField()
39
+    
40
+    class Meta:
41
+        model = Conflict
42
+        fields = [
43
+            'id', 'name', 'start_year', 'end_year', 
44
+            'description', 'casualty_count', 'order'
45
+        ]
46
+
47
+
48
+class ConflictDetailSerializer(serializers.ModelSerializer):
49
+    """Conflict with list of casualties"""
50
+    casualty_count = serializers.ReadOnlyField()
51
+    casualties = PersonListSerializer(many=True, read_only=True)
52
+    
53
+    class Meta:
54
+        model = Conflict
55
+        fields = [
56
+            'id', 'name', 'start_year', 'end_year', 
57
+            'description', 'casualty_count', 'order', 'casualties'
58
+        ]
memorial/urls.pyadded
@@ -0,0 +1,11 @@
1
+from django.urls import path, include
2
+from rest_framework.routers import DefaultRouter
3
+from .views import ConflictViewSet, PersonViewSet
4
+
5
+router = DefaultRouter()
6
+router.register('conflicts', ConflictViewSet)
7
+router.register('persons', PersonViewSet)
8
+
9
+urlpatterns = [
10
+    path('', include(router.urls)),
11
+]
memorial/views.pymodified
@@ -1,3 +1,56 @@
1
-from django.shortcuts import render
1
+from rest_framework import viewsets, status
2
+from rest_framework.decorators import action
3
+from rest_framework.response import Response
4
+from django.http import HttpResponse
5
+from .models import Conflict, Person
6
+from .serializers import (
7
+    ConflictSerializer, ConflictDetailSerializer,
8
+    PersonListSerializer, PersonDetailSerializer
9
+)
2
 
10
 
3
-# Create your views here.
11
+
12
+class ConflictViewSet(viewsets.ReadOnlyModelViewSet):
13
+    """API endpoints for conflicts"""
14
+    queryset = Conflict.objects.all()
15
+    
16
+    def get_serializer_class(self):
17
+        if self.action == 'retrieve':
18
+            return ConflictDetailSerializer
19
+        return ConflictSerializer
20
+
21
+
22
+class PersonViewSet(viewsets.ReadOnlyModelViewSet):
23
+    """API endpoints for people"""
24
+    queryset = Person.objects.all()
25
+    
26
+    def get_serializer_class(self):
27
+        if self.action == 'list':
28
+            return PersonListSerializer
29
+        return PersonDetailSerializer
30
+    
31
+    def get_queryset(self):
32
+        queryset = super().get_queryset()
33
+        conflict_id = self.request.query_params.get('conflict', None)
34
+        if conflict_id is not None:
35
+            queryset = queryset.filter(conflict_id=conflict_id)
36
+        return queryset
37
+    
38
+    @action(detail=True, methods=['get'])
39
+    def pdf(self, request, pk=None):
40
+        """Get PDF for a person (placeholder for now)"""
41
+        person = self.get_object()
42
+        
43
+        if not person.pdf_key:
44
+            return Response(
45
+                {"error": "No PDF available for this person"}, 
46
+                status=status.HTTP_404_NOT_FOUND
47
+            )
48
+        
49
+        # TODO: Generate S3 presigned URL and redirect
50
+        # For now, return a placeholder response
51
+        return HttpResponse(
52
+            f"PDF placeholder for {person.display_name}\n"
53
+            f"S3 Key: {person.pdf_key}\n"
54
+            f"This will redirect to S3 presigned URL in production",
55
+            content_type="text/plain"
56
+        )
requirements.txtadded
@@ -0,0 +1,19 @@
1
+asgiref==3.9.1
2
+boto3==1.34.0
3
+botocore==1.34.162
4
+dj-database-url==3.0.1
5
+Django==5.0.6
6
+django-cors-headers==4.3.1
7
+django-storages==1.14.3
8
+djangorestframework==3.15.1
9
+gunicorn==21.2.0
10
+jmespath==1.0.1
11
+packaging==25.0
12
+psycopg2-binary==2.9.9
13
+python-dateutil==2.9.0.post0
14
+python-decouple==3.8
15
+s3transfer==0.9.0
16
+six==1.17.0
17
+sqlparse==0.5.3
18
+urllib3==2.5.0
19
+whitenoise==6.6.0
runtime.txtadded
@@ -0,0 +1,1 @@
1
+python-3.11.8
vmi_wardead_be/urls.pymodified
@@ -1,23 +1,8 @@
1
-"""
2
-URL configuration for vmi_wardead_be project.
3
-
4
-The `urlpatterns` list routes URLs to views. For more information please see:
5
-    https://docs.djangoproject.com/en/5.0/topics/http/urls/
6
-Examples:
7
-Function views
8
-    1. Add an import:  from my_app import views
9
-    2. Add a URL to urlpatterns:  path('', views.home, name='home')
10
-Class-based views
11
-    1. Add an import:  from other_app.views import Home
12
-    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
13
-Including another URLconf
14
-    1. Import the include() function: from django.urls import include, path
15
-    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
16
-"""
17
-
18
 from django.contrib import admin
1
 from django.contrib import admin
19
-from django.urls import path
2
+from django.urls import path, include
20
 
3
 
21
 urlpatterns = [
4
 urlpatterns = [
22
-    path("admin/", admin.site.urls),
5
+    path('admin/', admin.site.urls),
23
-]
6
+    path('api/memorial/', include('memorial.urls')),
7
+    path('api-auth/', include('rest_framework.urls')),  # For browsable API
8
+]