338 lines
12 KiB
Python
338 lines
12 KiB
Python
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),
|
|
})
|
|
|
|
|