• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gm3dmo / cmp / 13540659929

26 Feb 2025 09:28AM UTC coverage: 58.092% (-1.9%) from 59.952%
13540659929

push

github

web-flow
Merge pull request #299 from gm3dmo/provost

Adding provost officer form

17 of 87 new or added lines in 3 files covered. (19.54%)

1 existing line in 1 file now uncovered.

761 of 1310 relevant lines covered (58.09%)

1.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.79
/cmp/forms.py
1
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
2✔
2

3
from django import forms
2✔
4

5
from .models import CustomUser
2✔
6

7
from .models import Country
2✔
8
from .models import Rank
2✔
9
from .models import Cemetery
2✔
10
from .models import PowCamp
2✔
11
from .models import Soldier
2✔
12
from .models import SoldierDeath
2✔
13
from .models import SoldierImprisonment
2✔
14
from .models import SoldierDecoration
2✔
15
from .models import Company
2✔
16
from .models import Decoration
2✔
17
from .models import Acknowledgement
2✔
18
from .models import ProvostAppointment
2✔
19
from django.forms import inlineformset_factory
2✔
20

21
from crispy_forms.helper import FormHelper
2✔
22
from crispy_forms.layout import Layout, Field
2✔
23
from crispy_forms.bootstrap import Accordion, AccordionGroup, TabHolder, Tab
2✔
24

25

26
class SoldierImprisonmentForm(forms.ModelForm):
2✔
27
    date_from = forms.DateField(
2✔
28
        initial='1940-01-01',
29
        widget=forms.DateInput(
30
            attrs={
31
                'type': 'date',
32
                'class': 'form-control',
33
                'style': 'width: 20%;'
34
            }
35
        ),
36
        required=False
37
    )
38
    date_to = forms.DateField(
2✔
39
        initial='1940-01-01',
40
        widget=forms.DateInput(
41
            attrs={
42
                'type': 'date',
43
                'class': 'form-control',
44
                'style': 'width: 20%;'
45
            }
46
        ),
47
        required=False
48
    )
49

50
    class Meta:
2✔
51
        model = SoldierImprisonment
2✔
52
        fields = ['pow_camp', 'pow_number', 'date_from', 'date_to', 'notes']
2✔
53
        widgets = {
2✔
54
            'date_to': forms.DateInput(
55
                attrs={
56
                    'type': 'date',
57
                    'class': 'form-control'
58
                }
59
            )
60
        }
61

62
    def __init__(self, *args, **kwargs):
2✔
63
        super().__init__(*args, **kwargs)
×
64
        self.fields['pow_camp'].required = False
×
65
        self.fields['pow_number'].required = False
×
66
        self.fields['date_to'].required = False
×
67
        self.fields['notes'].required = False
×
68

69
class SoldierImprisonmentFormSetHelper(FormHelper):
2✔
70
    def __init__(self, *args, **kwargs):
2✔
71
        super().__init__(*args, **kwargs)
×
72
        self.form_tag = False
×
73
        
74
        # Default to collapsed
75
        has_data = False
×
76
        title = 'Prisoner of War Details (None Recorded)'
×
77
        
78
        self.layout = Layout(
×
79
            Accordion(
80
                AccordionGroup(
81
                    title,
82
                    'pow_camp',
83
                    'pow_number',
84
                    'date_from',
85
                    'date_to',
86
                    'notes',
87
                    active=has_data,  # Collapsed by default
88
                    css_class='bg-info bg-opacity-25 border rounded p-3'
89
                ),
90
                css_id="imprisonment-details-accordion"
91
            )
92
        )
93

94
    def update_title(self):
2✔
95
        """Update the title based on formset data"""
96
        if hasattr(self, 'formset') and self.formset.initial_forms:
×
97
            has_data = any(form.initial for form in self.formset.initial_forms)
×
98
            title = 'Prisoner of War Details' if has_data else 'Prisoner of War Details (None Recorded)'
×
99
            self.layout[0][0].name = title
×
100
            self.layout[0][0].active = has_data
×
101

102
# Create the formset
103
SoldierImprisonmentInlineFormSet = inlineformset_factory(
2✔
104
    Soldier,
105
    SoldierImprisonment,
106
    form=SoldierImprisonmentForm,
107
    extra=1,
108
    can_delete=True
109
)
110

