sumaq/backend/app/models/models.py

178 lines
6.9 KiB
Python

from sqlalchemy import Table, Column, Integer, String, Boolean, ForeignKey, DateTime, Text, Enum, JSON
from sqlalchemy.orm import relationship
from app.db.database import Base
import datetime
import enum
# Junction Tables
project_specialties = Table(
'project_specialties',
Base.metadata,
Column('project_id', Integer, ForeignKey('projects.id'), primary_key=True),
Column('specialty_id', Integer, ForeignKey('specialties.id'), primary_key=True)
)
project_contractors = Table(
'project_contractors',
Base.metadata,
Column('project_id', Integer, ForeignKey('projects.id'), primary_key=True),
Column('contractor_id', Integer, ForeignKey('contractors.id'), primary_key=True)
)
class UserRole(str, enum.Enum):
ADMIN = "admin"
DIRECTOR = "director"
SUPERVISOR = "supervisor"
COORDINATOR = "coordinator"
CONTRACTOR = "contractor"
CLIENT = "client"
class ActivityType(str, enum.Enum):
INSPECTION = "inspection"
MEETING = "meeting"
VIRTUAL_MEETING = "virtual_meeting"
COORDINATION = "coordination"
TEST = "test"
OTHER = "other"
class NCLevel(str, enum.Enum):
CRITICAL = "critical"
MAJOR = "major"
MINOR = "minor"
class NCType(str, enum.Enum):
HUMAN_ERROR = "Errores humanos"
PROCESS_FAILURE = "Fallas en los procesos"
DESIGN_ISSUE = "Problemas de diseño"
UNCONTROLLED_CHANGE = "Cambios no controlados"
COMMUNICATION_FAILURE = "Falta de comunicación"
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String, unique=True, index=True, nullable=False)
hashed_password = Column(String, nullable=False)
full_name = Column(String)
role = Column(Enum(UserRole), default=UserRole.SUPERVISOR)
is_active = Column(Boolean, default=True)
class Project(Base):
__tablename__ = "projects"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True, nullable=False)
code = Column(String, unique=True, index=True)
location = Column(String)
start_date = Column(DateTime)
end_date = Column(DateTime)
status = Column(String, default="active")
parent_id = Column(Integer, ForeignKey("projects.id"), nullable=True)
# Relationships
parent = relationship("Project", remote_side=[id], back_populates="subprojects")
subprojects = relationship("Project", back_populates="parent")
activities = relationship("Activity", back_populates="project")
specialties = relationship("Specialty", secondary=project_specialties)
contractors = relationship("Contractor", secondary=project_contractors)
class Specialty(Base):
__tablename__ = "specialties"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, unique=True, nullable=False) # Civil, Mecánica, Eléctrica, SSOMA
class Contractor(Base):
__tablename__ = "contractors"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, nullable=False)
ruc = Column(String, unique=True)
contact_name = Column(String)
email = Column(String, nullable=True)
phone = Column(String, nullable=True)
address = Column(String, nullable=True)
is_active = Column(Boolean, default=True)
specialty_id = Column(Integer, ForeignKey("specialties.id"), nullable=True)
parent_id = Column(Integer, ForeignKey("contractors.id"), nullable=True)
specialty = relationship("Specialty")
parent = relationship("Contractor", remote_side=[id], back_populates="subcontractors")
subcontractors = relationship("Contractor", back_populates="parent")
class Activity(Base):
__tablename__ = "activities"
id = Column(Integer, primary_key=True, index=True)
project_id = Column(Integer, ForeignKey("projects.id"))
specialty_id = Column(Integer, ForeignKey("specialties.id"))
contractor_id = Column(Integer, ForeignKey("contractors.id"), nullable=True)
user_id = Column(Integer, ForeignKey("users.id")) # Reporter
date = Column(DateTime, default=datetime.datetime.utcnow)
end_date = Column(DateTime, nullable=True)
type = Column(Enum(ActivityType), default=ActivityType.INSPECTION)
area = Column(String) # Frente de obra / Linea
description = Column(Text)
observations = Column(Text)
audio_transcription = Column(Text, nullable=True)
status = Column(String, default="completed")
project = relationship("Project", back_populates="activities")
specialty = relationship("Specialty")
contractor = relationship("Contractor")
reporter = relationship("User")
non_conformities = relationship("NonConformity", back_populates="activity")
evidences = relationship("Evidence", back_populates="activity")
class NonConformity(Base):
__tablename__ = "non_conformities"
id = Column(Integer, primary_key=True, index=True)
activity_id = Column(Integer, ForeignKey("activities.id"))
level = Column(Enum(NCLevel), default=NCLevel.MINOR)
description = Column(Text, nullable=False)
status = Column(String, default="open") # open, closed
# New Fields
due_date = Column(DateTime, nullable=True)
responsible_person = Column(String, nullable=True) # Legend/Name
responsible_email = Column(String, nullable=True) # For guest access
access_hash = Column(String, unique=True, index=True, nullable=True)
contractor_id = Column(Integer, ForeignKey("contractors.id"), nullable=True)
action_checklist = Column(JSON, nullable=True) # List of dicts {text: str, completed: bool}
nc_type = Column(Enum(NCType), nullable=True)
impact_description = Column(Text, nullable=True)
closure_description = Column(Text, nullable=True)
guest_actions = Column(Text, nullable=True) # Field for guest to describe actions taken
parent_id = Column(Integer, ForeignKey("non_conformities.id"), nullable=True)
activity = relationship("Activity", back_populates="non_conformities")
contractor = relationship("Contractor")
evidences = relationship("Evidence", back_populates="non_conformity")
parent = relationship("NonConformity", remote_side=[id], back_populates="child_ncs")
child_ncs = relationship("NonConformity", back_populates="parent")
class Evidence(Base):
__tablename__ = "evidences"
id = Column(Integer, primary_key=True, index=True)
activity_id = Column(Integer, ForeignKey("activities.id"), nullable=True)
non_conformity_id = Column(Integer, ForeignKey("non_conformities.id"), nullable=True)
file_path = Column(String, nullable=False)
media_type = Column(String) # image, video, document
description = Column(String)
captured_at = Column(DateTime, default=datetime.datetime.utcnow)
# Transcription fields for audio
transcription = Column(Text, nullable=True)
transcription_status = Column(String, default="none") # none, pending, processing, completed, error
activity = relationship("Activity", back_populates="evidences")
non_conformity = relationship("NonConformity", back_populates="evidences")