Spaces:
Sleeping
Sleeping
| """모두의 빛길 — 노드/엣지 데이터 스키마 정의. | |
| 이 모듈은 추천 그래프를 구성하는 모든 노드 타입을 dataclass로 정의한다. | |
| PyG HeteroData에 그대로 매핑할 수 있도록 단순한 평면 구조로 유지한다. | |
| 노드 타입 | |
| --------- | |
| - VenueNode : 문화시설 (박물관/미술관/공연장/도서관 등) | |
| - EventNode : 공연·전시 이벤트 | |
| - TransitNode : 지하철역·버스정류장 | |
| - AmenityNode : 화장실/쉼터/벤치/CCTV | |
| - HazardNode : 사고다발지역/공사구간/저조도 | |
| 엣지 타입 | |
| --------- | |
| - (VENUE)-hosts->(EVENT) | |
| - (VENUE)<->(TRANSIT) near | |
| - (VENUE)<->(AMENITY) has_amenity | |
| - (TRANSIT)<->(TRANSIT) walkable / connects | |
| - (TRANSIT)->(HAZARD) passes_hazard | |
| """ | |
| from __future__ import annotations | |
| from dataclasses import dataclass, field | |
| from typing import List, Optional | |
| # ============================================================ | |
| # 노드 dataclass | |
| # ============================================================ | |
| class VenueNode: | |
| """문화시설 노드.""" | |
| id: int | |
| name: str | |
| lat: float | |
| lng: float | |
| venue_type: str # 박물관/미술관/공연장/도서관/카페/문화센터 | |
| accessibility_score: float = 0.0 # 0~1, 무장애 종합점수 | |
| has_elevator: bool = False | |
| has_disabled_toilet: bool = False | |
| has_ramp: bool = False | |
| free: bool = False | |
| indoor: bool = True | |
| age_friendly: bool = False # 어르신 무료/할인 등 | |
| booking_url: str = "" | |
| phone: str = "" | |
| def feature_vec(self) -> List[float]: | |
| """GNN 입력 feature 벡터.""" | |
| return [ | |
| self.accessibility_score, | |
| float(self.has_elevator), | |
| float(self.has_disabled_toilet), | |
| float(self.has_ramp), | |
| float(self.free), | |
| float(self.indoor), | |
| float(self.age_friendly), | |
| # venue_type one-hot (8종) | |
| *self._type_onehot(), | |
| ] | |
| def _type_onehot(self) -> List[float]: | |
| types = ["박물관", "미술관", "공연장", "도서관", "카페", "문화센터", "체험관", "기타"] | |
| v = [0.0] * len(types) | |
| if self.venue_type in types: | |
| v[types.index(self.venue_type)] = 1.0 | |
| else: | |
| v[-1] = 1.0 | |
| return v | |
| class EventNode: | |
| """공연·전시 이벤트 노드.""" | |
| id: int | |
| venue_id: int | |
| title: str | |
| start_time: str # "HH:MM" | |
| end_time: str | |
| age_limit: Optional[int] = None | |
| price: int = 0 | |
| booking_url: str = "" | |
| event_type: str = "공연" # 공연/전시/체험/강좌 | |
| has_subtitle: bool = False # 자막 | |
| has_sign_lang: bool = False # 수어 통역 | |
| has_audio_guide: bool = False # 음성해설/점자 | |
| def feature_vec(self) -> List[float]: | |
| sh, sm = map(int, self.start_time.split(":")) | |
| eh, em = map(int, self.end_time.split(":")) | |
| return [ | |
| sh + sm / 60.0, | |
| eh + em / 60.0, | |
| float(self.age_limit or 0), | |
| float(self.price), | |
| float(self.has_subtitle), | |
| float(self.has_sign_lang), | |
| float(self.has_audio_guide), | |
| float(self.event_type == "공연"), | |
| float(self.event_type == "전시"), | |
| float(self.event_type == "체험"), | |
| float(self.event_type == "강좌"), | |
| ] | |
| class TransitNode: | |
| """지하철역·버스정류장 노드.""" | |
| id: int | |
| name: str | |
| lat: float | |
| lng: float | |
| transit_type: str = "지하철" # 지하철/버스/저상버스 | |
| has_elevator: bool = False | |
| has_toilet: bool = False | |
| line: str = "" | |
| def feature_vec(self) -> List[float]: | |
| return [ | |
| float(self.has_elevator), | |
| float(self.has_toilet), | |
| float(self.transit_type == "지하철"), | |
| float(self.transit_type == "저상버스"), | |
| float(self.transit_type == "버스"), | |
| ] | |
| class AmenityNode: | |
| """편의시설 노드 — 화장실/쉼터/벤치/CCTV.""" | |
| id: int | |
| name: str | |
| lat: float | |
| lng: float | |
| amenity_type: str # 공공화장실/장애인화장실/무더위쉼터/벤치/CCTV/가로등 | |
| accessible: bool = False | |
| open_24h: bool = False | |
| def feature_vec(self) -> List[float]: | |
| return [ | |
| float(self.accessible), | |
| float(self.open_24h), | |
| float(self.amenity_type == "공공화장실"), | |
| float(self.amenity_type == "장애인화장실"), | |
| float(self.amenity_type == "무더위쉼터"), | |
| float(self.amenity_type == "벤치"), | |
| float(self.amenity_type == "CCTV"), | |
| float(self.amenity_type == "가로등"), | |
| ] | |
| class HazardNode: | |
| """위험구간 노드 — 사고다발지역/공사구간/저조도.""" | |
| id: int | |
| lat: float | |
| lng: float | |
| hazard_type: str # 보행자사고다발/노인사고다발/공사/저조도/계단/턱 | |
| severity: float = 0.5 # 0~1 | |
| night_only: bool = False # 야간에만 위험 | |
| def feature_vec(self) -> List[float]: | |
| return [ | |
| self.severity, | |
| float(self.night_only), | |
| float(self.hazard_type == "보행자사고다발"), | |
| float(self.hazard_type == "노인사고다발"), | |
| float(self.hazard_type == "공사"), | |
| float(self.hazard_type == "저조도"), | |
| float(self.hazard_type == "계단"), | |
| float(self.hazard_type == "턱"), | |
| ] | |
| # ============================================================ | |
| # 컬렉션 컨테이너 | |
| # ============================================================ | |
| class GraphData: | |
| """GraphBuilder에 입력될 컬렉션.""" | |
| venues: List[VenueNode] = field(default_factory=list) | |
| events: List[EventNode] = field(default_factory=list) | |
| transits: List[TransitNode] = field(default_factory=list) | |
| amenities: List[AmenityNode] = field(default_factory=list) | |
| hazards: List[HazardNode] = field(default_factory=list) | |
| def stats(self) -> dict: | |
| return { | |
| "VENUE": len(self.venues), | |
| "EVENT": len(self.events), | |
| "TRANSIT": len(self.transits), | |
| "AMENITY": len(self.amenities), | |
| "HAZARD": len(self.hazards), | |
| } | |
| # ============================================================ | |
| # 슬롯 정의 (slot_extractor와 공유) | |
| # ============================================================ | |
| SLOT_NAMES = [ | |
| "USER_TYPE", # 고령자/휠체어사용자/임산부/시각장애/청각장애/어린이동반/보행보조기 | |
| "COMPANION", # 가족/보호자/활동지원사/단체 | |
| "ORIGIN", # 병원/복지관/지하철역/자택 | |
| "WALK_LIMIT", # 짧음/거리지정/환승최소 | |
| "AVOID", # 위험회피/계단회피/경사회피/혼잡회피/사고다발회피/야외회피 | |
| "CULTURE_PREF", # 전시/공연/박물관/미술관/도서관/카페/체험/문화일반/미디어아트/전통문화 | |
| "WEATHER", # 폭염/한파/강우/미세먼지 | |
| "TIME_WINDOW", # 오전/낮/오후/저녁(야간)/시간한정 | |
| "BUDGET", # 무료/할인/저예산 | |
| "SPECIAL_NEEDS", # 화장실/수유실/안내견/점자/수어자막/엘리베이터/장애인화장실 | |
| ] | |
| __all__ = [ | |
| "VenueNode", "EventNode", "TransitNode", "AmenityNode", "HazardNode", | |
| "GraphData", "SLOT_NAMES", | |
| ] | |