neurodeveloper's picture
feat: initial commit for Hugging Face Space Workplace-Pulse-Telemetry
10b0bc1
Raw History Blame Contribute Delete
8.92 kB
"""
Workplace-Pulse-Telemetry: Organizational Analytics & Executive Reporting Engine
Engineered by Fabio Torres (neurodeveloper11)
Aggregates message-level telemetry into department pulses, Karasek matrices,
early burnout warnings, and actionable interventions aligned with Colombian Resolution 2764/2022.
"""
from typing import List, Dict
from datetime import datetime
from collections import defaultdict
import uuid
from src.schemas import (
MessageTelemetry,
DepartmentPulse,
ExecutiveReport,
KarasekQuadrant,
RiskLevel,
)
def compute_karasek_quadrant(avg_demand: float, avg_autonomy: float) -> KarasekQuadrant:
"""Classifies a team's organizational climate into the Karasek 4-quadrant topology."""
if avg_demand >= 45.0:
if avg_autonomy >= 50.0:
return KarasekQuadrant.ACTIVE
else:
return KarasekQuadrant.HIGH_STRAIN
else:
if avg_autonomy >= 50.0:
return KarasekQuadrant.LOW_STRAIN
else:
return KarasekQuadrant.PASSIVE
def compute_risk_level(
avg_stress: float,
avg_friction: float,
avg_psi: float,
after_hours_ratio: float,
) -> RiskLevel:
"""Evaluates multi-axial psychosocial risk severity."""
if (
avg_stress >= 65.0
or avg_friction >= 55.0
or avg_psi <= 35.0
or after_hours_ratio >= 0.40
):
return RiskLevel.CRITICAL
elif (
avg_stress >= 50.0
or avg_friction >= 40.0
or avg_psi <= 45.0
or after_hours_ratio >= 0.25
):
return RiskLevel.HIGH
elif avg_stress >= 35.0 or avg_friction >= 25.0 or avg_psi <= 52.0:
return RiskLevel.MODERATE
else:
return RiskLevel.LOW
def generate_recommendations(
quadrant: KarasekQuadrant,
risk: RiskLevel,
avg_stress: float,
avg_friction: float,
avg_psi: float,
after_hours_ratio: float,
) -> List[str]:
"""
Formulates evidence-based organizational directives aligned with Res. 2764/2022
(Intra-labor psychosocial risk management) and Edmondson's psychological safety research.
"""
recs: List[str] = []
# 1. After-hours & Burnout
if after_hours_ratio >= 0.25 or avg_stress >= 55.0:
recs.append(
"Barrera de Desconexi贸n Digital: Restringir notificaciones fuera de jornada (19:00 a 07:00) "
"y auditar cuellos de botella de entrega inmediata (Res. 2764/2022, Dominio Demandas de la Jornada)."
)
# 2. Karasek Quadrant Directives
if quadrant == KarasekQuadrant.HIGH_STRAIN:
recs.append(
"Intervenci贸n Prioritaria en Alta Tensi贸n: Delegar mayor margen de decisi贸n operativa en las c茅lulas "
"y redistribuir picos de carga para mitigar riesgos psicosom谩ticos y rotaci贸n de talento."
)
elif quadrant == KarasekQuadrant.PASSIVE:
recs.append(
"Reestructuraci贸n de Puestos y Autonom铆a: El equipo evidencia baja demanda pero nula latitud decisional. "
"Fomentar iniciativas de auto-organizaci贸n para evitar el s铆ndrome de apat铆a laboral (Boreout)."
)
elif quadrant == KarasekQuadrant.ACTIVE:
recs.append(
"Mantenimiento Sostenible de Alto Rendimiento: El equipo exhibe alta autonom铆a y alta motivaci贸n; "
"monitorear descansos intermitentes para evitar que la sobre-exigencia sostenida degenere en agotamiento."
)
# 3. Interpersonal Friction
if avg_friction >= 35.0:
recs.append(
"Protocolo de Higiene Comunicacional: Desescalar patrones de comunicaci贸n defensiva y pasivo-agresiva. "
"Realizar sesiones de alineaci贸n de expectativas interdepartamentales (Dominio de Relaciones Sociales en el Trabajo)."
)
# 4. Psychological Safety (Amy Edmondson)
if avg_psi <= 45.0:
recs.append(
"Cultura de Seguridad Psicol贸gica (Amy Edmondson): Institucionalizar 'Blameless Post-Mortems' y auditor铆a de liderazgo "
"para que los colaboradores puedan reportar incidentes y dudas sin temor a consecuencias punitivas."
)
if not recs:
recs.append(
"Ambiente Ocupacional Saludable: Mantener pol铆ticas de reconocimiento y autonom铆a vigentes. Realizar chequeos preventivos peri贸dicos."
)
return recs
def aggregate_department_pulse(
dept_name: str, telemetry_list: List[MessageTelemetry]
) -> DepartmentPulse:
"""Aggregates individual message telemetry into a comprehensive departmental pulse."""
if not telemetry_list:
return DepartmentPulse(
department=dept_name,
message_count=0,
participant_count=0,
avg_stress_burnout=0.0,
avg_psychological_safety=100.0,
avg_friction=0.0,
avg_autonomy=50.0,
avg_demand=0.0,
after_hours_ratio=0.0,
karasek_quadrant=KarasekQuadrant.LOW_STRAIN,
overall_risk_level=RiskLevel.LOW,
burnout_alert=False,
key_recommendations=["Sin actividad registrada en este per铆odo."],
)
count = len(telemetry_list)
unique_senders = len(set(m.sender_pseudonym for m in telemetry_list))
avg_stress = sum(m.stress_urgency_score for m in telemetry_list) / count
avg_psi = sum(m.psychological_safety_score for m in telemetry_list) / count
avg_friction = sum(m.friction_score for m in telemetry_list) / count
avg_autonomy = sum(m.autonomy_score for m in telemetry_list) / count
avg_demand = sum(m.demand_score for m in telemetry_list) / count
after_hours_count = sum(1 for m in telemetry_list if m.after_hours_flag)
after_hours_ratio = after_hours_count / count
quadrant = compute_karasek_quadrant(avg_demand, avg_autonomy)
risk = compute_risk_level(avg_stress, avg_friction, avg_psi, after_hours_ratio)
burnout_flag = avg_stress >= 55.0 or after_hours_ratio >= 0.30
recs = generate_recommendations(
quadrant=quadrant,
risk=risk,
avg_stress=avg_stress,
avg_friction=avg_friction,
avg_psi=avg_psi,
after_hours_ratio=after_hours_ratio,
)
return DepartmentPulse(
department=dept_name,
message_count=count,
participant_count=unique_senders,
avg_stress_burnout=round(avg_stress, 1),
avg_psychological_safety=round(avg_psi, 1),
avg_friction=round(avg_friction, 1),
avg_autonomy=round(avg_autonomy, 1),
avg_demand=round(avg_demand, 1),
after_hours_ratio=round(after_hours_ratio, 2),
karasek_quadrant=quadrant,
overall_risk_level=risk,
burnout_alert=burnout_flag,
key_recommendations=recs,
)
def generate_executive_report(
telemetry_list: List[MessageTelemetry], total_pii_redacted: int = 0
) -> ExecutiveReport:
"""Builds the comprehensive executive report across all departments in the organization."""
grouped = defaultdict(list)
for t in telemetry_list:
grouped[t.department].append(t)
department_pulses: Dict[str, DepartmentPulse] = {}
critical_alerts: List[str] = []
total_msgs = len(telemetry_list)
if total_msgs == 0:
return ExecutiveReport(
report_id=str(uuid.uuid4())[:8],
total_messages_analyzed=0,
total_pii_redacted=0,
organization_burnout_index=0.0,
organization_psych_safety_index=100.0,
organization_friction_index=0.0,
department_pulses={},
critical_alerts=["No se encontraron datos para procesar."],
)
for dept, msgs in grouped.items():
pulse = aggregate_department_pulse(dept, msgs)
department_pulses[dept] = pulse
if pulse.overall_risk_level in [RiskLevel.CRITICAL, RiskLevel.HIGH]:
critical_alerts.append(
f"馃毃 [{dept.upper()}] Riesgo {pulse.overall_risk_level.value.upper()}: "
f"Burnout {pulse.avg_stress_burnout}/100 | Fricci贸n {pulse.avg_friction}/100 | "
f"Cuadrante Karasek: {pulse.karasek_quadrant.value.replace('_', ' ').title()}"
)
org_burnout = sum(m.stress_urgency_score for m in telemetry_list) / total_msgs
org_psi = sum(m.psychological_safety_score for m in telemetry_list) / total_msgs
org_friction = sum(m.friction_score for m in telemetry_list) / total_msgs
return ExecutiveReport(
report_id=f"WPT-{str(uuid.uuid4())[:8].upper()}",
generated_at=datetime.now(),
total_messages_analyzed=total_msgs,
total_pii_redacted=total_pii_redacted,
organization_burnout_index=round(org_burnout, 1),
organization_psych_safety_index=round(org_psi, 1),
organization_friction_index=round(org_friction, 1),
department_pulses=department_pulses,
critical_alerts=critical_alerts,
)