"""Location + LocationType models.""" from shopdb.extensions import db from .base import BaseModel def derive_locationcode(locationname): """Operation/short code for a location = leading token of its name. Locations have no dedicated code column, so the operation code is the leading whitespace-delimited token of the location name. Example: '0615 Blisk Inspection' -> '0615'. Blank/None name -> None. Used by printed labels that encode a tool's inspection operation rather than the tool itself. """ if not locationname: return None parts = str(locationname).strip().split() return parts[0] if parts else None class LocationType(BaseModel): """Location classification (ADR-001 shared reference data). Seeded values: section, cell, subcell, operation, meetingroom, lab, office, storage, hallway, networkcloset, building. Sites can extend. """ __tablename__ = 'locationtypes' locationtypeid = db.Column(db.Integer, primary_key=True) locationtype = db.Column(db.String(50), unique=True, nullable=False) description = db.Column(db.Text) color = db.Column(db.String(20), comment='CSS color for UI') def __repr__(self): return f"" class Location(BaseModel): """Physical location model.""" __tablename__ = 'locations' locationid = db.Column(db.Integer, primary_key=True) locationname = db.Column(db.String(100), unique=True, nullable=False) building = db.Column(db.String(100)) floor = db.Column(db.String(50)) room = db.Column(db.String(50)) description = db.Column(db.Text) # Classification + tree (ADR-001) locationtypeid = db.Column( db.Integer, db.ForeignKey('locationtypes.locationtypeid'), nullable=True ) parentlocationid = db.Column( db.Integer, db.ForeignKey('locations.locationid'), nullable=True, comment='Parent location for the site location tree' ) # Map configuration mapimage = db.Column(db.String(500), comment='Path to floor map image') mapwidth = db.Column(db.Integer) mapheight = db.Column(db.Integer) # Location-level position used as fallback when an asset has no own coords # and no inheritsposition relationship resolves. ADR-001 position resolution # chain priority 3. mapx = db.Column(db.Integer, comment='Default X coordinate for assets at this location') mapy = db.Column(db.Integer, comment='Default Y coordinate for assets at this location') # Which drawing those coordinates are pixels of (ADR-017). A location is on a # level as much as an asset is, and an asset with no position of its own # inherits BOTH the coordinates and the level from here - inheriting the # coordinates alone would draw them on whatever level the asset claims. levelid = db.Column( db.Integer, db.ForeignKey('maplevels.levelid'), nullable=True, index=True, comment='Which level mapx/mapy are pixels of (ADR-017)') # Relationships locationtype = db.relationship('LocationType') parent = db.relationship('Location', remote_side=[locationid]) @property def locationcode(self): """Derived operation/short code (leading token of the name).""" return derive_locationcode(self.locationname) def to_dict(self): data = super().to_dict() data['locationtypename'] = self.locationtype.locationtype if self.locationtype else None data['parentlocationname'] = self.parent.locationname if self.parent else None data['locationcode'] = self.locationcode return data def __repr__(self): return f""