from sqlalchemy import Column, Integer, String, Float, JSON, ForeignKey, DateTime, Text, Boolean from sqlalchemy.orm import relationship from .database import Base import datetime class Diagram(Base): __tablename__ = "diagrams" id = Column(Integer, primary_key=True, index=True) name = Column(String, nullable=False, default="Untitled Diagram") description = Column(Text, default="") created_at = Column(DateTime, default=datetime.datetime.utcnow) updated_at = Column(DateTime, default=datetime.datetime.utcnow) sheets = relationship("Sheet", back_populates="diagram", cascade="all, delete-orphan", order_by="Sheet.position") devices = relationship("Device", back_populates="diagram", cascade="all, delete-orphan") wires = relationship("Wire", back_populates="diagram", cascade="all, delete-orphan") wire_bundles = relationship("WireBundle", back_populates="diagram", cascade="all, delete-orphan") views = relationship("DiagramView", back_populates="diagram", cascade="all, delete-orphan") class WireBundle(Base): __tablename__ = "wire_bundles" id = Column(Integer, primary_key=True, index=True) diagram_id = Column(Integer, ForeignKey("diagrams.id"), nullable=False) label = Column(String, default="") jacket_color = Column(String, default="#2a2a2a") diagram = relationship("Diagram", back_populates="wire_bundles") wires = relationship("Wire", back_populates="bundle") class Sheet(Base): __tablename__ = "sheets" id = Column(Integer, primary_key=True, index=True) diagram_id = Column(Integer, ForeignKey("diagrams.id", ondelete="CASCADE"), nullable=False) name = Column(String, nullable=False, default="Sheet 1") position = Column(Integer, default=0) diagram = relationship("Diagram", back_populates="sheets") devices = relationship("Device", back_populates="sheet", cascade="all, delete-orphan") wires = relationship("Wire", back_populates="sheet", cascade="all, delete-orphan") class Device(Base): __tablename__ = "devices" id = Column(Integer, primary_key=True, index=True) diagram_id = Column(Integer, ForeignKey("diagrams.id"), nullable=False) sheet_id = Column(Integer, ForeignKey("sheets.id", ondelete="CASCADE"), nullable=True) device_type = Column(String, nullable=False) label = Column(String, default="") reference = Column(String, default="") x = Column(Float, default=100) y = Column(Float, default=100) width = Column(Float, default=120) height = Column(Float, default=60) properties = Column(JSON, default={}) pins = Column(JSON, default=[]) diagram = relationship("Diagram", back_populates="devices") sheet = relationship("Sheet", back_populates="devices") class Wire(Base): __tablename__ = "wires" id = Column(Integer, primary_key=True, index=True) diagram_id = Column(Integer, ForeignKey("diagrams.id"), nullable=False) sheet_id = Column(Integer, ForeignKey("sheets.id", ondelete="CASCADE"), nullable=True) label = Column(String, default="") from_device_id = Column(Integer, ForeignKey("devices.id"), nullable=True) from_pin = Column(String, default="") to_device_id = Column(Integer, ForeignKey("devices.id"), nullable=True) to_pin = Column(String, default="") color_primary = Column(String, default="#FF0000") color_stripe = Column(String, nullable=True) gauge = Column(String, default="18 AWG") length = Column(Float, nullable=True) length_unit = Column(String, default="in") notes = Column(Text, default="") waypoints = Column(JSON, default=[]) twisted_pair = Column(Boolean, default=False) twist_pitch = Column(Float, default=16.0) shielded = Column(Boolean, default=False) bundle_id = Column(Integer, ForeignKey("wire_bundles.id"), nullable=True) show_size_label = Column(Boolean, default=False) diagram = relationship("Diagram", back_populates="wires") sheet = relationship("Sheet", back_populates="wires") bundle = relationship("WireBundle", back_populates="wires", foreign_keys=[bundle_id]) class DiagramView(Base): __tablename__ = "diagram_views" id = Column(Integer, primary_key=True, index=True) diagram_id = Column(Integer, ForeignKey("diagrams.id", ondelete="CASCADE"), nullable=False) name = Column(String, nullable=False, default="View") route_mode = Column(String, default="direct") created_at = Column(DateTime, default=datetime.datetime.utcnow) diagram = relationship("Diagram", back_populates="views") device_positions = relationship("ViewDevicePosition", back_populates="view", cascade="all, delete-orphan") wire_waypoints = relationship("ViewWireWaypoints", back_populates="view", cascade="all, delete-orphan") class ViewDevicePosition(Base): __tablename__ = "view_device_positions" id = Column(Integer, primary_key=True, index=True) view_id = Column(Integer, ForeignKey("diagram_views.id", ondelete="CASCADE"), nullable=False) device_id = Column(Integer, ForeignKey("devices.id", ondelete="CASCADE"), nullable=False) x = Column(Float, nullable=False, default=100) y = Column(Float, nullable=False, default=100) view = relationship("DiagramView", back_populates="device_positions") class ViewWireWaypoints(Base): __tablename__ = "view_wire_waypoints" id = Column(Integer, primary_key=True, index=True) view_id = Column(Integer, ForeignKey("diagram_views.id", ondelete="CASCADE"), nullable=False) wire_id = Column(Integer, ForeignKey("wires.id", ondelete="CASCADE"), nullable=False) waypoints = Column(JSON, default=[]) view = relationship("DiagramView", back_populates="wire_waypoints") class FormboardLayout(Base): __tablename__ = "formboard_layouts" id = Column(Integer, primary_key=True, index=True) diagram_id = Column(Integer, ForeignKey("diagrams.id", ondelete="CASCADE"), nullable=False, unique=True) nodes = relationship("FormboardNode", back_populates="layout", cascade="all, delete-orphan") segments = relationship("FormboardSegment", back_populates="layout", cascade="all, delete-orphan") class FormboardNode(Base): __tablename__ = "formboard_nodes" id = Column(Integer, primary_key=True, index=True) layout_id = Column(Integer, ForeignKey("formboard_layouts.id", ondelete="CASCADE"), nullable=False) node_type = Column(String, default="branch") # connector | branch | splice device_id = Column(Integer, ForeignKey("devices.id", ondelete="SET NULL"), nullable=True) x = Column(Float, default=100) y = Column(Float, default=100) label = Column(String, default="") notes = Column(Text, default="") layout = relationship("FormboardLayout", back_populates="nodes") class FormboardSegment(Base): __tablename__ = "formboard_segments" id = Column(Integer, primary_key=True, index=True) layout_id = Column(Integer, ForeignKey("formboard_layouts.id", ondelete="CASCADE"), nullable=False) from_node_id = Column(Integer, ForeignKey("formboard_nodes.id", ondelete="CASCADE"), nullable=False) to_node_id = Column(Integer, ForeignKey("formboard_nodes.id", ondelete="CASCADE"), nullable=False) waypoints = Column(JSON, default=[]) wire_ids = Column(JSON, default=[]) # wire IDs from main diagram fitting_type = Column(String, default="open") # open | loom | conduit | heat_shrink | tape fitting_color = Column(String, default="#888888") label = Column(String, default="") length_mm = Column(Float, nullable=True) layout = relationship("FormboardLayout", back_populates="segments") class CustomConnector(Base): __tablename__ = "custom_connectors" id = Column(Integer, primary_key=True, index=True) name = Column(String, nullable=False) manufacturer = Column(String, default="") part_number = Column(String, default="") category = Column(String, default="Custom") description = Column(String, default="") pin_count = Column(Integer, nullable=False, default=2) pin_labels = Column(JSON, default=[]) created_at = Column(DateTime, default=datetime.datetime.utcnow) updated_at = Column(DateTime, default=datetime.datetime.utcnow)