import json from django.contrib.auth.decorators import login_required from django.db import connection from django.shortcuts import render from django.utils.translation import get_language from common.models import UserConfig, Thematic from reporting.models import ReportingContractConfig def _can_access_reporting(user): if user.is_superuser: return True try: user_config = UserConfig.objects.get(user=user) return user_config.can_access_view("reporting") except UserConfig.DoesNotExist: return False @login_required def dashboard(request): if not _can_access_reporting(request.user): from django.core.exceptions import PermissionDenied raise PermissionDenied # Determine which contracts are accessible for the selector try: user_config = UserConfig.objects.prefetch_related("roles").get(user=request.user) is_unrestricted = ( request.user.is_superuser or user_config.roles.filter(name__in=["admin", "top_manager", "operator"]).exists() ) except UserConfig.DoesNotExist: user_config = None is_unrestricted = request.user.is_superuser # Reporting-enabled contracts enabled_qs = ( ReportingContractConfig.objects.filter(is_enabled=True) .select_related("contract") .order_by("contract__contract_number") ) if is_unrestricted: accessible_contracts = [ {"id": rc.contract.id, "number": rc.contract.contract_number} for rc in enabled_qs ] else: user_contract_ids = set( user_config.contracts.values_list("id", flat=True) ) if user_config else set() accessible_contracts = [ {"id": rc.contract.id, "number": rc.contract.contract_number} for rc in enabled_qs if rc.contract.id in user_contract_ids ] # Build thematic → contract_ids map from mv_interventions # (only corrective interventions with dt_to_be_processed, for accessible contracts) accessible_contract_ids = [c["id"] for c in accessible_contracts] lang = get_language() or "fr" if accessible_contract_ids: placeholders = ", ".join(["%s"] * len(accessible_contract_ids)) sql = f""" SELECT thematic_id, MAX(thematic_name_fr) AS name_fr, MAX(thematic_name_nl) AS name_nl, ARRAY_AGG(DISTINCT contract_id) AS contract_ids FROM reporting.mv_interventions WHERE maintenance_type = 'corrective' AND dt_to_be_processed IS NOT NULL AND thematic_id IS NOT NULL AND contract_id IN ({placeholders}) GROUP BY thematic_id ORDER BY MAX(thematic_name_fr) ASC """ with connection.cursor() as cursor: cursor.execute(sql, accessible_contract_ids) rows = cursor.fetchall() # Fetch thematic codes to map thematic IDs to codes thematic_ids = [r[0] for r in rows] thematic_codes = { t.id: t.code for t in Thematic.objects.filter(id__in=thematic_ids) } if thematic_ids else {} accessible_thematics = [] thematic_contract_map = {} # { thematic_id: [contract_id, ...] } thematic_code_map = {} # { thematic_id: code } for thematic_id, name_fr, name_nl, contract_ids in rows: name = name_nl if lang == "nl" else name_fr contract_ids_clean = sorted(int(c) for c in (contract_ids or []) if c is not None) code = thematic_codes.get(thematic_id) thematic_code_map[thematic_id] = code accessible_thematics.append({ "id": thematic_id, "name": name, "name_fr": name_fr, "name_nl": name_nl, "code": code, }) thematic_contract_map[thematic_id] = contract_ids_clean else: accessible_thematics = [] thematic_contract_map = {} thematic_code_map = {} # Saved preferences for this page saved_prefs = {} if user_config: saved_prefs = (user_config.reporting_preferences or {}).get("dashboard", {}) return render(request, "reporting/dashboard.html", { "accessible_contracts": accessible_contracts, "accessible_thematics": accessible_thematics, "thematic_contract_map_json": json.dumps(thematic_contract_map), "thematic_code_map_json": json.dumps(thematic_code_map), "saved_prefs_json": json.dumps(saved_prefs), }) @login_required def volume_dashboard(request): if not _can_access_reporting(request.user): from django.core.exceptions import PermissionDenied raise PermissionDenied # Determine which contracts are accessible for the selector try: user_config = UserConfig.objects.prefetch_related("roles").get(user=request.user) is_unrestricted = ( request.user.is_superuser or user_config.roles.filter(name__in=["admin", "top_manager", "operator"]).exists() ) except UserConfig.DoesNotExist: user_config = None is_unrestricted = request.user.is_superuser # Reporting-enabled contracts enabled_qs = ( ReportingContractConfig.objects.filter(is_enabled=True) .select_related("contract__company") .order_by("contract__contract_number") ) if is_unrestricted: accessible_contracts = [ { "id": rc.contract.id, "number": rc.contract.contract_number, "company_name": rc.contract.company.name } for rc in enabled_qs ] else: user_contract_ids = set( user_config.contracts.values_list("id", flat=True) ) if user_config else set() accessible_contracts = [ { "id": rc.contract.id, "number": rc.contract.contract_number, "company_name": rc.contract.company.name } for rc in enabled_qs if rc.contract.id in user_contract_ids ] # Build thematic → contract_ids map from mv_interventions accessible_contract_ids = [c["id"] for c in accessible_contracts] lang = get_language() or "fr" if accessible_contract_ids: placeholders = ", ".join(["%s"] * len(accessible_contract_ids)) sql = f""" SELECT thematic_id, MAX(thematic_name_fr) AS name_fr, MAX(thematic_name_nl) AS name_nl, ARRAY_AGG(DISTINCT contract_id) AS contract_ids FROM reporting.mv_interventions WHERE maintenance_type = 'corrective' AND dt_to_be_processed IS NOT NULL AND thematic_id IS NOT NULL AND contract_id IN ({placeholders}) GROUP BY thematic_id ORDER BY MAX(thematic_name_fr) ASC """ with connection.cursor() as cursor: cursor.execute(sql, accessible_contract_ids) rows = cursor.fetchall() # Fetch thematic codes to map thematic IDs to codes thematic_ids = [r[0] for r in rows] thematic_codes = { t.id: t.code for t in Thematic.objects.filter(id__in=thematic_ids) } if thematic_ids else {} accessible_thematics = [] thematic_contract_map = {} # { thematic_id: [contract_id, ...] } thematic_code_map = {} # { thematic_id: code } for thematic_id, name_fr, name_nl, contract_ids in rows: name = name_nl if lang == "nl" else name_fr contract_ids_clean = sorted(int(c) for c in (contract_ids or []) if c is not None) code = thematic_codes.get(thematic_id) thematic_code_map[thematic_id] = code accessible_thematics.append({ "id": thematic_id, "name": name, "name_fr": name_fr, "name_nl": name_nl, "code": code, }) thematic_contract_map[thematic_id] = contract_ids_clean else: accessible_thematics = [] thematic_contract_map = {} thematic_code_map = {} # Saved preferences for this page saved_prefs = {} if user_config: saved_prefs = (user_config.reporting_preferences or {}).get("volume_dashboard", {}) return render(request, "reporting/volume_dashboard.html", { "accessible_contracts": accessible_contracts, "accessible_thematics": accessible_thematics, "thematic_contract_map_json": json.dumps(thematic_contract_map), "thematic_code_map_json": json.dumps(thematic_code_map), "saved_prefs_json": json.dumps(saved_prefs), }) @login_required def observations_dashboard(request): if not _can_access_reporting(request.user): from django.core.exceptions import PermissionDenied raise PermissionDenied # Determine which thematic, category, symptom are accessible try: user_config = UserConfig.objects.prefetch_related("roles").get(user=request.user) is_unrestricted = ( request.user.is_superuser or user_config.roles.filter(name__in=["admin", "top_manager", "operator"]).exists() ) except UserConfig.DoesNotExist: user_config = None is_unrestricted = request.user.is_superuser lang = get_language() or "fr" from common.models import Thematic from assets.models import AssetCategory from interventions.models import Symptom from observations.permissions import get_observation_access_context if is_unrestricted: allowed_tids = list(Thematic.objects.values_list("id", flat=True)) else: access_context = get_observation_access_context(request.user, user_config=user_config) allowed_tids = list(access_context.thematic_ids) if access_context else [] if allowed_tids: thematics_qs = Thematic.objects.filter(id__in=allowed_tids).order_by("name_fr") all_categories_qs = AssetCategory.objects.filter(thematic_id__in=allowed_tids).order_by("name_fr") symptoms_qs = Symptom.objects.filter(thematic_id__in=allowed_tids).order_by("name_fr") accessible_thematics = [ { "id": t.id, "name": t.name_nl if lang == "nl" else t.name_fr, "code": t.code } for t in thematics_qs ] accessible_categories = [ { "id": c.id, "name": c.name_nl if lang == "nl" else c.name_fr, "thematic_id": c.thematic_id } for c in all_categories_qs ] accessible_symptoms = [ { "id": s.id, "name": s.name_nl if lang == "nl" else s.name_fr, "thematic_id": s.thematic_id } for s in symptoms_qs ] # Build thematic → list of category IDs mapping thematic_category_map = {} for c in accessible_categories: tid = c["thematic_id"] if tid not in thematic_category_map: thematic_category_map[tid] = [] thematic_category_map[tid].append(c["id"]) # Build thematic → list of symptom IDs mapping thematic_symptom_map = {} for s in accessible_symptoms: tid = s["thematic_id"] if tid not in thematic_symptom_map: thematic_symptom_map[tid] = [] thematic_symptom_map[tid].append(s["id"]) else: accessible_thematics = [] accessible_categories = [] accessible_symptoms = [] thematic_category_map = {} thematic_symptom_map = {} # Saved preferences for this page saved_prefs = {} if user_config: saved_prefs = (user_config.reporting_preferences or {}).get("observations_dashboard", {}) return render(request, "reporting/observations_dashboard.html", { "accessible_thematics": accessible_thematics, "accessible_categories": accessible_categories, "accessible_symptoms": accessible_symptoms, "thematic_category_map_json": json.dumps(thematic_category_map), "thematic_symptom_map_json": json.dumps(thematic_symptom_map), "saved_prefs_json": json.dumps(saved_prefs), })