111
class SoldierImprisonmentFormSetWithHelper(SoldierImprisonmentInlineFormSet):
2✔
112
    def __init__(self, *args, **kwargs):
2✔
113
        super().__init__(*args, **kwargs)
×
114
        self.helper = SoldierImprisonmentFormSetHelper()
×
115
        self.helper.formset = self
×
116
        self.helper.update_title()
×
117

118

119
class CustomUserCreationForm(UserCreationForm):
2✔
120
    class Meta:
2✔
121
        model = CustomUser
2✔
122
        fields = ("email",)
2✔
123

124

125
class CustomUserChangeForm(UserChangeForm):
2✔
126
    class Meta:
2✔
127
        model = CustomUser
2✔
128
        fields = ("email",)
2✔
129

130

131
class editPowCampForm(forms.ModelForm):
2✔
132
    def __init__(self, *args, **kwargs):
2✔
133
        super().__init__(*args, **kwargs)
×
134
        self.helper = FormHelper()
×
135
        
136
        # Determine header class and active state based on whether form has data
137
        header_class = 'bg-light' if self.instance and self.instance.pk else 'bg-light-blue'
×
138
        is_active = bool(self.instance and self.instance.pk)
×
139
        
140
        self.helper.layout = Layout(
×
141
            Field('name'),
142
            Accordion(
143
                AccordionGroup(
144
                    'POW Details',
145
                    Field('nearest_city'),
146
                    Field('notes'),
147
                    Field('wartime_country'),
148
                    Field('latitude'),
149
                    Field('longitude'),
150
                    active=is_active,
151
                    button_class=header_class
152
                ),
153
                css_id="powcamp-details-accordion"
154
            )
155
        )
156

157
    name = forms.CharField(
2✔
158
        widget=forms.TextInput(attrs={
159
            'class': 'wide-input'
160
        })
161
    )
162
    nearest_city = forms.CharField(
2✔
163
        widget=forms.TextInput(attrs={
164
            'class': 'wide-input'
165
        }),
166
        required=False
167
    )
168
    notes = forms.CharField(
2✔
169
        widget=forms.Textarea(attrs={
170
            'class': 'wide-input'
171
        }),
172
        required=False
173
    )
174
    wartime_country = forms.CharField(
2✔
175
        widget=forms.TextInput(attrs={
176
            'class': 'wide-input'
177
        }),
178
        required=False
179
    )
180
    latitude = forms.CharField(
2✔
181
        widget=forms.TextInput(attrs={
182
            'class': 'wide-input'
183
        }),
184
        required=False
185
    )
186
    longitude = forms.CharField(
2✔
187
        widget=forms.TextInput(attrs={
188
            'class': 'wide-input'
189
        }),
190
        required=False
191
    )
192

193
    class Meta:
2✔
194
        model = PowCamp
2✔
195
        fields = '__all__'
2✔
196

197

198
class editCemeteryForm(forms.ModelForm):
2✔
199
    name = forms.CharField(
2✔
200
        widget=forms.TextInput(attrs={
201
            'class': 'wide-input',
202
            'style': 'width: 500px;'
203
        })
204
    )
205
    country = forms.ModelChoiceField(
2✔
206
        queryset=Country.objects.all().order_by('name'),
207
        empty_label="Select a country"
208
    )
209

210
    class Meta:
2✔
211
        model = Cemetery
2✔
212
        fields = ['name', 'country', 'latitude', 'longitude']
2✔
213

214

215
class editCountryForm(forms.ModelForm):
2✔
216
    def __init__(self, *args, **kwargs):
2✔
217
        super().__init__(*args, **kwargs)
×
218
        self.helper = FormHelper()
×
219
        self.helper.label_class = 'form-label'  
×
220
    class Meta:
2✔
221
        model = Country
2✔
222
        fields = "__all__"
2✔
223

224
class AcknowledgementForm(forms.ModelForm):
2✔
225
    def __init__(self, *args, **kwargs):
2✔
226
        super().__init__(*args, **kwargs)
2✔
227
        self.helper = FormHelper()
2✔
228
        self.helper.label_class = 'form-label'  
2✔
229
    class Meta:
2✔
230
        model = Acknowledgement
2✔
231
        created_at = forms.DateTimeField(disabled=True, required=False)
2✔
232
        exclude = ['created_at']  # This will hide created_at from the form
2✔
233
        fields = '__all__'
2✔
234

235

