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

gm3dmo / cmp / 13715903360

07 Mar 2025 07:29AM UTC coverage: 63.113% (+5.4%) from 57.744%
13715903360

push

github

web-flow
Merge pull request #307 from gm3dmo/gm3dmo-patch-1

Update cmp-test.yml

888 of 1407 relevant lines covered (63.11%)

1.26 hits per line

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

56.15
/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, Submit
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
    class Meta:
2✔
269
        model = SoldierDeath
2✔
270
        fields = ['date', 'company', 'cemetery', 'cwgc_id', 'image']
2✔
271
        widgets = {
2✔
272
            'date': forms.DateInput(
273
                attrs={
274
                    'type': 'date',
275
                    'class': 'form-control',
276
                }
277
            ),
278
            'image': forms.FileInput(
279
                attrs={
280
                    'class': 'form-control',
281
                    'accept': 'image/*'
282
                }
283
            )
284
        }
285

286
    def __init__(self, *args, **kwargs):
2✔
287
        super().__init__(*args, **kwargs)
×
288
        self.helper = FormHelper()
×
289
        self.helper.form_tag = False
×
290
        
291
        # Determine header class and active state based on whether form has data
292
        header_class = 'bg-light' if self.instance and self.instance.pk else 'bg-light-blue'
×
293
        is_active = bool(self.instance and self.instance.pk)
×
294
        
295
        self.helper.layout = Layout(
×
296
            Accordion(
297
                AccordionGroup(
298
                    'Death Details',
299
                    'date',
300
                    'company',
301
                    'cemetery',
302
                    'cwgc_id',
303
                    'image',
304
                    active=is_active,
305
                    css_id="death-details-accordion",
306
                    button_class=header_class
307
                )
308
            )
309
        )
310

311

312
class editSoldierForm(forms.ModelForm):
2✔
313
    def __init__(self, *args, **kwargs):
2✔
314
        super().__init__(*args, **kwargs)
×
315
        self.helper = FormHelper()
×
316
        self.helper.label_class = 'form-label'  
×
317
        self.fields['provost_officer'].disabled = True
×
318

319
        # Determine header class and active state based on whether form has data
320
        header_class = 'bg-light' if self.instance and self.instance.pk else 'bg-light-blue'
×
321
        is_active = bool(self.instance and self.instance.pk)
×
322
        
323
        # Check if there are any imprisonment records
324
        has_imprisonment = False
×
325
        if self.instance and self.instance.pk:
×
326
            has_imprisonment = SoldierImprisonment.objects.filter(soldier=self.instance).exists()
×
327
        
328
        # Set title based on whether there's data
329
        title = 'Prisoner of War Details' if has_imprisonment else 'Prisoner of War Details (None Recorded)'
×
330
        
331
        self.helper.layout = Layout(
×
332
            Field('surname'),
333
            Field('initials'),
334
            Field('army_number'),
335
            Field('rank'),
336
            Field('provost_officer'),
337
            Field('notes'),
338
            Accordion(
339
                AccordionGroup(
340
                    title,
341
                    'imprisonment_formset',
342
                    active=has_imprisonment,
343
                    button_class=header_class
344
                ),
345
                css_id="imprisonment-details-accordion"
346
            )
347
        )
348
        # Initialize the formset
349
        self.imprisonment_formset = SoldierImprisonmentInlineFormSet(
×
350
            instance=self.instance,
351
            prefix='imprisonment'
352
        )
353

354
    class Meta:
2✔
355
        model = Soldier
2✔
356
        fields = ['surname', 'initials', 'army_number', 'rank', 'notes', 'provost_officer']
2✔
357
        exclude = ['created_at']  # Exclude the created_at field
2✔
358

359

360
class ProvostOfficerSearchForm(forms.Form):
2✔
361
    q = forms.CharField(
2✔
362
        required=False,
363
        label='Search',
364
        widget=forms.TextInput(attrs={
365
            'class': 'form-control',
366
            'placeholder': 'Search by surname or army number...'
367
        })
368
    )
369

370
class ProvostOfficerForm(forms.ModelForm):
2✔
371
    def __init__(self, *args, **kwargs):
2✔
372
        super().__init__(*args, **kwargs)
×
373
        self.helper = FormHelper()
×
374
        self.helper.label_class = 'form-label'
×
375
        
376
        # Filter rank choices to only show officer ranks
377
        self.fields['rank'].queryset = Rank.objects.filter(rank_class="OF").order_by('name')
×
378
        
379
        # Set provost_officer field
380
        self.fields['provost_officer'] = forms.BooleanField(
×
381
            initial=True,
382
            disabled=True,
383
            required=False,
384
            help_text="All officers created through this form are automatically marked as Provost Officers"
385
        )
386
        
387
        # Determine header class and active state
388
        header_class = 'bg-light' if self.instance and self.instance.pk else 'bg-light-blue'
×
389
        is_active = bool(self.instance and self.instance.pk)
×
390
        
391
        self.helper.layout = Layout(
×
392
            Field('surname'),
393
            Field('initials'),
394
            Field('army_number'),
395
            Field('rank'),
396
            Field('provost_officer'),
397
            Accordion(
398
                AccordionGroup(
399
                    'Appointment Details',
400
                    'appointment_formset',
401
                    active=is_active,
402
                    button_class=header_class
403
                ),
404
                css_id="appointment-details-accordion"
405
            ),
406
            Field('notes')
407
        )
408

409
    class Meta:
2✔
410
        model = Soldier
2✔
411
        fields = ['surname', 'initials', 'army_number', 'rank', 'notes']
2✔
412
        widgets = {
2✔
413
            'notes': forms.Textarea(attrs={'rows': 3}),
414
        }
415

416
    def save(self, commit=True):
2✔
417
        soldier = super().save(commit=False)
×
418
        soldier.provost_officer = True
×
419
        if commit:
×
420
            soldier.save()
×
421
        return soldier
×
422

423
class ProvostAppointmentForm(forms.ModelForm):
2✔
424
    def __init__(self, *args, **kwargs):
2✔
425
        super().__init__(*args, **kwargs)
×
426
        self.helper = FormHelper()
×
427
        self.helper.label_class = 'form-label'
×
428
        
429
        # Filter rank choices to only show officer ranks
430
        self.fields['rank'].queryset = Rank.objects.filter(rank_class="OF").order_by('name')
×
431
        
432
        self.helper.layout = Layout(
×
433
            Field('rank'),
434
            Field('date'),
435
            Field('notes')
436
        )
437

438
    date = forms.DateField(
2✔
439
        widget=forms.DateInput(
440
            attrs={
441
                'type': 'date',
442
                'class': 'form-control',
443
                'style': 'width: 20%;'
444
            }
445
        ),
446
        required=False
447
    )
448

449
    class Meta:
2✔
450
        model = ProvostAppointment
2✔
451
        fields = ['rank', 'date', 'notes']
2✔
452
        widgets = {
2✔
453
            'notes': forms.Textarea(attrs={'rows': 3}),
454
        }
455

456
# Create the formset
457
ProvostAppointmentInlineFormSet = inlineformset_factory(
2✔
458
    Soldier,
459
    ProvostAppointment,
460
    form=ProvostAppointmentForm,
461
    extra=1,
462
    can_delete=True
463
)
464

465
class ProvostAppointmentFormSetHelper(FormHelper):
2✔
466
    def __init__(self, *args, **kwargs):
2✔
467
        super().__init__(*args, **kwargs)
×
468
        self.form_tag = False
×
469
        
470
        # Default to collapsed
471
        has_data = False
×
472
        title = 'Appointment Details (None Recorded)'
×
473
        
474
        self.layout = Layout(
×
475
            Accordion(
476
                AccordionGroup(
477
                    title,
478
                    'rank',
479
                    'date',
480
                    'notes',
481
                    active=has_data,
482
                    css_class='bg-info bg-opacity-25 border rounded p-3'
483
                ),
484
                css_id="appointment-details-accordion"
485
            )
486
        )
487

488
    def update_title(self):
2✔
489
        if hasattr(self, 'formset') and self.formset.initial_forms:
×
490
            has_data = any(form.initial for form in self.formset.initial_forms)
×
491
            title = 'Appointment Details' if has_data else 'Appointment Details (None Recorded)'
×
492
            self.layout[0][0].name = title
×
493
            self.layout[0][0].active = has_data
×
494

495
class ProvostAppointmentFormSetWithHelper(ProvostAppointmentInlineFormSet):
2✔
496
    def __init__(self, *args, **kwargs):
2✔
497
        super().__init__(*args, **kwargs)
×
498
        self.helper = ProvostAppointmentFormSetHelper()
×
499
        self.helper.formset = self
×
500
        self.helper.update_title()
×
501

502

503
class SoldierDecorationForm(forms.ModelForm):
2✔
504
    country = forms.ModelChoiceField(
2✔
505
        queryset=Country.objects.all(),
506
    )
507

508
    class Meta:
2✔
509
        model = SoldierDecoration
2✔
510
        fields = ['decoration', 'gazette_issue', 'gazette_page', 'gazette_date', 'theatre', 'country', 'citation', 'notes']
2✔
511
        widgets = {
2✔
512
            'gazette_date': forms.DateInput(
513
                attrs={
514
                    'type': 'date',
515
                    'class': 'form-control',
516
                    'style': 'width: 20%;'
517
                }
518
            )
519
        }
520

521
class SoldierDecorationFormSetHelper(FormHelper):
2✔
522
    def __init__(self, *args, **kwargs):
2✔
523
        super().__init__(*args, **kwargs)
×
524
        self.form_tag = False
×
525
        
526
        # Default to collapsed
527
        has_data = False
×
528
        title = 'Decoration Details (None Recorded)'
×
529
        
530
        self.layout = Layout(
×
531
            Accordion(
532
                AccordionGroup(
533
                    title,
534
                    'decoration',
535
                    'gazette_issue',
536
                    'gazette_page',
537
                    'gazette_date',
538
                    'theatre',
539
                    'country',
540
                    'citation',
541
                    'notes',
542
                    active=has_data,
543
                    css_class='bg-info bg-opacity-25 border rounded p-3'
544
                ),
545
                css_id="decoration-details-accordion"
546
            )
547
        )
548

549
    def update_title(self):
2✔
550
        if hasattr(self, 'formset') and self.formset.initial_forms:
×
551
            has_data = any(form.initial for form in self.formset.initial_forms)
×
552
            title = 'Decoration Details' if has_data else 'Decoration Details (None Recorded)'
×
553
            self.layout[0][0].name = title
×
554
            self.layout[0][0].active = has_data
×
555

556
# Create the formset
557
SoldierDecorationInlineFormSet = inlineformset_factory(
2✔
558
    Soldier,
559
    SoldierDecoration,
560
    form=SoldierDecorationForm,
561
    extra=1,
562
    can_delete=True
563
)
564

565
# Add the helper to the formset
566
class SoldierDecorationFormSetWithHelper(SoldierDecorationInlineFormSet):
2✔
567
    def __init__(self, *args, **kwargs):
2✔
568
        super().__init__(*args, **kwargs)
×
569
        self.helper = SoldierDecorationFormSetHelper()
×
570
        self.helper.formset = self
×
571
        self.helper.update_title()
×
572

573

574
class SoldierDeathFormHelper(FormHelper):
2✔
575
    def __init__(self, *args, **kwargs):
2✔
576
        super().__init__(*args, **kwargs)
×
577
        self.form_tag = False
×
578
        
579
        # Default to collapsed
580
        has_data = False
×
581
        title = 'Death Details (None Recorded)'
×
582
        
583
        self.layout = Layout(
×
584
            Accordion(
585
                AccordionGroup(
586
                    title,
587
                    'date',
588
                    'company',
589
                    'cemetery',
590
                    'cwgc_id',
591
                    'image',
592
                    active=has_data,  # Collapsed by default
593
                    css_class='bg-info bg-opacity-25 border rounded p-3'
594
                ),
595
                css_id="death-details-accordion"
596
            )
597
        )
598

599
    def update_title(self):
2✔
600
        """Update the title based on form data"""
601
        if hasattr(self, 'form') and self.form.initial:
×
602
            has_data = any(self.form.initial.values())
×
603
            title = 'Death Details' if has_data else 'Death Details (None Recorded)'
×
604
            self.layout[0][0].name = title
×
605
            self.layout[0][0].active = has_data
×
606

607

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