Files
boc/iom/defect/defect_codes.py
T
Bernt bae705aa97 ARCHITECTURE: NFC roadmap, edge AI, audit logging
- Add NFC ePassport roadmap (ICAO 9303, eIDAS)
- Add TensorFlow.js edge face detection (BlazeFace)
- Add structured audit logger (GDPR-compliant)
- Risk scoring support

Part of KYC Apple Native UX v1.1.0
2026-06-29 16:24:48 +00:00

626 lines
23 KiB
Python

"""
Defect Codes - Layer 7 of IOM
Hierarchical classification system for infrastructure defects
"""
from typing import Dict, List, Optional, Tuple
from enum import Enum
class DefectCategory(str, Enum):
"""Top-level defect categories"""
SURFACE = "surface"
STRUCTURAL = "structural"
COMPONENT = "component"
BLOCKAGE = "blockage"
ENVIRONMENTAL = "environmental"
CORROSION = "corrosion"
SAFETY = "safety"
FUNCTIONAL = "functional"
class DefectSeverity(str, Enum):
"""Defect severity levels"""
COSMETIC = "cosmetic" # Purely visual
MINOR = "minor" # Minor issue, no immediate action
MODERATE = "moderate" # Requires attention within 12 months
MAJOR = "major" # Requires attention within 3 months
CRITICAL = "critical" # Immediate action required
class DefectCode:
"""Represents a defect code with metadata"""
def __init__(
self,
code: str,
name_sv: str,
name_en: str,
category: DefectCategory,
severity: DefectSeverity,
description_sv: str = "",
description_en: str = "",
parent_code: Optional[str] = None,
ai_keywords: Optional[List[str]] = None
):
self.code = code
self.name_sv = name_sv
self.name_en = name_en
self.category = category
self.severity = severity
self.description_sv = description_sv
self.description_en = description_en
self.parent_code = parent_code
self.ai_keywords = ai_keywords or []
def get_name(self, lang: str = "sv") -> str:
"""Get name in specified language"""
return self.name_en if lang == "en" else self.name_sv
def get_description(self, lang: str = "sv") -> str:
"""Get description in specified language"""
return self.description_en if lang == "en" else self.description_sv
def to_dict(self, lang: str = "sv") -> Dict:
"""Convert to dictionary"""
return {
"code": self.code,
"name": self.get_name(lang),
"description": self.get_description(lang),
"category": self.category.value,
"severity": self.severity.value,
"parent_code": self.parent_code,
"ai_keywords": self.ai_keywords
}
class DefectRegistry:
"""Registry of all defect codes"""
def __init__(self):
self._codes: Dict[str, DefectCode] = {}
self._init_codes()
def _init_codes(self):
"""Initialize all defect codes"""
# 1000 - Corrosion
self._add(DefectCode(
"1000", "Korrosion", "Corrosion",
DefectCategory.CORROSION, DefectSeverity.MODERATE,
"Korrosion på metalliska material",
"Corrosion on metallic materials",
ai_keywords=["rust", "corrosion", "oxidation", "pit"]
))
self._add(DefectCode(
"1100", "Ytrost", "Surface Rust",
DefectCategory.CORROSION, DefectSeverity.MINOR,
"Ytlig rost på yta",
"Surface rust on surface",
parent_code="1000",
ai_keywords=["surface rust", "light rust", "orange"]
))
self._add(DefectCode(
"1200", "Genomrost", "Deep Corrosion",
DefectCategory.CORROSION, DefectSeverity.MAJOR,
"Genomgående korrosion som påverkar struktur",
"Deep corrosion affecting structure",
parent_code="1000",
ai_keywords=["deep rust", "structural rust", "hole"]
))
self._add(DefectCode(
"1250", "Galvanisk korrosion", "Galvanic Corrosion",
DefectCategory.CORROSION, DefectSeverity.MODERATE,
"Korrosion mellan olika metaller",
"Corrosion between dissimilar metals",
parent_code="1000",
ai_keywords=["galvanic", "dissimilar metal"]
))
# 1300 - Concrete Cracks
self._add(DefectCode(
"1300", "Betongspricka", "Concrete Crack",
DefectCategory.STRUCTURAL, DefectSeverity.MODERATE,
"Spricka i betongkonstruktion",
"Crack in concrete structure",
ai_keywords=["crack", "fissure", "split"]
))
self._add(DefectCode(
"1310", "Plastisk krympning", "Plastic Shrinkage",
DefectCategory.STRUCTURAL, DefectSeverity.MINOR,
"Spricka från plastisk krympning",
"Crack from plastic shrinkage",
parent_code="1300",
ai_keywords=["shrinkage crack", "surface crack"]
))
self._add(DefectCode(
"1320", "Temperaturspricka", "Temperature Crack",
DefectCategory.STRUCTURAL, DefectSeverity.MINOR,
"Spricka från temperaturförändring",
"Crack from temperature change",
parent_code="1300",
ai_keywords=["temperature crack", "thermal crack"]
))
self._add(DefectCode(
"1330", "Lastspricka", "Load Crack",
DefectCategory.STRUCTURAL, DefectSeverity.MAJOR,
"Spricka från överbelastning",
"Crack from overload",
parent_code="1300",
ai_keywords=["load crack", "structural crack", "wide crack"]
))
# 1400 - Impact Damage
self._add(DefectCode(
"1400", "Påkörningsskada", "Impact Damage",
DefectCategory.STRUCTURAL, DefectSeverity.MAJOR,
"Skada från påkörning eller stöt",
"Damage from impact or collision",
ai_keywords=["impact", "collision", "dent", "damage"]
))
self._add(DefectCode(
"1410", "Ytskada", "Surface Damage",
DefectCategory.SURFACE, DefectSeverity.MINOR,
"Ytlig skada på yta",
"Superficial damage to surface",
parent_code="1400",
ai_keywords=["scratch", "scuff", "minor damage"]
))
self._add(DefectCode(
"1420", "Deformation", "Deformation",
DefectCategory.STRUCTURAL, DefectSeverity.MODERATE,
"Permanent deformation",
"Permanent deformation",
parent_code="1400",
ai_keywords=["bent", "deformed", "twisted"]
))
self._add(DefectCode(
"1430", "Genomskjutning", "Penetration",
DefectCategory.STRUCTURAL, DefectSeverity.CRITICAL,
"Genomskjutning av konstruktion",
"Penetration of structure",
parent_code="1400",
ai_keywords=["hole", "penetration", "puncture"]
))
# 1500 - Missing Component
self._add(DefectCode(
"1500", "Saknad komponent", "Missing Component",
DefectCategory.COMPONENT, DefectSeverity.MODERATE,
"Komponent saknas",
"Component is missing",
ai_keywords=["missing", "absent", "gone"]
))
self._add(DefectCode(
"1510", "Stöld", "Theft",
DefectCategory.COMPONENT, DefectSeverity.MAJOR,
"Komponent stulen",
"Component stolen",
parent_code="1500",
ai_keywords=["theft", "stolen", "removed"]
))
self._add(DefectCode(
"1520", "Ej installerad", "Not Installed",
DefectCategory.COMPONENT, DefectSeverity.MINOR,
"Komponent ej installerad",
"Component not installed",
parent_code="1500",
ai_keywords=["not installed", "missing installation"]
))
self._add(DefectCode(
"1530", "Avlägsnad", "Removed",
DefectCategory.COMPONENT, DefectSeverity.MINOR,
"Komponent avlägsnad",
"Component removed",
parent_code="1500",
ai_keywords=["removed", "taken away"]
))
# 1600 - Deformation/Settlement
self._add(DefectCode(
"1600", "Lutning / Sättning", "Tilt / Settlement",
DefectCategory.STRUCTURAL, DefectSeverity.MODERATE,
"Lutning eller sättning",
"Tilt or settlement",
ai_keywords=["tilt", "lean", "settlement", "sinking"]
))
self._add(DefectCode(
"1610", "Sättning", "Settlement",
DefectCategory.STRUCTURAL, DefectSeverity.MAJOR,
"Sättning av fundament",
"Foundation settlement",
parent_code="1600",
ai_keywords=["settlement", "sinking", "subsidence"]
))
self._add(DefectCode(
"1620", "Böjning", "Bending",
DefectCategory.STRUCTURAL, DefectSeverity.MODERATE,
"Böjning av konstruktion",
"Bending of structure",
parent_code="1600",
ai_keywords=["bending", "sagging", "deflection"]
))
self._add(DefectCode(
"1630", "Vridning", "Torsion",
DefectCategory.STRUCTURAL, DefectSeverity.MODERATE,
"Vridning av konstruktion",
"Torsion of structure",
parent_code="1600",
ai_keywords=["twist", "torsion", "warp"]
))
# 1700 - Vibration
self._add(DefectCode(
"1700", "Vibration", "Vibration",
DefectCategory.FUNCTIONAL, DefectSeverity.MODERATE,
"Oönskad vibration",
"Unwanted vibration",
ai_keywords=["vibration", "shaking", "oscillation"]
))
self._add(DefectCode(
"1710", "Mekanisk vibration", "Mechanical Vibration",
DefectCategory.FUNCTIONAL, DefectSeverity.MODERATE,
"Vibration från mekanisk källa",
"Vibration from mechanical source",
parent_code="1700",
ai_keywords=["mechanical vibration", "machinery"]
))
self._add(DefectCode(
"1720", "Aerodynamisk vibration", "Aerodynamic Vibration",
DefectCategory.FUNCTIONAL, DefectSeverity.MINOR,
"Vibration från vind",
"Vibration from wind",
parent_code="1700",
ai_keywords=["wind vibration", "aerodynamic", "flutter"]
))
# 1800 - Vegetation
self._add(DefectCode(
"1800", "Vegetation", "Vegetation",
DefectCategory.ENVIRONMENTAL, DefectSeverity.MINOR,
"Oönskad vegetation",
"Unwanted vegetation",
ai_keywords=["vegetation", "plant", "tree", "weed"]
))
self._add(DefectCode(
"1810", "Blockerande vegetation", "Blocking Vegetation",
DefectCategory.ENVIRONMENTAL, DefectSeverity.MODERATE,
"Vegetation som blockerar sikt eller tillgång",
"Vegetation blocking view or access",
parent_code="1800",
ai_keywords=["blocking", "overgrown", "obscured"]
))
self._add(DefectCode(
"1820", "Rotinträngning", "Root Intrusion",
DefectCategory.STRUCTURAL, DefectSeverity.MAJOR,
"Rötter som tränger in i struktur",
"Roots intruding into structure",
parent_code="1800",
ai_keywords=["root", "intrusion", "crack with root"]
))
self._add(DefectCode(
"1830", "Fuktbevarande vegetation", "Moisture-Retaining Vegetation",
DefectCategory.ENVIRONMENTAL, DefectSeverity.MINOR,
"Vegetation som bevarar fukt",
"Vegetation retaining moisture",
parent_code="1800",
ai_keywords=["moss", "lichen", "moisture"]
))
# 1900 - Blockage
self._add(DefectCode(
"1900", "Blockering", "Blockage",
DefectCategory.BLOCKAGE, DefectSeverity.MODERATE,
"Blockering av funktion eller tillgång",
"Blockage of function or access",
ai_keywords=["blocked", "obstructed", "clogged"]
))
self._add(DefectCode(
"1910", "Fysisk blockering", "Physical Blockage",
DefectCategory.BLOCKAGE, DefectSeverity.MODERATE,
"Fysisk blockering",
"Physical blockage",
parent_code="1900",
ai_keywords=["physical block", "debris", "trash"]
))
self._add(DefectCode(
"1920", "Digital blockering", "Digital Blockage",
DefectCategory.FUNCTIONAL, DefectSeverity.MINOR,
"Digital eller elektronisk blockering",
"Digital or electronic blockage",
parent_code="1900",
ai_keywords=["digital", "electronic", "signal"]
))
self._add(DefectCode(
"1930", "Organisk blockering", "Organic Blockage",
DefectCategory.BLOCKAGE, DefectSeverity.MODERATE,
"Organisk blockering (löv, grenar)",
"Organic blockage (leaves, branches)",
parent_code="1900",
ai_keywords=["organic", "leaves", "branches"]
))
# 2000 - Surface Issues (Phase 1 focus)
self._add(DefectCode(
"2000", "Ytskada", "Surface Issue",
DefectCategory.SURFACE, DefectSeverity.MINOR,
"Ytskada eller nedsmutsning",
"Surface damage or dirt",
ai_keywords=["surface", "dirt", "stain"]
))
self._add(DefectCode(
"2100", "Nedsmutsning", "Dirt Accumulation",
DefectCategory.SURFACE, DefectSeverity.COSMETIC,
"Nedsmutsning på yta",
"Dirt accumulation on surface",
parent_code="2000",
ai_keywords=["dirty", "dirt", "grime", "dust", "mud"]
))
self._add(DefectCode(
"2200", "Färgförändring", "Color Change",
DefectCategory.SURFACE, DefectSeverity.COSMETIC,
"Färgförändring eller blekning",
"Color change or fading",
parent_code="2000",
ai_keywords=["faded", "discoloration", "color change", "bleached"]
))
self._add(DefectCode(
"2300", "Ytskada", "Surface Damage",
DefectCategory.SURFACE, DefectSeverity.MINOR,
"Skada på yta",
"Damage to surface",
parent_code="2000",
ai_keywords=["damage", "scratch", "chip", "peel"]
))
self._add(DefectCode(
"2400", "Klotter", "Graffiti",
DefectCategory.SURFACE, DefectSeverity.COSMETIC,
"Klotter eller målning",
"Graffiti or painting",
parent_code="2000",
ai_keywords=["graffiti", "paint", "tag", "vandalism"]
))
# 3000 - Structural Issues
self._add(DefectCode(
"3000", "Strukturell skada", "Structural Damage",
DefectCategory.STRUCTURAL, DefectSeverity.MAJOR,
"Strukturell skada",
"Structural damage",
ai_keywords=["structural", "damage", "crack", "break"]
))
self._add(DefectCode(
"3100", "Spricka", "Crack",
DefectCategory.STRUCTURAL, DefectSeverity.MODERATE,
"Spricka i material",
"Crack in material",
parent_code="3000",
ai_keywords=["crack", "fissure", "fracture"]
))
self._add(DefectCode(
"3200", "Deformation", "Deformation",
DefectCategory.STRUCTURAL, DefectSeverity.MODERATE,
"Permanent deformation",
"Permanent deformation",
parent_code="3000",
ai_keywords=["deformed", "bent", "warped"]
))
self._add(DefectCode(
"3300", "Materialförlust", "Material Loss",
DefectCategory.STRUCTURAL, DefectSeverity.MAJOR,
"Förlust av material",
"Loss of material",
parent_code="3000",
ai_keywords=["missing material", "erosion", "wear"]
))
# 4000 - Component Issues
self._add(DefectCode(
"4000", "Komponentskada", "Component Issue",
DefectCategory.COMPONENT, DefectSeverity.MODERATE,
"Problem med komponent",
"Component issue",
ai_keywords=["component", "part", "piece"]
))
self._add(DefectCode(
"4100", "Saknad del", "Missing Part",
DefectCategory.COMPONENT, DefectSeverity.MODERATE,
"Del saknas",
"Part missing",
parent_code="4000",
ai_keywords=["missing", "absent", "gone"]
))
self._add(DefectCode(
"4200", "Trasig del", "Broken Part",
DefectCategory.COMPONENT, DefectSeverity.MODERATE,
"Del är trasig",
"Part broken",
parent_code="4000",
ai_keywords=["broken", "damaged", "fractured"]
))
self._add(DefectCode(
"4300", "Lossnad del", "Loose Part",
DefectCategory.COMPONENT, DefectSeverity.MINOR,
"Del är lossnad",
"Part loose",
parent_code="4000",
ai_keywords=["loose", "detached", "hanging"]
))
# 5000 - Blockage Issues
self._add(DefectCode(
"5000", "Blockering", "Blockage",
DefectCategory.BLOCKAGE, DefectSeverity.MODERATE,
"Blockering",
"Blockage",
ai_keywords=["blocked", "obstructed"]
))
self._add(DefectCode(
"5100", "Fysisk blockering", "Physical Blockage",
DefectCategory.BLOCKAGE, DefectSeverity.MODERATE,
"Fysisk blockering",
"Physical blockage",
parent_code="5000",
ai_keywords=["physical", "block", "debris"]
))
self._add(DefectCode(
"5200", "Synlig blockering", "Visible Blockage",
DefectCategory.BLOCKAGE, DefectSeverity.MINOR,
"Synlig blockering",
"Visible blockage",
parent_code="5000",
ai_keywords=["visible", "block", "obscured"]
))
# 6000 - Environmental Issues
self._add(DefectCode(
"6000", "Miljöskada", "Environmental Damage",
DefectCategory.ENVIRONMENTAL, DefectSeverity.MINOR,
"Skada från miljöfaktorer",
"Damage from environmental factors",
ai_keywords=["environmental", "weather", "water"]
))
self._add(DefectCode(
"6100", "Vegetation", "Vegetation",
DefectCategory.ENVIRONMENTAL, DefectSeverity.MINOR,
"Oönskad vegetation",
"Unwanted vegetation",
parent_code="6000",
ai_keywords=["vegetation", "plant", "tree"]
))
self._add(DefectCode(
"6200", "Vattenskada", "Water Damage",
DefectCategory.ENVIRONMENTAL, DefectSeverity.MODERATE,
"Skada från vatten",
"Water damage",
parent_code="6000",
ai_keywords=["water", "moisture", "leak", "flood"]
))
self._add(DefectCode(
"6300", "Isskada", "Ice Damage",
DefectCategory.ENVIRONMENTAL, DefectSeverity.MODERATE,
"Skada från is eller frost",
"Damage from ice or frost",
parent_code="6000",
ai_keywords=["ice", "frost", "freeze"]
))
def _add(self, code: DefectCode):
"""Add defect code to registry"""
self._codes[code.code] = code
def get(self, code: str) -> Optional[DefectCode]:
"""Get defect code by code string"""
return self._codes.get(code)
def get_by_category(self, category: DefectCategory) -> List[DefectCode]:
"""Get all codes in a category"""
return [c for c in self._codes.values() if c.category == category]
def get_by_severity(self, severity: DefectSeverity) -> List[DefectCode]:
"""Get all codes with a severity level"""
return [c for c in self._codes.values() if c.severity == severity]
def search(self, query: str, lang: str = "sv") -> List[DefectCode]:
"""Search codes by name or description"""
query = query.lower()
results = []
for code in self._codes.values():
name = code.get_name(lang).lower()
desc = code.get_description(lang).lower()
if query in name or query in desc or query in code.code:
results.append(code)
return results
def get_children(self, parent_code: str) -> List[DefectCode]:
"""Get child codes for a parent code"""
return [c for c in self._codes.values() if c.parent_code == parent_code]
def get_top_level(self) -> List[DefectCode]:
"""Get all top-level codes (no parent)"""
return [c for c in self._codes.values() if c.parent_code is None]
def get_ai_suggestions(self, keywords: List[str]) -> List[Tuple[DefectCode, float]]:
"""
Get defect code suggestions based on AI keywords
Returns:
List of (DefectCode, confidence) tuples
"""
suggestions = []
for code in self._codes.values():
matches = sum(1 for kw in keywords if kw.lower() in [k.lower() for k in code.ai_keywords])
if matches > 0:
confidence = matches / len(keywords)
suggestions.append((code, confidence))
# Sort by confidence
suggestions.sort(key=lambda x: x[1], reverse=True)
return suggestions
def list_all(self) -> List[Dict]:
"""List all defect codes"""
return [code.to_dict() for code in self._codes.values()]
def list_categories(self) -> List[Dict]:
"""List all categories with counts"""
categories = {}
for code in self._codes.values():
cat = code.category.value
if cat not in categories:
categories[cat] = 0
categories[cat] += 1
return [
{"category": cat, "count": count}
for cat, count in sorted(categories.items())
]
# Singleton instance
defect_registry = DefectRegistry()
def get_defect_code(code: str) -> Optional[DefectCode]:
"""Convenience function"""
return defect_registry.get(code)
def search_defects(query: str, lang: str = "sv") -> List[DefectCode]:
"""Convenience function"""
return defect_registry.search(query, lang)
def suggest_defect_codes(keywords: List[str]) -> List[Tuple[DefectCode, float]]:
"""Convenience function"""
return defect_registry.get_ai_suggestions(keywords)
if __name__ == '__main__':
# Example usage
registry = DefectRegistry()
print("=== All Defect Codes ===")
for code in registry.list_all()[:5]:
print(f"{code['code']}: {code['name']} ({code['category']})")
print("\n=== Search for 'crack' ===")
results = registry.search("spricka")
for code in results[:5]:
print(f"{code.code}: {code.name_sv}")
print("\n=== AI Suggestions for ['rust', 'corrosion'] ===")
suggestions = registry.get_ai_suggestions(["rust", "corrosion"])
for code, confidence in suggestions[:5]:
print(f"{code.code}: {code.name_en} (confidence: {confidence:.2f})")
print("\n=== Categories ===")
for cat in registry.list_categories():
print(f"{cat['category']}: {cat['count']} codes")