236
class editCompanyForm(forms.ModelForm):
2✔
237
    def __init__(self, *args, **kwargs):
2✔
238
        super().__init__(*args, **kwargs)
×
239
        self.helper = FormHelper()
×
240
        self.helper.label_class = 'form-label'  
×
241
    class Meta:
2✔
242
        model = Company 
2✔
243
        fields = "__all__"
2✔
244

245
class editDecorationForm(forms.ModelForm):
2✔
246
    def __init__(self, *args, **kwargs):
2✔
247
        super().__init__(*args, **kwargs)
×
248
        self.helper = FormHelper()
×
249
        self.helper.label_class = 'form-label'  
×
250
    class Meta:
2✔
251
        model = Decoration
2✔
252
        fields = "__all__"
2✔
253

254

255
class editRankForm(forms.ModelForm):
2✔
256
    name = forms.CharField(
2✔
257
        widget=forms.TextInput(attrs={
258
            'class': 'wide-input',
259
        })
260
    )
261

262
    class Meta:
2✔
263
        model = Rank
2✔
264
        fields = '__all__'
2✔
265

266

267
class editSoldierDeathForm(forms.ModelForm):
2✔
268
    company = forms.ModelChoiceField(
2✔
269
        queryset=Company.objects.all()
270
    )
271

272
    class Meta:
2✔
273
        model = SoldierDeath
2✔
274
        fields = ['date', 'company', 'cemetery', 'cwgc_id', 'image']
2✔
275
        widgets = {
2✔
276
            'date': forms.DateInput(
277
                attrs={
278
                    'type': 'date',
279
                    'class': 'form-control',
280
                    'style': 'width: 20%;'
281
                }
282
            )
283
        }
284
        labels = {
2✔
285
            'image': 'Grave Photograph'
286
        }
287

288
    def __init__(self, *args, **kwargs):
2✔
289
        super().__init__(*args, **kwargs)
×
290
        self.helper = SoldierDeathFormHelper()
×
291
        self.helper.form = self
×
292
        self.helper.update_title()
×
293

294

295
class editSoldierForm(forms.ModelForm):
2✔
296
    def __init__(self, *args, **kwargs):
2✔
297
        super().__init__(*args, **kwargs)
×
298
        self.helper = FormHelper()
×
299
        self.helper.label_class = 'form-label'  
×
300
        self.fields['provost_officer'].disabled = True
×
301

302
        # Determine header class and active state based on whether form has data
303
        header_class = 'bg-light' if self.instance and self.instance.pk else 'bg-light-blue'
×
304
        is_active = bool(self.instance and self.instance.pk)
×
305
        
306
        # Check if there are any imprisonment records
307
        has_imprisonment = False
×
308
        if self.instance and self.instance.pk:
×
309
            has_imprisonment = SoldierImprisonment.objects.filter(soldier=self.instance).exists()
×
310
        
311
        # Set title based on whether there's data
312
        title = 'Prisoner of War Details' if has_imprisonment else 'Prisoner of War Details (None Recorded)'
×
313
        
314
        self.helper.layout = Layout(
×
315
            Field('surname'),
316
            Field('initials'),
317
            Field('army_number'),
318
            Field('rank'),
319
            Field('provost_officer'),
320
            Field('notes'),
321
            Accordion(
322
                AccordionGroup(
323
                    title,
324
                    'imprisonment_formset',
325
                    active=has_imprisonment,
326
                    button_class=header_class
327
                ),
328
                css_id="imprisonment-details-accordion"
329
            )
330
        )
331
        # Initialize the formset
332
        self.imprisonment_formset = SoldierImprisonmentInlineFormSet(
×
333
            instance=self.instance,
334
            prefix='imprisonment'
335
        )
336

337
    class Meta:
2✔
338
        model = Soldier
2✔
339
        exclude = ['created_at']  # Exclude the created_at field
2✔
340

341

342
class ProvostOfficerSearchForm(forms.Form):
2✔
343
    q = forms.CharField(
2✔
344
        required=False,
345
        label='Search',
346
        widget=forms.TextInput(attrs={
347
            'class': 'form-control',
348
            'placeholder': 'Search by surname or army number...'
349
        })
350
    )
351

352
class ProvostOfficerForm(forms.ModelForm):
2✔
353
    def __init__(self, *args, **kwargs):
2✔
354
        super().__init__(*args, **kwargs)
