Files
checklist/backend/app/models.py
ronalds 2b5424790a feat: AI prompts personalizados por pregunta - backend v1.0.13, frontend v1.0.21
Backend:
- Agregar campo ai_prompt a tabla questions
- Endpoint analyze-image recibe custom_prompt
- Validación de imagen apropiada (sugiere cambiar foto si no corresponde)
- Script de migración migrate_ai_prompt.py

Frontend:
- Campo de texto para configurar prompt de IA en editor de preguntas
- Envía custom_prompt al endpoint de análisis
- UI con fondo morado para sección de IA

La IA ahora analiza fotos según el contexto específico de cada pregunta
y sugiere cambiar la imagen si no corresponde al componente solicitado.
2025-11-21 02:41:50 -03:00

186 lines
7.4 KiB
Python

from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, Text, JSON, Float
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from app.core.database import Base
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String(50), unique=True, index=True, nullable=False)
email = Column(String(100), unique=True, index=True)
password_hash = Column(String(255), nullable=False)
role = Column(String(20), nullable=False) # admin, mechanic, asesor
full_name = Column(String(100))
is_active = Column(Boolean, default=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
# Relationships
checklists_created = relationship("Checklist", back_populates="creator")
inspections = relationship("Inspection", back_populates="mechanic")
api_tokens = relationship("APIToken", back_populates="user", cascade="all, delete-orphan")
class APIToken(Base):
__tablename__ = "api_tokens"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
token = Column(String(100), unique=True, index=True, nullable=False)
description = Column(String(200))
is_active = Column(Boolean, default=True)
last_used_at = Column(DateTime(timezone=True))
created_at = Column(DateTime(timezone=True), server_default=func.now())
# Relationship
user = relationship("User", back_populates="api_tokens")
class Checklist(Base):
__tablename__ = "checklists"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(200), nullable=False)
description = Column(Text)
ai_mode = Column(String(20), default="off") # off, assisted, copilot
scoring_enabled = Column(Boolean, default=True)
max_score = Column(Integer, default=0)
logo_url = Column(String(500))
is_active = Column(Boolean, default=True)
created_by = Column(Integer, ForeignKey("users.id"))
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Relationships
creator = relationship("User", back_populates="checklists_created")
questions = relationship("Question", back_populates="checklist", cascade="all, delete-orphan")
inspections = relationship("Inspection", back_populates="checklist")
class Question(Base):
__tablename__ = "questions"
id = Column(Integer, primary_key=True, index=True)
checklist_id = Column(Integer, ForeignKey("checklists.id"), nullable=False)
section = Column(String(100)) # Sistema eléctrico, Frenos, etc
text = Column(Text, nullable=False)
type = Column(String(30), nullable=False) # pass_fail, good_bad, text, etc
points = Column(Integer, default=1)
options = Column(JSON) # Para multiple choice
order = Column(Integer, default=0)
allow_photos = Column(Boolean, default=True)
max_photos = Column(Integer, default=3)
requires_comment_on_fail = Column(Boolean, default=False)
# Conditional logic
parent_question_id = Column(Integer, ForeignKey("questions.id"), nullable=True)
show_if_answer = Column(String(50), nullable=True) # Valor que dispara esta pregunta
# AI Analysis
ai_prompt = Column(Text, nullable=True) # Prompt personalizado para análisis de IA de esta pregunta
created_at = Column(DateTime(timezone=True), server_default=func.now())
# Relationships
checklist = relationship("Checklist", back_populates="questions")
answers = relationship("Answer", back_populates="question")
subquestions = relationship("Question", backref="parent", remote_side=[id])
class Inspection(Base):
__tablename__ = "inspections"
id = Column(Integer, primary_key=True, index=True)
checklist_id = Column(Integer, ForeignKey("checklists.id"), nullable=False)
mechanic_id = Column(Integer, ForeignKey("users.id"), nullable=False)
# Datos de la OR
or_number = Column(String(50))
work_order_number = Column(String(50))
# Datos del vehículo
vehicle_plate = Column(String(20), nullable=False, index=True)
vehicle_brand = Column(String(50))
vehicle_model = Column(String(100))
vehicle_km = Column(Integer)
client_name = Column(String(200))
# Scoring
score = Column(Integer, default=0)
max_score = Column(Integer, default=0)
percentage = Column(Float, default=0.0)
flagged_items_count = Column(Integer, default=0)
# Estado
status = Column(String(20), default="draft") # draft, completed, inactive
is_active = Column(Boolean, default=True)
# Firma
signature_data = Column(Text) # Base64 de la firma
signed_at = Column(DateTime(timezone=True))
# Timestamps
started_at = Column(DateTime(timezone=True), server_default=func.now())
completed_at = Column(DateTime(timezone=True))
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Relationships
checklist = relationship("Checklist", back_populates="inspections")
mechanic = relationship("User", back_populates="inspections")
answers = relationship("Answer", back_populates="inspection", cascade="all, delete-orphan")
class Answer(Base):
__tablename__ = "answers"
id = Column(Integer, primary_key=True, index=True)
inspection_id = Column(Integer, ForeignKey("inspections.id"), nullable=False)
question_id = Column(Integer, ForeignKey("questions.id"), nullable=False)
answer_value = Column(Text) # La respuesta del mecánico
status = Column(String(20), default="ok") # ok, warning, critical, info
points_earned = Column(Integer, default=0)
comment = Column(Text) # Comentarios adicionales
ai_analysis = Column(JSON) # Análisis de IA si aplica
is_flagged = Column(Boolean, default=False) # Si requiere atención
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Relationships
inspection = relationship("Inspection", back_populates="answers")
question = relationship("Question", back_populates="answers")
media_files = relationship("MediaFile", back_populates="answer", cascade="all, delete-orphan")
class MediaFile(Base):
__tablename__ = "media_files"
id = Column(Integer, primary_key=True, index=True)
answer_id = Column(Integer, ForeignKey("answers.id"), nullable=False)
file_path = Column(String(500), nullable=False)
file_type = Column(String(20), default="image") # image, video
caption = Column(Text)
order = Column(Integer, default=0)
uploaded_at = Column(DateTime(timezone=True), server_default=func.now())
# Relationships
answer = relationship("Answer", back_populates="media_files")
class AIConfiguration(Base):
__tablename__ = "ai_configurations"
id = Column(Integer, primary_key=True, index=True)
provider = Column(String(50), nullable=False) # openai, gemini
api_key = Column(Text, nullable=False)
model_name = Column(String(100), nullable=False)
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())