155 lines
7.3 KiB
Python
155 lines
7.3 KiB
Python
# forms.py
|
|
from django import forms
|
|
from django.utils.translation import gettext_lazy as _, get_language
|
|
|
|
from .models import Observation, OBSERVATION_TYPE_CHOICES
|
|
from common.models import Thematic
|
|
from assets.models import AssetCategory
|
|
|
|
class ObservationForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Observation
|
|
fields = ['description', 'latitude', 'longitude', 'thematic', 'category', 'observation_type', 'symptom', 'address']
|
|
widgets = {
|
|
'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
|
'thematic': forms.Select(attrs={'class': 'form-control', 'id': 'id_thematic'}),
|
|
'category': forms.Select(attrs={'class': 'form-control', 'id': 'id_category', 'disabled': 'disabled'}),
|
|
'observation_type': forms.Select(attrs={'class': 'form-control', 'id': 'id_observation_type'}),
|
|
'symptom': forms.Select(attrs={'class': 'form-control', 'id': 'id_symptom', 'disabled': 'disabled'}),
|
|
'latitude': forms.HiddenInput(),
|
|
'longitude': forms.HiddenInput(),
|
|
'address': forms.HiddenInput(),
|
|
}
|
|
labels = {
|
|
'thematic': _('Thématique'),
|
|
'category': _('Catégorie'),
|
|
'observation_type': _('Type d\'observation'),
|
|
'symptom': _('Symptôme'),
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
# Get current language
|
|
lang = get_language()
|
|
order_field = f'name_{lang}' if lang == 'nl' else 'name_fr'
|
|
|
|
# Remove restrictions - allow all thematics, sorted by current language
|
|
self.fields['thematic'].queryset = Thematic.objects.all().order_by(order_field)
|
|
self.fields['thematic'].empty_label = _("--- Sélectionnez une thématique ---")
|
|
|
|
# Initially show all categories, will be filtered by JavaScript based on selected thematic
|
|
self.fields['category'].queryset = AssetCategory.objects.all().order_by(order_field)
|
|
self.fields['category'].empty_label = _("--- Sélectionnez une catégorie ---")
|
|
|
|
# Observation type choices with translations
|
|
self.fields['observation_type'].choices = [
|
|
('', _('--- Sélectionnez un type ---')),
|
|
('incident', _('Incident')),
|
|
('inventory', _('Inventaire')),
|
|
('remark', _('Remarque')),
|
|
]
|
|
|
|
# Symptom field - initially empty, will be populated by JavaScript (and server on POST)
|
|
from interventions.models import Symptom
|
|
self.fields['symptom'].queryset = Symptom.objects.none()
|
|
self.fields['symptom'].empty_label = _("--- Sélectionnez un symptôme ---")
|
|
self.fields['symptom'].required = False
|
|
|
|
# If we have an instance with a thematic, filter categories by that thematic
|
|
if self.instance and self.instance.thematic_id:
|
|
self.fields['category'].queryset = AssetCategory.objects.filter(
|
|
thematic=self.instance.thematic
|
|
).order_by(order_field)
|
|
# Enable category field if thematic is already selected
|
|
self.fields['category'].widget.attrs.pop('disabled', None)
|
|
|
|
# If observation type is incident and category exists, load symptoms
|
|
if self.instance.observation_type == 'incident' and self.instance.category_id:
|
|
self.fields['symptom'].queryset = Symptom.objects.filter(
|
|
asset_categories=self.instance.category,
|
|
is_active=True
|
|
).order_by(order_field)
|
|
self.fields['symptom'].widget.attrs.pop('disabled', None)
|
|
|
|
# If form is bound, adjust querysets based on submitted data so choices validate
|
|
if self.is_bound:
|
|
# Filter categories by posted thematic (for correct re-render and validation context)
|
|
thematic_id = self.data.get('thematic') or self.data.get('id_thematic')
|
|
if thematic_id:
|
|
try:
|
|
self.fields['category'].queryset = AssetCategory.objects.filter(
|
|
thematic_id=int(thematic_id)
|
|
).order_by(order_field)
|
|
# Enable category when thematic provided
|
|
self.fields['category'].widget.attrs.pop('disabled', None)
|
|
except ValueError:
|
|
pass
|
|
|
|
# Configure symptom queryset based on posted category when observation_type is incident
|
|
# Prefer sub-category ID if provided
|
|
obs_type = self.data.get('observation_type')
|
|
sub_cat_id = self.data.get('obs_sub_category_id')
|
|
category_id = sub_cat_id or self.data.get('category')
|
|
if obs_type == 'incident' and category_id:
|
|
try:
|
|
cat_id_int = int(category_id)
|
|
# If it's a sub-category, also include parent category symptoms
|
|
cat_ids = [cat_id_int]
|
|
if sub_cat_id:
|
|
try:
|
|
sub_cat_obj = AssetCategory.objects.get(pk=cat_id_int)
|
|
if sub_cat_obj.parent_id:
|
|
cat_ids.append(sub_cat_obj.parent_id)
|
|
except AssetCategory.DoesNotExist:
|
|
pass
|
|
self.fields['symptom'].queryset = Symptom.objects.filter(
|
|
asset_categories__id__in=cat_ids,
|
|
is_active=True
|
|
).distinct().order_by(order_field)
|
|
self.fields['symptom'].widget.attrs.pop('disabled', None)
|
|
# Symptom required only for incident
|
|
self.fields['symptom'].required = True
|
|
except ValueError:
|
|
# leave as none()
|
|
pass
|
|
else:
|
|
# Not incident: ensure not required and empty queryset ok
|
|
self.fields['symptom'].required = False
|
|
|
|
def clean(self):
|
|
cleaned = super().clean()
|
|
|
|
# If a sub-category was selected, override category with it
|
|
sub_cat_id = self.data.get('obs_sub_category_id')
|
|
if sub_cat_id:
|
|
try:
|
|
sub_cat = AssetCategory.objects.get(pk=sub_cat_id)
|
|
cleaned['category'] = sub_cat
|
|
self.errors.pop('category', None)
|
|
except (AssetCategory.DoesNotExist, ValueError):
|
|
pass
|
|
|
|
obs_type = cleaned.get('observation_type')
|
|
if obs_type != 'incident':
|
|
# Clear symptom if not incident
|
|
cleaned['symptom'] = None
|
|
else:
|
|
# When incident, ensure symptom belongs to the selected category (or its parent)
|
|
category = cleaned.get('category')
|
|
symptom = cleaned.get('symptom')
|
|
if category and symptom:
|
|
from interventions.models import Symptom
|
|
lang = get_language()
|
|
order_field = f'name_{lang}' if lang == 'nl' else 'name_fr'
|
|
cat_ids = [category.pk]
|
|
if category.parent_id:
|
|
cat_ids.append(category.parent_id)
|
|
valid_qs = Symptom.objects.filter(asset_categories__id__in=cat_ids, is_active=True)
|
|
if not valid_qs.filter(pk=symptom.pk).exists():
|
|
self.add_error('symptom', _("Sélectionnez un choix valide. Ce choix ne fait pas partie de ceux disponibles."))
|
|
return cleaned
|
|
|
|
|
|
|
|
|