Initial commit — wiring diagram maker with full feature set
- FastAPI + SQLite backend with routers for diagrams, devices, wires, bundles, connectors, export, and Octopart/Nexar search - Konva.js canvas: ortho/direct/curved routing, wire crossings, harness mode, snap-to-grid, multi-select, drag, resize, undo - Wire features: color/stripe, twisted pair helix, shielded glow, gauge size label, multi-core bundle grouping, length/unit tracking - Device library: connector, terminal block, cable, splice, label, group, relay, fuse, switch, component with custom connector support - Exports: BOM CSV, assembly TXT, JSON, formboard layout, PNG image - Auto-migration for schema changes via ALTER TABLE at startup Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
from pydantic import BaseModel, field_validator
|
||||
from typing import Optional, List, Any, Dict
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class DeviceBase(BaseModel):
|
||||
device_type: str
|
||||
label: str = ""
|
||||
reference: str = ""
|
||||
x: float = 100
|
||||
y: float = 100
|
||||
width: float = 120
|
||||
height: float = 60
|
||||
properties: Dict[str, Any] = {}
|
||||
pins: List[Dict[str, Any]] = []
|
||||
|
||||
|
||||
class DeviceCreate(DeviceBase):
|
||||
diagram_id: int
|
||||
|
||||
|
||||
class DeviceUpdate(BaseModel):
|
||||
label: Optional[str] = None
|
||||
reference: Optional[str] = None
|
||||
x: Optional[float] = None
|
||||
y: Optional[float] = None
|
||||
width: Optional[float] = None
|
||||
height: Optional[float] = None
|
||||
properties: Optional[Dict[str, Any]] = None
|
||||
pins: Optional[List[Dict[str, Any]]] = None
|
||||
|
||||
|
||||
class Device(DeviceBase):
|
||||
id: int
|
||||
diagram_id: int
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class WireBase(BaseModel):
|
||||
label: str = ""
|
||||
from_device_id: Optional[int] = None
|
||||
from_pin: str = ""
|
||||
to_device_id: Optional[int] = None
|
||||
to_pin: str = ""
|
||||
color_primary: str = "#FF0000"
|
||||
color_stripe: Optional[str] = None
|
||||
gauge: str = "18 AWG"
|
||||
length: Optional[float] = None
|
||||
length_unit: str = "in"
|
||||
notes: str = ""
|
||||
waypoints: List[Dict[str, Any]] = []
|
||||
twisted_pair: bool = False
|
||||
twist_pitch: float = 16.0
|
||||
shielded: bool = False
|
||||
bundle_id: Optional[int] = None
|
||||
show_size_label: bool = False
|
||||
|
||||
@field_validator("waypoints", mode="before")
|
||||
@classmethod
|
||||
def _coerce_waypoints(cls, v):
|
||||
return v if v is not None else []
|
||||
|
||||
|
||||
class WireCreate(WireBase):
|
||||
diagram_id: int
|
||||
|
||||
|
||||
class WireUpdate(BaseModel):
|
||||
label: Optional[str] = None
|
||||
from_device_id: Optional[int] = None
|
||||
from_pin: Optional[str] = None
|
||||
to_device_id: Optional[int] = None
|
||||
to_pin: Optional[str] = None
|
||||
color_primary: Optional[str] = None
|
||||
color_stripe: Optional[str] = None
|
||||
gauge: Optional[str] = None
|
||||
length: Optional[float] = None
|
||||
length_unit: Optional[str] = None
|
||||
notes: Optional[str] = None
|
||||
waypoints: Optional[List[Dict[str, Any]]] = None
|
||||
twisted_pair: Optional[bool] = None
|
||||
twist_pitch: Optional[float] = None
|
||||
shielded: Optional[bool] = None
|
||||
bundle_id: Optional[int] = None
|
||||
show_size_label: Optional[bool] = None
|
||||
|
||||
|
||||
class Wire(WireBase):
|
||||
id: int
|
||||
diagram_id: int
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
# ── WireBundle ────────────────────────────────────────────────────────────────
|
||||
|
||||
class WireBundleBase(BaseModel):
|
||||
label: str = ""
|
||||
jacket_color: str = "#2a2a2a"
|
||||
|
||||
|
||||
class WireBundleCreate(WireBundleBase):
|
||||
diagram_id: int
|
||||
|
||||
|
||||
class WireBundleUpdate(BaseModel):
|
||||
label: Optional[str] = None
|
||||
jacket_color: Optional[str] = None
|
||||
|
||||
|
||||
class WireBundleOut(WireBundleBase):
|
||||
id: int
|
||||
diagram_id: int
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
# ── Diagram ───────────────────────────────────────────────────────────────────
|
||||
|
||||
class DiagramBase(BaseModel):
|
||||
name: str = "Untitled Diagram"
|
||||
description: str = ""
|
||||
|
||||
|
||||
class DiagramCreate(DiagramBase):
|
||||
pass
|
||||
|
||||
|
||||
class DiagramUpdate(BaseModel):
|
||||
name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
|
||||
|
||||
class DiagramSummary(DiagramBase):
|
||||
id: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class DiagramFull(DiagramSummary):
|
||||
devices: List[Device] = []
|
||||
wires: List[Wire] = []
|
||||
wire_bundles: List[WireBundleOut] = []
|
||||
|
||||
|
||||
# ── CustomConnector ───────────────────────────────────────────────────────────
|
||||
|
||||
class CustomConnectorBase(BaseModel):
|
||||
name: str
|
||||
manufacturer: str = ""
|
||||
part_number: str = ""
|
||||
category: str = "Custom"
|
||||
description: str = ""
|
||||
pin_count: int
|
||||
pin_labels: List[str] = []
|
||||
|
||||
|
||||
class CustomConnectorCreate(CustomConnectorBase):
|
||||
pass
|
||||
|
||||
|
||||
class CustomConnectorUpdate(BaseModel):
|
||||
name: Optional[str] = None
|
||||
manufacturer: Optional[str] = None
|
||||
part_number: Optional[str] = None
|
||||
category: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
pin_count: Optional[int] = None
|
||||
pin_labels: Optional[List[str]] = None
|
||||
|
||||
|
||||
class CustomConnector(CustomConnectorBase):
|
||||
id: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
Reference in New Issue
Block a user