|
|
@@ -0,0 +1,268 @@
|
|
|
+"""
|
|
|
+Label translator for hazard detection system.
|
|
|
+Translates pinyin/English labels to Chinese for display and database queries.
|
|
|
+"""
|
|
|
+
|
|
|
+from typing import List, Dict, Optional
|
|
|
+
|
|
|
+
|
|
|
+# Complete mapping: pinyin/English -> Chinese
|
|
|
+LABEL_MAPPING: Dict[str, str] = {
|
|
|
+ # Gas Station (加油站) labels
|
|
|
+ "fangzhuanglan": "防撞栏",
|
|
|
+ "jiayouqiang": "加油枪",
|
|
|
+ "jiayouji": "加油机",
|
|
|
+ "miehuoqi": "灭火器",
|
|
|
+ "zhuixingtong": "锥形桶",
|
|
|
+ "push_extinguisher": "手推灭火器",
|
|
|
+ "support_pillar": "支撑柱",
|
|
|
+ "fuel_hose": "加油机输油软管",
|
|
|
+ "vent_pipe": "通气管",
|
|
|
+ "oil_discharge_port": "卸油口",
|
|
|
+ "static_release_post": "静电释放桩",
|
|
|
+ "static_wire": "静电线",
|
|
|
+ "static_clip": "静电夹",
|
|
|
+ "power_distribution_box": "配电箱",
|
|
|
+
|
|
|
+ # Alternative English names (aliases)
|
|
|
+ "crash_barrier": "防撞栏",
|
|
|
+ "fuel_nozzle": "加油枪",
|
|
|
+ "fuel_dispenser": "加油机",
|
|
|
+ "fire_extinguisher": "灭火器",
|
|
|
+ "traffic_cone": "锥形桶",
|
|
|
+ "pillar": "支撑柱",
|
|
|
+ "oil_hose": "加油机输油软管",
|
|
|
+ "ventilation_pipe": "通气管",
|
|
|
+ "discharge_port": "卸油口",
|
|
|
+ "grounding_post": "静电释放桩",
|
|
|
+ "grounding_wire": "静电线",
|
|
|
+ "grounding_clip": "静电夹",
|
|
|
+ "distribution_box": "配电箱",
|
|
|
+}
|
|
|
+
|
|
|
+# Reverse mapping: Chinese -> pinyin/English (for reference)
|
|
|
+REVERSE_MAPPING: Dict[str, str] = {v: k for k, v in LABEL_MAPPING.items()}
|
|
|
+
|
|
|
+
|
|
|
+def translate_label(label: str, fallback_to_original: bool = True) -> str:
|
|
|
+ """
|
|
|
+ Translate a single label from pinyin/English to Chinese.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ label: The label to translate (pinyin or English)
|
|
|
+ fallback_to_original: If True, return original label when no translation found
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Translated Chinese label, or original label if not found and fallback is True
|
|
|
+
|
|
|
+ Examples:
|
|
|
+ >>> translate_label("fangzhuanglan")
|
|
|
+ "防撞栏"
|
|
|
+ >>> translate_label("fuel_hose")
|
|
|
+ "加油机输油软管"
|
|
|
+ >>> translate_label("防撞栏") # Already Chinese
|
|
|
+ "防撞栏"
|
|
|
+ >>> translate_label("unknown_label")
|
|
|
+ "unknown_label"
|
|
|
+ """
|
|
|
+ if not label:
|
|
|
+ return label
|
|
|
+
|
|
|
+ label_stripped = label.strip()
|
|
|
+ if not label_stripped:
|
|
|
+ return label
|
|
|
+
|
|
|
+ # Check if already Chinese (contains Chinese characters)
|
|
|
+ if _contains_chinese(label_stripped):
|
|
|
+ return label_stripped
|
|
|
+
|
|
|
+ # Try exact match (case-insensitive)
|
|
|
+ label_lower = label_stripped.lower()
|
|
|
+ if label_lower in LABEL_MAPPING:
|
|
|
+ return LABEL_MAPPING[label_lower]
|
|
|
+
|
|
|
+ # Try with underscores replaced by spaces
|
|
|
+ label_spaced = label_lower.replace("_", " ")
|
|
|
+ if label_spaced in LABEL_MAPPING:
|
|
|
+ return LABEL_MAPPING[label_spaced]
|
|
|
+
|
|
|
+ # Fallback to original if no translation found
|
|
|
+ if fallback_to_original:
|
|
|
+ return label_stripped
|
|
|
+
|
|
|
+ return ""
|
|
|
+
|
|
|
+
|
|
|
+def translate_labels(labels: List[str], fallback_to_original: bool = True) -> List[str]:
|
|
|
+ """
|
|
|
+ Translate a list of labels from pinyin/English to Chinese.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ labels: List of labels to translate
|
|
|
+ fallback_to_original: If True, keep original labels when no translation found
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ List of translated Chinese labels
|
|
|
+
|
|
|
+ Examples:
|
|
|
+ >>> translate_labels(["fangzhuanglan", "jiayouqiang", "防撞栏"])
|
|
|
+ ["防撞栏", "加油枪", "防撞栏"]
|
|
|
+ """
|
|
|
+ if not labels:
|
|
|
+ return []
|
|
|
+
|
|
|
+ return [translate_label(label, fallback_to_original) for label in labels]
|
|
|
+
|
|
|
+
|
|
|
+def is_translated(label: str) -> bool:
|
|
|
+ """
|
|
|
+ Check if a label is already in Chinese (translated).
|
|
|
+
|
|
|
+ Args:
|
|
|
+ label: The label to check
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ True if label contains Chinese characters, False otherwise
|
|
|
+ """
|
|
|
+ return _contains_chinese(label)
|
|
|
+
|
|
|
+
|
|
|
+def get_english_name(chinese_label: str) -> Optional[str]:
|
|
|
+ """
|
|
|
+ Get the English/pinyin name for a Chinese label (reverse lookup).
|
|
|
+
|
|
|
+ Args:
|
|
|
+ chinese_label: Chinese label to look up
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ English/pinyin name if found, None otherwise
|
|
|
+
|
|
|
+ Examples:
|
|
|
+ >>> get_english_name("防撞栏")
|
|
|
+ "fangzhuanglan"
|
|
|
+ """
|
|
|
+ return REVERSE_MAPPING.get(chinese_label.strip())
|
|
|
+
|
|
|
+
|
|
|
+def add_custom_mapping(pinyin_or_english: str, chinese: str) -> None:
|
|
|
+ """
|
|
|
+ Add a custom label mapping at runtime.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ pinyin_or_english: The pinyin or English label
|
|
|
+ chinese: The Chinese translation
|
|
|
+
|
|
|
+ Note:
|
|
|
+ This is useful for dynamic label additions without modifying the source code.
|
|
|
+ """
|
|
|
+ key = pinyin_or_english.strip().lower()
|
|
|
+ value = chinese.strip()
|
|
|
+ LABEL_MAPPING[key] = value
|
|
|
+ REVERSE_MAPPING[value] = key
|
|
|
+
|
|
|
+
|
|
|
+def get_all_mappings() -> Dict[str, str]:
|
|
|
+ """
|
|
|
+ Get all label mappings.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Dictionary of all pinyin/English -> Chinese mappings
|
|
|
+ """
|
|
|
+ return LABEL_MAPPING.copy()
|
|
|
+
|
|
|
+
|
|
|
+def _contains_chinese(text: str) -> bool:
|
|
|
+ """
|
|
|
+ Check if text contains Chinese characters.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ text: Text to check
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ True if text contains at least one Chinese character
|
|
|
+ """
|
|
|
+ if not text:
|
|
|
+ return False
|
|
|
+
|
|
|
+ for char in text:
|
|
|
+ if '\u4e00' <= char <= '\u9fff':
|
|
|
+ return True
|
|
|
+
|
|
|
+ return False
|
|
|
+
|
|
|
+
|
|
|
+# Sub-secondary to secondary scene mapping
|
|
|
+# Used only for database queries - these scenes still appear as-is in UI and storage
|
|
|
+SUB_SECONDARY_TO_SECONDARY_MAPPING: Dict[str, str] = {
|
|
|
+ # 加油设施_附属场地 -> maps to parent secondary scene (note: uses underscore in DB)
|
|
|
+ "加油机": "加油设施_附属场地",
|
|
|
+ "加油枪": "加油设施_附属场地",
|
|
|
+ "加油机输油软管": "加油设施_附属场地",
|
|
|
+ "防撞栏": "加油设施_附属场地",
|
|
|
+ "锥形桶": "加油设施_附属场地",
|
|
|
+ "灭火器": "加油设施_附属场地",
|
|
|
+
|
|
|
+ # 罩棚 and 立柱 are separate scenes in DB, map support pillar to 立柱
|
|
|
+ "支撑立柱": "立柱",
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+def translate_scene_for_query(scene_name: str) -> str:
|
|
|
+ """
|
|
|
+ Translate sub-secondary scene to its parent secondary scene for database queries.
|
|
|
+ This is ONLY used when querying the database, not for display or storage.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ scene_name: The scene name to translate (could be sub-secondary or secondary)
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Parent secondary scene name if it's a sub-secondary scene, otherwise original name
|
|
|
+
|
|
|
+ Examples:
|
|
|
+ >>> translate_scene_for_query("加油机")
|
|
|
+ "加油设施及附属场地"
|
|
|
+ >>> translate_scene_for_query("支撑立柱")
|
|
|
+ "罩棚及立柱"
|
|
|
+ >>> translate_scene_for_query("加油设施及附属场地")
|
|
|
+ "加油设施及附属场地"
|
|
|
+ """
|
|
|
+ if not scene_name:
|
|
|
+ return scene_name
|
|
|
+
|
|
|
+ scene_stripped = scene_name.strip()
|
|
|
+ return SUB_SECONDARY_TO_SECONDARY_MAPPING.get(scene_stripped, scene_stripped)
|
|
|
+
|
|
|
+
|
|
|
+def translate_scenes_for_query(scene_names: List[str]) -> List[str]:
|
|
|
+ """
|
|
|
+ Translate a list of sub-secondary scenes to their parent secondary scenes for database queries.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ scene_names: List of scene names to translate
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ List of translated scene names (parent secondary scenes)
|
|
|
+
|
|
|
+ Examples:
|
|
|
+ >>> translate_scenes_for_query(["加油机", "加油枪", "支撑立柱"])
|
|
|
+ ["加油设施及附属场地", "加油设施及附属场地", "罩棚及立柱"]
|
|
|
+ """
|
|
|
+ if not scene_names:
|
|
|
+ return []
|
|
|
+
|
|
|
+ return [translate_scene_for_query(scene) for scene in scene_names]
|
|
|
+
|
|
|
+
|
|
|
+# Export main functions
|
|
|
+__all__ = [
|
|
|
+ "translate_label",
|
|
|
+ "translate_labels",
|
|
|
+ "is_translated",
|
|
|
+ "get_english_name",
|
|
|
+ "add_custom_mapping",
|
|
|
+ "get_all_mappings",
|
|
|
+ "translate_scene_for_query",
|
|
|
+ "translate_scenes_for_query",
|
|
|
+ "LABEL_MAPPING",
|
|
|
+ "SUB_SECONDARY_TO_SECONDARY_MAPPING",
|
|
|
+]
|