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').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" context = { 'base_template': base_template, 'is_mobile': is_mobile, 'thematics': thematics, 'categories': categories, } 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, notes=notes, apply_changes_to_asset=apply_changes, ) created_intervention_code = None if request.POST.get('create_intervention') == 'true' and result_status in ('non_compliant', 'damaged', 'missing'): thematic = None if hasattr(target_obj, 'category') and target_obj.category: thematic = target_obj.category.thematic elif hasattr(target_obj, 'thematic'): thematic = target_obj.thematic 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)