×
355
        self.helper = FormHelper()
×
NEW
356
        self.helper.label_class = 'form-label'
×
357
        
358
        # Filter rank choices to only show officer ranks
UNCOV
359
        self.fields['rank'].queryset = Rank.objects.filter(rank_class="OF").order_by('name')
×
360
        
361
        # Set provost_officer field
NEW
362
        self.fields['provost_officer'] = forms.BooleanField(
×
363
            initial=True,
364
            disabled=True,
365
            required=False,
366
            help_text="All officers created through this form are automatically marked as Provost Officers"
367
        )
368
        
369
        # Determine header class and active state
NEW
370
        header_class = 'bg-light' if self.instance and self.instance.pk else 'bg-light-blue'
×
NEW
371
        is_active = bool(self.instance and self.instance.pk)
×
372
        
NEW
373
        self.helper.layout = Layout(
×
374
            Field('surname'),
375
            Field('initials'),
376
            Field('army_number'),
377
            Field('rank'),
378
            Field('provost_officer'),
379
            Accordion(
380
                AccordionGroup(
381
                    'Appointment Details',
382
                    'appointment_formset',
383
                    active=is_active,
384
                    button_class=header_class
385
                ),
386
                css_id="appointment-details-accordion"
387
            ),
388
            Field('notes')
389
        )
390

391
    class Meta:
2✔
392
        model = Soldier
2✔
393
        fields = ['surname', 'initials', 'army_number', 'rank', 'notes']
2✔
394
        widgets = {
2✔
395
            'notes': forms.Textarea(attrs={'rows': 3}),
396
        }
397

398
    def save(self, commit=True):
2✔
399
        soldier = super().save(commit=False)
×
NEW
400
        soldier.provost_officer = True
×
401
        if commit:
×
402
            soldier.save()
×
403
        return soldier
×
404

405
class ProvostAppointmentForm(forms.ModelForm):
2✔
406
    def __init__(self, *args, **kwargs):
2✔
407
        super().__init__(*args, **kwargs)
×
408
        self.helper = FormHelper()
×
NEW
409
        self.helper.label_class = 'form-label'
×
410
        
411
        # Filter rank choices to only show officer ranks
NEW
412
        self.fields['rank'].queryset = Rank.objects.filter(rank_class="OF").order_by('name')
×
413
        
NEW
414
        self.helper.layout = Layout(
×
415
            Field('rank'),
416
            Field('date'),
417
            Field('notes')
418
        )
419

420
    date = forms.DateField(
2✔
421
        widget=forms.DateInput(
422
            attrs={
423
                'type': 'date',
424
                'class': 'form-control',
425
                'style': 'width: 20%;'
426
            }
427
        ),
428
        required=False
429
    )
430

431
    class Meta:
2✔
432
        model = ProvostAppointment
2✔
433
        fields = ['rank', 'date', 'notes']
2✔
434
        widgets = {
2✔
435
            'notes': forms.Textarea(attrs={'rows': 3}),
436
        }
437

438
# Create the formset
439
ProvostAppointmentInlineFormSet = inlineformset_factory(
2✔
440
    Soldier,
441
    ProvostAppointment,
442
    form=ProvostAppointmentForm,
443
    extra=1,
444
    can_delete=True
445
)
446

447
class ProvostAppointmentFormSetHelper(FormHelper):
2✔
448
    def __init__(self, *args, **kwargs):
2✔
449
        super().__init__(*args, **kwargs)
×
NEW
450
        self.form_tag = False
×
451
        
452
        # Default to collapsed
NEW
453
        has_data = False
×
NEW
454
        title = 'Appointment Details (None Recorded)'
×
455
        
NEW
456
        self.layout = Layout(
×
457
            Accordion(
458
                AccordionGroup(
459
                    title,
460
                    'rank',
461
                    'date',
462
                    'notes',
463
                    active=has_data,
464
                    css_class='bg-info bg-opacity-25 border rounded p-3'
465
                ),
466
                css_id="appointment-details-accordion"
467
            )
468
        )
469

470
    def update_title(self):
2✔
NEW
471
        if hasattr(self, 'formset') and self.formset.initial_forms:
×
NEW
472
            has_data = any(form.initial for form in self.formset.initial_forms)
×
NEW
473
            title = 'Appointment Details' if has_data else 'Appointment Details (None Recorded)'
