loko/streetup/assets/views/inspections.py

228 lines
9.5 KiB
Python

from django.shortcuts import render, get_object_or_404, redirect
from django.http import JsonResponse, Http404, HttpResponseForbidden
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_POST
from django.db.models import Q
from django.contrib.contenttypes.models import ContentType
from django.utils import timezone
from django.utils.translation import gettext as _
from common.models import Thematic
from assets.models import AssetCategory
from assets.models.inspection import (
AssetInspection, ParkingSpotInspection,
INSPECTION_RESULT_CHOICES, INSPECTION_NATURE_CHOICES, INSPECTOR_ROLE_CHOICES
)
def get_user_inspectable_thematics(user):
"""
Retourne les thématiques pour lesquelles l'utilisateur détient la permission can_inspect.
"""
if hasattr(user, 'config'):
return user.config.get_inspectable_thematics().order_by('name_fr')
return Thematic.objects.all().order_by('name_fr')
@login_required
def inspections_list(request):
"""
Vue de liste et de gestion des inspections avec filtre par défaut 'Mes inspections'.
"""
filter_type = request.GET.get('tab', 'my') # 'my' ou 'all'
thematic_code = request.GET.get('thematic', '')
status_filter = request.GET.get('status', '')
nature_filter = request.GET.get('nature', '')
search_q = request.GET.get('q', '').strip()
qs = AssetInspection.objects.all().select_related('asset_content_type', 'inspector').prefetch_related('documents').order_by('-inspection_date')
if filter_type == 'my':
qs = qs.filter(inspector=request.user)
if thematic_code:
try:
thematic = Thematic.objects.get(code=thematic_code)
categories = AssetCategory.objects.filter(thematic=thematic)
cts = ContentType.objects.filter(assetcategory__in=categories).distinct()
qs = qs.filter(asset_content_type__in=cts)
except Thematic.DoesNotExist:
pass
if status_filter:
qs = qs.filter(result_status=status_filter)
if nature_filter:
qs = qs.filter(nature=nature_filter)
if search_q:
qs = qs.filter(
Q(notes__icontains=search_q) |
Q(inspector__first_name__icontains=search_q) |
Q(inspector__last_name__icontains=search_q) |
Q(inspector__username__icontains=search_q)
)
thematics = get_user_inspectable_thematics(request.user)
can_inspect = thematics.exists()
is_mobile = request.path.startswith('/mobile/')
base_template = "mobile/mobile_base.html" if is_mobile else "base.html"
context = {
'base_template': base_template,
'is_mobile': is_mobile,
'inspections': qs[:100],
'total_count': qs.count(),
'filter_type': filter_type,
'selected_thematic': thematic_code,
'selected_status': status_filter,
'selected_nature': nature_filter,
'search_q': search_q,
'thematics': thematics,
'can_inspect': can_inspect,
'result_choices': INSPECTION_RESULT_CHOICES,
'nature_choices': INSPECTION_NATURE_CHOICES,
}
return render(request, 'assets/inspections_list.html', context)
@login_required
def quick_inspection_map(request):
"""
Vue cartographique d'inspection rapide optimisée mobile & GPS.
"""
thematics = get_user_inspectable_thematics(request.user)
if not thematics.exists():
return HttpResponseForbidden(_("Vous ne disposez d'aucune thématique autorisée pour réaliser des inspections."))
categories = AssetCategory.objects.filter(thematic__in=thematics).select_related('thematic').order_by('name_fr')
is_mobile = request.path.startswith('/mobile/')
base_template = "mobile/mobile_base.html" if is_mobile else "base.html"
from assets.models import ParkingSpot
parking_spot_ct_id = ContentType.objects.get_for_model(ParkingSpot).id
context = {
'base_template': base_template,
'is_mobile': is_mobile,
'thematics': thematics,
'categories': categories,
'parking_spot_ct_id': parking_spot_ct_id,
}
return render(request, 'assets/quick_inspection_map.html', context)
@login_required
@require_POST
def create_inspection_api(request):
"""
API endpoint pour l'enregistrement d'une nouvelle inspection.
"""
content_type_id = request.POST.get('content_type_id')
object_id = request.POST.get('object_id')
result_status = request.POST.get('result_status', 'compliant')
nature = request.POST.get('nature', 'visual')
inspector_role = request.POST.get('inspector_role', 'observer')
notes = request.POST.get('notes', '').strip()
verified_obsolescence = request.POST.get('verified_obsolescence', '').strip() or None
apply_changes = request.POST.get('apply_changes_to_asset', 'true') == 'true'
if not content_type_id or not object_id:
return JsonResponse({'success': False, 'error': _("Cible d'inspection non spécifiée.")}, status=400)
try:
content_type = ContentType.objects.get(pk=content_type_id)
target_obj = content_type.get_object_for_this_type(pk=object_id)
except Exception as e:
return JsonResponse({'success': False, 'error': _("Cible introuvable : %s") % str(e)}, status=404)
is_parking_spot = (content_type.model == 'parkingspot')
try:
if is_parking_spot:
inspection = ParkingSpotInspection(
asset_content_type=content_type,
asset_object_id=target_obj.pk,
inspector=request.user,
inspection_date=timezone.now(),
result_status=result_status,
nature=nature,
inspector_role=inspector_role,
verified_obsolescence=verified_obsolescence,
notes=notes,
apply_changes_to_asset=apply_changes,
)
if 'verified_standard_stand_count' in request.POST and request.POST['verified_standard_stand_count'] != '':
inspection.verified_standard_stand_count = int(request.POST['verified_standard_stand_count'])
if 'verified_cargo_stand_count' in request.POST and request.POST['verified_cargo_stand_count'] != '':
inspection.verified_cargo_stand_count = int(request.POST['verified_cargo_stand_count'])
if 'verified_stand_type' in request.POST and request.POST['verified_stand_type'] != '':
inspection.verified_stand_type = request.POST['verified_stand_type']
if 'verified_has_scooter_drop_zone' in request.POST:
inspection.verified_has_scooter_drop_zone = (request.POST['verified_has_scooter_drop_zone'] == 'true')
if 'verified_marking_quality' in request.POST and request.POST['verified_marking_quality'] != '':
inspection.verified_marking_quality = request.POST['verified_marking_quality']
inspection.save()
else:
inspection = AssetInspection.objects.create(
asset_content_type=content_type,
asset_object_id=target_obj.pk,
inspector=request.user,
inspection_date=timezone.now(),
result_status=result_status,
nature=nature,
inspector_role=inspector_role,
verified_obsolescence=verified_obsolescence,
apply_changes_to_asset=apply_changes,
)
# Enregistrement des photos et documents joints
uploaded_files = request.FILES.getlist('photos') + request.FILES.getlist('documents') + request.FILES.getlist('files')
if uploaded_files:
from assets.models.inspection import InspectionDocument
for f in uploaded_files:
InspectionDocument.objects.create(
inspection=inspection,
file=f
)
created_intervention_code = None
if request.POST.get('create_intervention') == 'true' and result_status in ('non_compliant', 'damaged', 'missing'):
from assets.permissions import _get_asset_thematic
thematic = _get_asset_thematic(target_obj)
if not thematic:
if is_parking_spot:
thematic = Thematic.objects.filter(code='parking').first()
else:
thematic = Thematic.objects.first()
from interventions.models import Intervention, InterventionAsset
intervention = Intervention.objects.create(
title=_("Intervention suite à inspection non conforme (%s)") % target_obj,
thematic=thematic,
maintain_type='corrective',
priority='2' if result_status in ('damaged', 'missing') else '3',
init_description=notes or _("Signalement automatique lors de l'inspection du %s.") % timezone.now().strftime('%d/%m/%Y'),
status='in_preparation',
created_by=request.user,
geom=getattr(target_obj, 'geom', None),
)
InterventionAsset.objects.create(
intervention=intervention,
content_type=content_type,
object_id=target_obj.pk
)
created_intervention_code = intervention.code
return JsonResponse({
'success': True,
'inspection_id': inspection.id,
'created_intervention_code': created_intervention_code,
'message': _("Inspection enregistrée avec succès.")
})
except Exception as e:
return JsonResponse({'success': False, 'error': str(e)}, status=500)