v1.0.63 Backend / v1.0.57 Frontend - Edición y auditoría de preguntas
This commit is contained in:
@@ -204,7 +204,7 @@ def send_completed_inspection_to_n8n(inspection, db):
|
||||
# No lanzamos excepción para no interrumpir el flujo normal
|
||||
|
||||
|
||||
BACKEND_VERSION = "1.0.28"
|
||||
BACKEND_VERSION = "1.0.63"
|
||||
app = FastAPI(title="Checklist Inteligente API", version=BACKEND_VERSION)
|
||||
|
||||
# S3/MinIO configuration
|
||||
@@ -966,6 +966,19 @@ def create_question(
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_question)
|
||||
|
||||
# Registrar auditoría
|
||||
audit_log = models.QuestionAuditLog(
|
||||
question_id=db_question.id,
|
||||
checklist_id=question.checklist_id,
|
||||
user_id=current_user.id,
|
||||
action="created",
|
||||
new_value=f"Pregunta creada: {question.text}",
|
||||
comment=f"Sección: {question.section}, Tipo: {question.type}, Puntos: {question.points}"
|
||||
)
|
||||
db.add(audit_log)
|
||||
db.commit()
|
||||
|
||||
return db_question
|
||||
|
||||
|
||||
@@ -983,11 +996,44 @@ def update_question(
|
||||
if not db_question:
|
||||
raise HTTPException(status_code=404, detail="Pregunta no encontrada")
|
||||
|
||||
# Guardar valores anteriores para auditoría
|
||||
import json
|
||||
changes = []
|
||||
|
||||
for key, value in question.dict(exclude_unset=True).items():
|
||||
setattr(db_question, key, value)
|
||||
old_value = getattr(db_question, key)
|
||||
if old_value != value:
|
||||
# Convertir a string para comparación y almacenamiento
|
||||
old_str = json.dumps(old_value, ensure_ascii=False) if isinstance(old_value, (dict, list)) else str(old_value)
|
||||
new_str = json.dumps(value, ensure_ascii=False) if isinstance(value, (dict, list)) else str(value)
|
||||
|
||||
changes.append({
|
||||
'field': key,
|
||||
'old': old_str,
|
||||
'new': new_str
|
||||
})
|
||||
setattr(db_question, key, value)
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_question)
|
||||
|
||||
# Registrar auditoría para cada campo cambiado
|
||||
for change in changes:
|
||||
audit_log = models.QuestionAuditLog(
|
||||
question_id=question_id,
|
||||
checklist_id=db_question.checklist_id,
|
||||
user_id=current_user.id,
|
||||
action="updated",
|
||||
field_name=change['field'],
|
||||
old_value=change['old'],
|
||||
new_value=change['new'],
|
||||
comment=f"Campo '{change['field']}' modificado"
|
||||
)
|
||||
db.add(audit_log)
|
||||
|
||||
if changes:
|
||||
db.commit()
|
||||
|
||||
return db_question
|
||||
|
||||
|
||||
@@ -1004,11 +1050,56 @@ def delete_question(
|
||||
if not db_question:
|
||||
raise HTTPException(status_code=404, detail="Pregunta no encontrada")
|
||||
|
||||
# Registrar auditoría antes de eliminar
|
||||
audit_log = models.QuestionAuditLog(
|
||||
question_id=question_id,
|
||||
checklist_id=db_question.checklist_id,
|
||||
user_id=current_user.id,
|
||||
action="deleted",
|
||||
old_value=f"Pregunta eliminada: {db_question.text}",
|
||||
comment=f"Sección: {db_question.section}, Tipo: {db_question.type}, Puntos: {db_question.points}"
|
||||
)
|
||||
db.add(audit_log)
|
||||
|
||||
db.delete(db_question)
|
||||
db.commit()
|
||||
return {"message": "Pregunta eliminada"}
|
||||
|
||||
|
||||
@app.get("/api/questions/{question_id}/audit", response_model=List[schemas.QuestionAuditLog])
|
||||
def get_question_audit_history(
|
||||
question_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: models.User = Depends(get_current_user)
|
||||
):
|
||||
"""Obtener historial de cambios de una pregunta"""
|
||||
if current_user.role != "admin":
|
||||
raise HTTPException(status_code=403, detail="Solo administradores pueden ver el historial")
|
||||
|
||||
audit_logs = db.query(models.QuestionAuditLog).filter(
|
||||
models.QuestionAuditLog.question_id == question_id
|
||||
).order_by(models.QuestionAuditLog.created_at.desc()).all()
|
||||
|
||||
return audit_logs
|
||||
|
||||
|
||||
@app.get("/api/checklists/{checklist_id}/questions/audit", response_model=List[schemas.QuestionAuditLog])
|
||||
def get_checklist_questions_audit_history(
|
||||
checklist_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: models.User = Depends(get_current_user)
|
||||
):
|
||||
"""Obtener historial de cambios de todas las preguntas de un checklist"""
|
||||
if current_user.role != "admin":
|
||||
raise HTTPException(status_code=403, detail="Solo administradores pueden ver el historial")
|
||||
|
||||
audit_logs = db.query(models.QuestionAuditLog).filter(
|
||||
models.QuestionAuditLog.checklist_id == checklist_id
|
||||
).order_by(models.QuestionAuditLog.created_at.desc()).all()
|
||||
|
||||
return audit_logs
|
||||
|
||||
|
||||
# ============= INSPECTION ENDPOINTS =============
|
||||
@app.get("/api/inspections", response_model=List[schemas.Inspection])
|
||||
def get_inspections(
|
||||
|
||||
@@ -208,6 +208,27 @@ class ChecklistPermission(Base):
|
||||
mechanic = relationship("User")
|
||||
|
||||
|
||||
class QuestionAuditLog(Base):
|
||||
"""Registro de auditoría para cambios en preguntas de checklists"""
|
||||
__tablename__ = "question_audit_log"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
question_id = Column(Integer, ForeignKey("questions.id", ondelete="CASCADE"), nullable=False)
|
||||
checklist_id = Column(Integer, ForeignKey("checklists.id", ondelete="CASCADE"), nullable=False)
|
||||
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
||||
action = Column(String(50), nullable=False) # created, updated, deleted
|
||||
field_name = Column(String(100), nullable=True) # Campo modificado
|
||||
old_value = Column(Text, nullable=True) # Valor anterior
|
||||
new_value = Column(Text, nullable=True) # Valor nuevo
|
||||
comment = Column(Text, nullable=True) # Comentario del cambio
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
# Relationships
|
||||
question = relationship("Question")
|
||||
checklist = relationship("Checklist")
|
||||
user = relationship("User")
|
||||
|
||||
|
||||
class InspectionAuditLog(Base):
|
||||
"""Registro de auditoría para cambios en inspecciones y respuestas"""
|
||||
__tablename__ = "inspection_audit_log"
|
||||
|
||||
@@ -142,6 +142,23 @@ class Question(QuestionBase):
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# Question Audit Schemas
|
||||
class QuestionAuditLog(BaseModel):
|
||||
id: int
|
||||
question_id: int
|
||||
checklist_id: int
|
||||
user_id: int
|
||||
action: str
|
||||
field_name: Optional[str] = None
|
||||
old_value: Optional[str] = None
|
||||
new_value: Optional[str] = None
|
||||
comment: Optional[str] = None
|
||||
created_at: datetime
|
||||
user: Optional['User'] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# Inspection Schemas
|
||||
class InspectionBase(BaseModel):
|
||||
|
||||
44
backend/migrations/add_question_audit_log.sql
Normal file
44
backend/migrations/add_question_audit_log.sql
Normal file
@@ -0,0 +1,44 @@
|
||||
-- Migration: Add question_audit_log table
|
||||
-- Date: 2025-11-27
|
||||
-- Description: Add audit logging for question changes
|
||||
|
||||
CREATE TABLE IF NOT EXISTS question_audit_log (
|
||||
id SERIAL PRIMARY KEY,
|
||||
question_id INTEGER NOT NULL,
|
||||
checklist_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
action VARCHAR(50) NOT NULL,
|
||||
field_name VARCHAR(100),
|
||||
old_value TEXT,
|
||||
new_value TEXT,
|
||||
comment TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- Foreign keys
|
||||
CONSTRAINT fk_question_audit_question
|
||||
FOREIGN KEY (question_id)
|
||||
REFERENCES questions(id)
|
||||
ON DELETE CASCADE,
|
||||
|
||||
CONSTRAINT fk_question_audit_checklist
|
||||
FOREIGN KEY (checklist_id)
|
||||
REFERENCES checklists(id)
|
||||
ON DELETE CASCADE,
|
||||
|
||||
CONSTRAINT fk_question_audit_user
|
||||
FOREIGN KEY (user_id)
|
||||
REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- Create indexes for better query performance
|
||||
CREATE INDEX idx_question_audit_question_id ON question_audit_log(question_id);
|
||||
CREATE INDEX idx_question_audit_checklist_id ON question_audit_log(checklist_id);
|
||||
CREATE INDEX idx_question_audit_created_at ON question_audit_log(created_at);
|
||||
CREATE INDEX idx_question_audit_action ON question_audit_log(action);
|
||||
|
||||
-- Add comment to table
|
||||
COMMENT ON TABLE question_audit_log IS 'Registro de auditoría para cambios en preguntas de checklists';
|
||||
COMMENT ON COLUMN question_audit_log.action IS 'Tipo de acción: created, updated, deleted';
|
||||
COMMENT ON COLUMN question_audit_log.field_name IS 'Nombre del campo modificado (solo para updates)';
|
||||
COMMENT ON COLUMN question_audit_log.old_value IS 'Valor anterior del campo';
|
||||
COMMENT ON COLUMN question_audit_log.new_value IS 'Valor nuevo del campo';
|
||||
Reference in New Issue
Block a user