×
NEW
474
            self.layout[0][0].name = title
×
NEW
475
            self.layout[0][0].active = has_data
×
476

477
class ProvostAppointmentFormSetWithHelper(ProvostAppointmentInlineFormSet):
2✔
478
    def __init__(self, *args, **kwargs):
2✔
NEW
479
        super().__init__(*args, **kwargs)
×
NEW
480
        self.helper = ProvostAppointmentFormSetHelper()
×
NEW
481
        self.helper.formset = self
×
NEW
482
        self.helper.update_title()
×
483

484

485
class SoldierDecorationForm(forms.ModelForm):
2✔
486
    country = forms.ModelChoiceField(
2✔
487
        queryset=Country.objects.all(),
488
    )
489

490
    class Meta:
2✔
491
        model = SoldierDecoration
2✔
492
        fields = ['decoration', 'gazette_issue', 'gazette_page', 'gazette_date', 'theatre', 'country', 'citation', 'notes']
2✔
493
        widgets = {
2✔
494
            'gazette_date': forms.DateInput(
495
                attrs={
496
                    'type': 'date',
497
                    'class': 'form-control',
498
                    'style': 'width: 20%;'
499
                }
500
            )
501
        }
502

503
class SoldierDecorationFormSetHelper(FormHelper):
2✔
504
    def __init__(self, *args, **kwargs):
2✔
505
        super().__init__(*args, **kwargs)
×
506
        self.form_tag = False
×
507
        
508
        # Default to collapsed
509
        has_data = False
×
510
        title = 'Decoration Details (None Recorded)'
×
511
        
512
        self.layout = Layout(
×
513
            Accordion(
514
                AccordionGroup(
515
                    title,
516
                    'decoration',
517
                    'gazette_issue',
518
                    'gazette_page',
519
                    'gazette_date',
520
                    'theatre',
521
                    'country',
522
                    'citation',
523
                    'notes',
524
                    active=has_data,
525
                    css_class='bg-info bg-opacity-25 border rounded p-3'
526
                ),
527
                css_id="decoration-details-accordion"
528
            )
529
        )
530

531
    def update_title(self):
2✔
532
        if hasattr(self, 'formset') and self.formset.initial_forms:
×
533
            has_data = any(form.initial for form in self.formset.initial_forms)
×
534
            title = 'Decoration Details' if has_data else 'Decoration Details (None Recorded)'
×
535
            self.layout[0][0].name = title
×
536
            self.layout[0][0].active = has_data
×
537

538
# Create the formset
539
SoldierDecorationInlineFormSet = inlineformset_factory(
2✔
540
    Soldier,
541
    SoldierDecoration,
542
    form=SoldierDecorationForm,
543
    extra=1,
544
    can_delete=True
545
)
546

547
# Add the helper to the formset
548
class SoldierDecorationFormSetWithHelper(SoldierDecorationInlineFormSet):
2✔
549
    def __init__(self, *args, **kwargs):
2✔
550
        super().__init__(*args, **kwargs)
×
551
        self.helper = SoldierDecorationFormSetHelper()
×
552
        self.helper.formset = self
×
553
        self.helper.update_title()
×
554

555

556
class SoldierDeathFormHelper(FormHelper):
2✔
557
    def __init__(self, *args, **kwargs):
2✔
558
        super().__init__(*args, **kwargs)
×
559
        self.form_tag = False
×
560
        
561
        # Default to collapsed
562
        has_data = False
×
563
        title = 'Death Details (None Recorded)'
×
564
        
565
        self.layout = Layout(
×
566
            Accordion(
567
                AccordionGroup(
568
                    title,
569
                    'date',
570
                    'company',
571
                    'cemetery',
572
                    'cwgc_id',
573
                    'image',
574
                    active=has_data,  # Collapsed by default
575
                    css_class='bg-info bg-opacity-25 border rounded p-3'
576
                ),
577
                css_id="death-details-accordion"
578
            )
579
        )
580

581
    def update_title(self):
2✔
582
        """Update the title based on form data"""
583
        if hasattr(self, 'form') and self.form.initial:
×
584
            has_data = any(self.form.initial.values())
×
585
            title = 'Death Details' if has_data else 'Death Details (None Recorded)'
×
586
            self.layout[0][0].name = title
×
587
            self.layout[0][0].active = has_data
×
588

589

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc