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

gm3dmo / cmp / 13722919996

07 Mar 2025 02:36PM UTC coverage: 62.535% (-0.2%) from 62.756%
13722919996

push

github

web-flow
Merge pull request #309 from gm3dmo/fixview

fixview

1 of 17 new or added lines in 2 files covered. (5.88%)

3 existing lines in 2 files now uncovered.

888 of 1420 relevant lines covered (62.54%)

1.25 hits per line

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

56.85
/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
        # Always start collapsed
75
        title = 'Prisoner of War Details (None Recorded)'
×
76
        
77
        self.layout = Layout(
×
78
            Accordion(
79
                AccordionGroup(
80
                    title,
81
                    'pow_camp',
82
                    'pow_number',
83
                    'date_from',
84
                    'date_to',
85
                    'notes',
86
                    active=False,  # Set to False to ensure it's collapsed
87
                    css_class='bg-info bg-opacity-25 border rounded p-3'
88
                ),
89
                css_id="imprisonment-details-accordion"
90
            )
91
        )
92

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

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

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

117

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

123

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

129

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

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

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

196

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

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

213

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

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

234

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

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

253

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

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

265

266
class editSoldierDeathForm(forms.ModelForm):
2✔
267
    class Meta:
2✔
268
        model = SoldierDeath
2✔
269
        fields = ['date', 'company', 'cemetery', 'cwgc_id', 'image']
2✔
270
        widgets = {
2✔
271
            'date': forms.DateInput(
272
                attrs={
273
                    'type': 'date',
274
                    'class': 'form-control',
275
                }
276
            ),
277
            'image': forms.FileInput(
278
                attrs={
279
                    'class': 'form-control',
280
                    'accept': 'image/*'
281
                }
282
            )
283
        }
284

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

310

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

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

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

358

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

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

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

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

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

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

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

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

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

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

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

501

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

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

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

546
    def update_title(self):
2✔
547
        if hasattr(self, 'formset') and self.formset.initial_forms:
×
548
            has_data = any(form.initial for form in self.formset.initial_forms)
×
549
            title = 'Decoration Details' if has_data else 'Decoration Details (None Recorded)'
×
550
            self.layout[0][0].name = title
×
NEW
551
            self.layout[0][0].active = False  # Always keep collapsed
×
552

553
# Create the formset
554
SoldierDecorationInlineFormSet = inlineformset_factory(
2✔
555
    Soldier,
556
    SoldierDecoration,
557
    form=SoldierDecorationForm,
558
    extra=0,
559
    can_delete=True
560
)
561

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

570

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

595
    def update_title(self):
2✔
596
        """Update the title based on form data"""
597
        if hasattr(self, 'form') and self.form.initial:
×
598
            has_data = any(self.form.initial.values())
×
599
            title = 'Death Details' if has_data else 'Death Details (None Recorded)'
×
600
            self.layout[0][0].name = title
×
NEW
601
            self.layout[0][0].active = False  # Always keep collapsed
×
602

603

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