backend crear endpoitns para permisos de checklist por mecanico, 1.0.30

This commit is contained in:
2025-11-25 09:22:38 -03:00
parent ad59152cce
commit eb94d8ccfc
7 changed files with 253 additions and 135 deletions

View File

@@ -617,7 +617,40 @@ def get_checklists(
query = db.query(models.Checklist)
if active_only:
query = query.filter(models.Checklist.is_active == True)
return query.offset(skip).limit(limit).all()
# Si es mecánico, solo ver checklists con permiso
if current_user.role == "mechanic":
# Obtener IDs de checklists con permiso o sin permisos (acceso global)
permitted_checklist_ids = db.query(models.ChecklistPermission.checklist_id).filter(
models.ChecklistPermission.mechanic_id == current_user.id
).distinct().all()
permitted_ids = [id[0] for id in permitted_checklist_ids]
# Checklists sin permisos = acceso global
checklists_without_permissions = db.query(models.Checklist.id).outerjoin(
models.ChecklistPermission
).group_by(models.Checklist.id).having(
func.count(models.ChecklistPermission.id) == 0
).all()
global_ids = [id[0] for id in checklists_without_permissions]
all_allowed_ids = list(set(permitted_ids + global_ids))
if all_allowed_ids:
query = query.filter(models.Checklist.id.in_(all_allowed_ids))
else:
# Si no hay permisos, devolver lista vacía
return []
checklists = query.offset(skip).limit(limit).all()
# Agregar allowed_mechanics a cada checklist
for checklist in checklists:
permissions = db.query(models.ChecklistPermission.mechanic_id).filter(
models.ChecklistPermission.checklist_id == checklist.id
).all()
checklist.allowed_mechanics = [p[0] for p in permissions]
return checklists
@app.get("/api/checklists/{checklist_id}", response_model=schemas.ChecklistWithQuestions)
@@ -629,6 +662,12 @@ def get_checklist(checklist_id: int, db: Session = Depends(get_db)):
if not checklist:
raise HTTPException(status_code=404, detail="Checklist no encontrado")
# Agregar allowed_mechanics
permissions = db.query(models.ChecklistPermission.mechanic_id).filter(
models.ChecklistPermission.checklist_id == checklist.id
).all()
checklist.allowed_mechanics = [p[0] for p in permissions]
return checklist
@@ -641,10 +680,28 @@ def create_checklist(
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="No autorizado")
db_checklist = models.Checklist(**checklist.dict(), created_by=current_user.id)
# Extraer mechanic_ids antes de crear el checklist
checklist_data = checklist.dict(exclude={'mechanic_ids'})
mechanic_ids = checklist.mechanic_ids or []
db_checklist = models.Checklist(**checklist_data, created_by=current_user.id)
db.add(db_checklist)
db.flush() # Para obtener el ID
# Crear permisos para mecánicos seleccionados
for mechanic_id in mechanic_ids:
permission = models.ChecklistPermission(
checklist_id=db_checklist.id,
mechanic_id=mechanic_id
)
db.add(permission)
db.commit()
db.refresh(db_checklist)
# Agregar allowed_mechanics a la respuesta
db_checklist.allowed_mechanics = mechanic_ids
return db_checklist
@@ -662,11 +719,38 @@ def update_checklist(
if not db_checklist:
raise HTTPException(status_code=404, detail="Checklist no encontrado")
for key, value in checklist.dict(exclude_unset=True).items():
# Extraer mechanic_ids si se envía
update_data = checklist.dict(exclude_unset=True, exclude={'mechanic_ids'})
mechanic_ids = checklist.mechanic_ids
# Actualizar campos del checklist
for key, value in update_data.items():
setattr(db_checklist, key, value)
# Si se proporcionan mechanic_ids, actualizar permisos
if mechanic_ids is not None:
# Eliminar permisos existentes
db.query(models.ChecklistPermission).filter(
models.ChecklistPermission.checklist_id == checklist_id
).delete()
# Crear nuevos permisos
for mechanic_id in mechanic_ids:
permission = models.ChecklistPermission(
checklist_id=checklist_id,
mechanic_id=mechanic_id
)
db.add(permission)
db.commit()
db.refresh(db_checklist)
# Agregar allowed_mechanics a la respuesta
permissions = db.query(models.ChecklistPermission.mechanic_id).filter(
models.ChecklistPermission.checklist_id == checklist_id
).all()
db_checklist.allowed_mechanics = [p[0] for p in permissions]
return db_checklist

View File

@@ -55,6 +55,7 @@ class Checklist(Base):
creator = relationship("User", back_populates="checklists_created")
questions = relationship("Question", back_populates="checklist", cascade="all, delete-orphan")
inspections = relationship("Inspection", back_populates="checklist")
permissions = relationship("ChecklistPermission", back_populates="checklist", cascade="all, delete-orphan")
class Question(Base):
@@ -186,3 +187,17 @@ class AIConfiguration(Base):
is_active = Column(Boolean, default=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
class ChecklistPermission(Base):
"""Tabla intermedia para permisos de checklist por mecánico"""
__tablename__ = "checklist_permissions"
id = Column(Integer, primary_key=True, index=True)
checklist_id = Column(Integer, ForeignKey("checklists.id", ondelete="CASCADE"), nullable=False)
mechanic_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
# Relationships
checklist = relationship("Checklist", back_populates="permissions")
mechanic = relationship("User")

View File

@@ -70,10 +70,11 @@ class ChecklistBase(BaseModel):
logo_url: Optional[str] = None
class ChecklistCreate(ChecklistBase):
pass
mechanic_ids: Optional[List[int]] = [] # IDs de mecánicos autorizados
class ChecklistUpdate(ChecklistBase):
is_active: Optional[bool] = None
mechanic_ids: Optional[List[int]] = None # IDs de mecánicos autorizados
class Checklist(ChecklistBase):
id: int
@@ -81,6 +82,7 @@ class Checklist(ChecklistBase):
is_active: bool
created_by: int
created_at: datetime
allowed_mechanics: Optional[List[int]] = [] # IDs de mecánicos permitidos
class Config:
from_attributes = True

View File

@@ -1,122 +0,0 @@
from fastapi import FastAPI, File, UploadFile, Form, Depends, HTTPException, status
from fastapi.middleware.cors import CORSMiddleware
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.orm import Session, joinedload
from sqlalchemy import func, case
from typing import List, Optional
import os
import boto3
from botocore.client import Config
import uuid
from app.core import config as app_config
from app.core.database import engine, get_db, Base
from app.core.security import verify_password, get_password_hash, create_access_token, decode_access_token
from app import models, schemas
import shutil
from datetime import datetime, timedelta
BACKEND_VERSION = "1.0.25"
app = FastAPI(title="Checklist Inteligente API", version=BACKEND_VERSION)
# S3/MinIO configuration
S3_ENDPOINT = app_config.MINIO_ENDPOINT
S3_ACCESS_KEY = app_config.MINIO_ACCESS_KEY
S3_SECRET_KEY = app_config.MINIO_SECRET_KEY
S3_IMAGE_BUCKET = app_config.MINIO_IMAGE_BUCKET
S3_PDF_BUCKET = app_config.MINIO_PDF_BUCKET
s3_client = boto3.client(
's3',
endpoint_url=S3_ENDPOINT,
aws_access_key_id=S3_ACCESS_KEY,
aws_secret_access_key=S3_SECRET_KEY,
config=Config(signature_version='s3v4'),
region_name='us-east-1'
)
# Crear tablas
Base.metadata.create_all(bind=engine)
# Información visual al iniciar el backend
print("\n================ BACKEND STARTUP INFO ================")
print(f"Backend version: {BACKEND_VERSION}")
print(f"Database URL: {app_config.settings.DATABASE_URL}")
print(f"Environment: {app_config.settings.ENVIRONMENT}")
print(f"MinIO endpoint: {app_config.MINIO_ENDPOINT}")
print("====================================================\n", flush=True)
# CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173", "http://localhost:3000"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Simulación de modelos y autenticación para ejemplo
class User:
def __init__(self, role):
self.role = role
class AIConfiguration:
is_active = True
logo_url = ""
class models:
User = User
AIConfiguration = AIConfiguration
# Simulación de get_db y get_current_user
def get_db():
# Aquí iría la lógica real de SQLAlchemy
class DummyDB:
def query(self, model):
return self
def filter(self, *args, **kwargs):
return self
def first(self):
return models.AIConfiguration()
def commit(self):
pass
def refresh(self, obj):
pass
return DummyDB()
def get_current_user():
# Aquí iría la lógica real de autenticación
return models.User(role="admin")
# Endpoint para subir el logo
@app.post("/api/config/logo", response_model=dict)
async def upload_logo(
file: UploadFile = File(...),
db: Session = Depends(get_db),
current_user: models.User = Depends(get_current_user)
):
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="Solo administradores pueden cambiar el logo")
# Subir imagen a MinIO/S3
file_extension = file.filename.split(".")[-1]
now = datetime.now()
folder = "logo"
file_name = f"logo_{now.strftime('%Y%m%d_%H%M%S')}.{file_extension}"
s3_key = f"{folder}/{file_name}"
# s3_client.upload_fileobj(file.file, S3_IMAGE_BUCKET, s3_key, ExtraArgs={"ContentType": file.content_type})
logo_url = f"https://minio.example.com/bucket/{s3_key}" # Ajusta según tu config
# Actualiza la configuración en la base de datos
# config = db.query(models.AIConfiguration).filter(models.AIConfiguration.is_active == True).first()
# if config:
# config.logo_url = logo_url
# db.commit()
return {"logo_url": logo_url}
# Endpoint para obtener el logo
@app.get("/api/config/logo", response_model=dict)
def get_logo_url(db: Session = Depends(get_db)):
# config = db.query(models.AIConfiguration).filter(models.AIConfiguration.is_active == True).first()
# if config and getattr(config, "logo_url", None):
# return {"logo_url": config.logo_url}
# return {"logo_url": "https://minio.example.com/bucket/logo/default_logo.png"}
return {"logo_url": "https://minio.example.com/bucket/logo/default_logo.png"}