vmi-virtual-memorial/vmi-wd-backend / 4faa21c

Browse files

add support for class letters

Authored by espadonne
SHA
4faa21c1e980925b0a8c72bdb411528ff39f6206
Parents
b063067
Tree
d1b15ad

4 changed files

StatusFile+-
M memorial/admin.py 1 1
A memorial/migrations/0006_person_class_letter.py 23 0
M memorial/models.py 20 4
M memorial/serializers.py 9 9
memorial/admin.pymodified
@@ -89,7 +89,7 @@ class PersonAdmin(admin.ModelAdmin):
8989
             'fields': ('first_name', 'middle_name', 'last_name', 'suffix')
9090
         }),
9191
         ('VMI Information', {
92
-            'fields': ('class_year',)
92
+            'fields': ('class_year', 'class_letter')
9393
         }),
9494
         ('Military Information', {
9595
             'fields': ('conflict', 'rank', 'unit')
memorial/migrations/0006_person_class_letter.pyadded
@@ -0,0 +1,23 @@
1
+# Generated by Django 5.0.6 on 2025-10-16 03:29
2
+
3
+from django.db import migrations, models
4
+
5
+
6
+class Migration(migrations.Migration):
7
+
8
+    dependencies = [
9
+        ("memorial", "0005_contribution"),
10
+    ]
11
+
12
+    operations = [
13
+        migrations.AddField(
14
+            model_name="person",
15
+            name="class_letter",
16
+            field=models.CharField(
17
+                blank=True,
18
+                default="",
19
+                help_text="Optional letter suffix for class year (e.g., 'M' for 1956M)",
20
+                max_length=1,
21
+            ),
22
+        ),
23
+    ]
memorial/models.pymodified
@@ -34,10 +34,16 @@ class Person(models.Model):
3434
     
3535
     # VMI info
3636
     class_year = models.IntegerField(
37
-        null=True, 
37
+        null=True,
3838
         blank=True,
3939
         help_text="VMI graduation year (e.g., 1965)"
4040
     )
41
+    class_letter = models.CharField(
42
+        max_length=1,
43
+        blank=True,
44
+        default='',
45
+        help_text="Optional letter suffix for class year (e.g., 'M' for 1956M)"
46
+    )
4147
     
4248
     # Military info
4349
     conflict = models.ForeignKey(
@@ -78,7 +84,17 @@ class Person(models.Model):
7884
     # Metadata
7985
     created_at = models.DateTimeField(auto_now_add=True)
8086
     updated_at = models.DateTimeField(auto_now=True)
81
-    
87
+
88
+    def clean(self):
89
+        """Validate and normalize class_letter"""
90
+        from django.core.exceptions import ValidationError
91
+        if self.class_letter:
92
+            self.class_letter = self.class_letter.upper().strip()
93
+            if len(self.class_letter) > 1 or not self.class_letter.isalpha():
94
+                raise ValidationError({
95
+                    'class_letter': 'Must be a single letter (A-Z)'
96
+                })
97
+
8298
     class Meta:
8399
         ordering = ['last_name', 'first_name']
84100
         verbose_name_plural = "People"
@@ -90,7 +106,7 @@ class Person(models.Model):
90106
         if self.suffix:
91107
             full_name = f"{full_name} {self.suffix}"
92108
         if self.class_year:
93
-            full_name = f"{full_name} '{str(self.class_year)[2:]}"  # e.g., John Doe '65
109
+            full_name = f"{full_name} '{str(self.class_year)[2:]}{self.class_letter}"  # e.g., John Doe '65M
94110
         return full_name
95111
     
96112
     @property
@@ -112,7 +128,7 @@ class Person(models.Model):
112128
         """Display name with class year"""
113129
         name = self.display_name
114130
         if self.class_year:
115
-            name = f"{name} '{str(self.class_year)[2:]}"
131
+            name = f"{name} '{str(self.class_year)[2:]}{self.class_letter}"
116132
         return name
117133
     
118134
     @property
memorial/serializers.pymodified
@@ -7,10 +7,10 @@ class PersonListSerializer(serializers.ModelSerializer):
77
     display_name = serializers.ReadOnlyField()
88
     full_display_name = serializers.ReadOnlyField()
99
     death_date_display = serializers.ReadOnlyField()
10
-    
10
+
1111
     class Meta:
1212
         model = Person
13
-        fields = ['id', 'display_name', 'full_display_name', 'rank', 'unit', 'class_year', 'death_description', 'death_date_display']
13
+        fields = ['id', 'display_name', 'full_display_name', 'rank', 'unit', 'class_year', 'class_letter', 'death_description', 'death_date_display']
1414
 
1515
 
1616
 class PersonDetailSerializer(serializers.ModelSerializer):
@@ -20,14 +20,14 @@ class PersonDetailSerializer(serializers.ModelSerializer):
2020
     conflict_name = serializers.CharField(source='conflict.name', read_only=True)
2121
     pdf_url = serializers.SerializerMethodField()
2222
     death_date_display = serializers.ReadOnlyField()
23
-    
23
+
2424
     class Meta:
2525
         model = Person
2626
         fields = [
2727
             'id', 'first_name', 'middle_name', 'last_name', 'suffix',
28
-            'display_name', 'full_display_name', 'class_year', 'rank', 'unit', 
29
-            'date_of_death', 'death_date_precision', 'death_date_display', 
30
-            'death_description', 'conflict', 'conflict_name', 
28
+            'display_name', 'full_display_name', 'class_year', 'class_letter', 'rank', 'unit',
29
+            'date_of_death', 'death_date_precision', 'death_date_display',
30
+            'death_description', 'conflict', 'conflict_name',
3131
             'pdf_key', 'pdf_url'
3232
         ]
3333
     
@@ -46,12 +46,12 @@ class PersonSearchSerializer(serializers.ModelSerializer):
4646
     conflict_name = serializers.CharField(source='conflict.name', read_only=True)
4747
     conflict_id = serializers.IntegerField(source='conflict.id', read_only=True)
4848
     death_date_display = serializers.ReadOnlyField()
49
-    
49
+
5050
     class Meta:
5151
         model = Person
5252
         fields = [
53
-            'id', 'display_name', 'full_display_name', 'class_year',
54
-            'rank', 'unit', 'date_of_death', 'death_date_display', 
53
+            'id', 'display_name', 'full_display_name', 'class_year', 'class_letter',
54
+            'rank', 'unit', 'date_of_death', 'death_date_display',
5555
             'conflict_name', 'conflict_id'
5656
         ]
5757