# -*- coding: utf-8 -*- """ 데이터 모델 정의 모듈 데이터베이스 테이블에 대응하는 데이터 모델 클래스들을 정의합니다. 각 모델 클래스는 테이블 스키마를 반영하며, 데이터 유효성 검사 및 직렬화 기능을 제공합니다. """ import json from dataclasses import dataclass, field, asdict from datetime import datetime, date, time from typing import Optional, Dict, Any, List from enum import Enum # ============================================================================ # 열거형 정의 # ============================================================================ class Role(Enum): """사용자 역할""" ADMIN = "admin" EDITOR = "editor" VIEWER = "viewer" class ShiftType(Enum): """근무 유형""" DAY = "주간" NIGHT = "야간" class CleaningType(Enum): """청소 유형""" NONE = "없음" MEDIUM = "중청소" LARGE = "대청소" # ============================================================================ # 기본 모델 클래스 # ============================================================================ @dataclass class BaseModel: """ 기본 모델 클래스 모든 데이터 모델의 기반 클래스입니다. 공통 필드 및 유틸리티 메서드를 제공합니다. """ id: Optional[int] = None created_at: Optional[datetime] = None updated_at: Optional[datetime] = None def to_dict(self) -> Dict[str, Any]: """ 모델을 딕셔너리로 변환합니다. Returns: 모델 데이터 딕셔너리 """ data = asdict(self) # datetime 객체를 문자열로 변환 for key, value in data.items(): if isinstance(value, datetime): data[key] = value.isoformat() elif isinstance(value, date): data[key] = value.isoformat() elif isinstance(value, time): data[key] = value.isoformat() elif isinstance(value, Enum): data[key] = value.value return data @classmethod def from_dict(cls, data: Dict[str, Any]) -> 'BaseModel': """ 딕셔너리에서 모델을 생성합니다. Args: data: 모델 데이터 딕셔너리 Returns: 모델 인스턴스 """ # datetime 문자열을 객체로 변환 for key in ['created_at', 'updated_at']: if key in data and isinstance(data[key], str): try: data[key] = datetime.fromisoformat(data[key]) except ValueError: data[key] = None return cls(**data) # ============================================================================ # 사용자 모델 # ============================================================================ @dataclass class User(BaseModel): """ 사용자 모델 Attributes: username: 사용자 ID (고유) password_hash: 비밀번호 해시 name: 이름 department: 부서 role: 역할 (admin, editor, viewer) is_active: 활성화 여부 """ username: str = "" password_hash: str = "" name: str = "" department: str = "" role: str = "viewer" is_active: bool = True def has_permission(self, action: str) -> bool: """ 특정 동작에 대한 권한이 있는지 확인합니다. Args: action: 동작 (create, read, update, delete) Returns: 권한 여부 """ if self.role == Role.ADMIN.value: return True elif self.role == Role.EDITOR.value: return action in ['create', 'read', 'update'] else: # viewer return action == 'read' def is_admin(self) -> bool: """관리자 여부 확인""" return self.role == Role.ADMIN.value or self.department == "검수팀" # ============================================================================ # 팀 모델 # ============================================================================ @dataclass class Team(BaseModel): """ 팀 모델 Attributes: name: 팀 이름 (A팀, B팀, C팀, D팀) shift_type: 근무 유형 (주간, 야간) is_active: 활성화 여부 """ name: str = "" shift_type: str = "" is_active: bool = True # ============================================================================ # 섹션 공통 모델 # ============================================================================ @dataclass class SectionBase(BaseModel): """ 섹션 기본 모델 모든 섹션(지시, 고장, 작업, 기타)의 공통 필드를 정의합니다. """ created_date: Optional[date] = None created_team: str = "" team_confirmations: str = "{}" # JSON 문자열 is_completed: bool = False completed_at: Optional[datetime] = None created_by: Optional[int] = None def get_team_confirmations(self) -> Dict[str, bool]: """팀 확인 상태를 딕셔너리로 반환""" try: return json.loads(self.team_confirmations) except json.JSONDecodeError: return {"1팀": False, "2팀": False, "3팀": False, "4팀": False} def set_team_confirmation(self, team: str, confirmed: bool): """특정 팀의 확인 상태를 설정""" confirmations = self.get_team_confirmations() confirmations[team] = confirmed self.team_confirmations = json.dumps(confirmations, ensure_ascii=False) # 모든 팀이 확인했는지 체크 if all(confirmations.values()): self.is_completed = True self.completed_at = datetime.now() def all_teams_confirmed(self) -> bool: """모든 팀이 확인했는지 반환""" confirmations = self.get_team_confirmations() return all(confirmations.values()) # ============================================================================ # 지시 섹션 모델 # ============================================================================ @dataclass class Instruction(SectionBase): """ 지시 섹션 모델 상위부서나 상급자의 지시사항을 기록합니다. Attributes: instructor: 지시자 instruction_content: 지시내용 instruction_date: 지시일자 is_continuous: 지속여부 """ instructor: str = "" instruction_content: str = "" instruction_date: Optional[date] = None is_continuous: bool = False # ============================================================================ # 고장 섹션 모델 # ============================================================================ @dataclass class Fault(SectionBase): """ 고장 섹션 모델 전동차 고장 정보를 기록합니다. Attributes: occurrence_date: 발생일자 column_number: 열번 train_number: 편성번호 car_number: 호차 fault_code: 고장코드 device_category: 장치분류 occurrence_station: 발생역 occurrence_time: 발생시간 fault_content: 고장내용 action_content: 조치내용 action_team: 조치팀 fault_source: 고장출처 severity: 심각도 (normal, high, critical) """ occurrence_date: Optional[date] = None column_number: str = "" train_number: str = "" car_number: str = "" fault_code: str = "" device_category: str = "" occurrence_station: str = "" occurrence_time: Optional[time] = None fault_content: str = "" action_content: str = "" action_team: str = "" fault_source: str = "" # 고장출처 severity: str = "normal" # 심각도 (normal, high, critical) # ============================================================================ # 작업 섹션 모델 # ============================================================================ @dataclass class Work(SectionBase): """ 작업 섹션 모델 전동차 관련 작업일정을 기록합니다. Attributes: work_date: 작업일정 work_entity: 작업주체 target_train: 대상편성 target_device: 대상기기 work_content: 작업내용 remarks: 특이사항 """ work_date: Optional[date] = None work_entity: str = "" target_train: str = "" target_device: str = "" work_content: str = "" remarks: str = "" # ============================================================================ # 기타 섹션 모델 # ============================================================================ @dataclass class Misc(SectionBase): """ 기타 섹션 모델 전동차 관련 작업 외 나머지 사항을 기록합니다. Attributes: reporter: 전달자 report_content: 전달내용 remarks: 특이사항 related_document: 관련문서 """ reporter: str = "" report_content: str = "" remarks: str = "" related_document: str = "" # ============================================================================ # 일상검수 모델 # ============================================================================ @dataclass class DailyInspection(BaseModel): """ 일상검수 모델 일일 점검 대상 편성을 기록합니다. Attributes: inspection_date: 검수일자 shift_type: 근무유형 (주간, 야간) slot_number: 슬롯번호 (1~5) train_number: 편성번호 cleaning_type: 청소유형 (없음, 중청소, 대청소) has_work: 작업여부 """ inspection_date: Optional[date] = None shift_type: str = "" slot_number: int = 0 train_number: str = "" cleaning_type: str = "없음" has_work: bool = False created_by: Optional[int] = None # ============================================================================ # Todo 모델 # ============================================================================ class TodoCategory: """할일 카테고리""" GENERAL = "일반" # 일반 할일 ARRIVAL_INSPECTION = "도착검수" # 도착검수 TASK = "작업" # 작업 @dataclass class Todo(BaseModel): """ 할일 모델 할일 목록을 기록합니다. Attributes: todo_date: 할일 날짜 category: 카테고리 (일반, 도착검수, 작업) target_train: 대상편성 schedule: 일정 content: 내용 is_completed: 완료여부 completed_at: 완료시간 """ todo_date: Optional[date] = None category: str = "일반" target_train: str = "" schedule: str = "" content: str = "" is_completed: bool = False completed_at: Optional[datetime] = None created_by: Optional[int] = None # ============================================================================ # 메모 모델 # ============================================================================ @dataclass class Memo(BaseModel): """ 메모 모델 메모를 기록합니다. Attributes: memo_date: 메모 날짜 content: 내용 """ memo_date: Optional[date] = None content: str = "" created_by: Optional[int] = None # ============================================================================ # 설정 모델 # ============================================================================ @dataclass class Setting(BaseModel): """ 설정 모델 키-값 형태의 설정을 저장합니다. Attributes: key: 설정 키 value: 설정 값 """ key: str = "" value: str = "" # ============================================================================ # 팀 인원 모델 # ============================================================================ @dataclass class TeamMember(BaseModel): """ 팀 인원 모델 각 팀의 구성원 정보를 저장합니다. Attributes: team: 팀 (1팀, 2팀, 3팀, 4팀) position: 직책 (부팀장, 운용) name: 이름 order: 순서 (당무 순서) partner_id: 짝궁 ID (함께 당무 서는 사람) is_active: 활성화 여부 """ team: str = "" position: str = "" name: str = "" order: int = 0 partner_id: Optional[int] = None is_active: bool = True @dataclass class DutySchedule(BaseModel): """ 당무 일정 모델 일별 당무자 정보를 저장합니다. Attributes: duty_date: 당무 날짜 team: 팀 shift_type: 근무 유형 (주간, 야간) vice_leader_id: 당무 부팀장 ID operator_id: 당무 운용 ID vice_leader_name: 당무 부팀장 이름 (조회용) operator_name: 당무 운용 이름 (조회용) """ duty_date: Optional[date] = None team: str = "" shift_type: str = "" vice_leader_id: Optional[int] = None operator_id: Optional[int] = None vice_leader_name: str = "" operator_name: str = "" # ============================================================================ # 날씨 모델 # ============================================================================ @dataclass class Weather(BaseModel): """ 날씨 모델 시간별 날씨 정보를 저장합니다. Attributes: datetime: 날씨 데이터 시각 location_name: 지역명 location_code: 지역코드 temp: 기온 feels_like: 체감온도 humidity: 습도 wind_speed: 풍속 wind_direction: 풍향 precipitation_prob: 강수확률 weather_condition: 날씨 상태 weather_icon: 날씨 아이콘 """ datetime: Optional[datetime] = None location_name: str = "" location_code: str = "" temp: Optional[int] = None feels_like: Optional[int] = None humidity: Optional[int] = None wind_speed: str = "" wind_direction: str = "" precipitation_prob: Optional[int] = None weather_condition: str = "" weather_icon: str = "" # ============================================================================ # 열차 다이아 시각표 모델 # ============================================================================ @dataclass class TrainSchedule(BaseModel): """ 열차 다이아 시각표 모델 열번과 역별 도착/출발 시각을 저장합니다. 열번과 역명으로 발생 시간을 유추할 때 사용됩니다. Attributes: column_number: 열번 (예: "1001", "1002") station: 역명 (예: "신평역", "하단역") arrival_time: 도착 시간 departure_time: 출발 시간 direction: 방향 (up: 상행, down: 하행) is_weekday: 평일 여부 (True: 평일, False: 주말/휴일) is_active: 활성화 여부 """ column_number: str = "" station: str = "" arrival_time: Optional[time] = None departure_time: Optional[time] = None direction: str = "up" # up: 상행, down: 하행 is_weekday: bool = True is_active: bool = True # ============================================================================ # 전동차 편성 모델 # ============================================================================ @dataclass class TrainFormation(BaseModel): """ 전동차 편성 모델 편성번호별 전동차 정보를 관리합니다. Attributes: train_number: 편성번호 (예: 134a, 134b, 1A) is_new_train: 신차 여부 (True: 신차, False: 구차) manufacturer: 제조사 introduction_date: 도입일 depot: 배속지 (신평, 노포) alias: 별칭 introduction_stage: 도입단계 introduction_count: 도입량 """ train_number: str = "" is_new_train: bool = True manufacturer: str = "" introduction_date: Optional[date] = None depot: str = "" alias: str = "" introduction_stage: str = "" introduction_count: int = 0 # ============================================================================ # 조치 단계 모델 # ============================================================================ @dataclass class ActionStep(BaseModel): """ 조치 단계 모델 고장에 대한 조치를 단계별로 기록합니다. Attributes: fault_id: 고장 ID (외래키) step_number: 단계 번호 action_content: 조치 내용 action_team: 조치팀 """ fault_id: int = 0 step_number: int = 0 action_content: str = "" action_team: str = "" # ============================================================================ # 모델 레지스트리 # ============================================================================ # 테이블 이름과 모델 클래스 매핑 MODEL_REGISTRY: Dict[str, type] = { "users": User, "teams": Team, "instructions": Instruction, "faults": Fault, "works": Work, "miscs": Misc, "daily_inspections": DailyInspection, "todos": Todo, "memos": Memo, "settings": Setting, "team_members": TeamMember, "duty_schedules": DutySchedule, "train_schedules": TrainSchedule, "weather": Weather, "train_formations": TrainFormation, "action_steps": ActionStep, } def get_model_class(table_name: str) -> Optional[type]: """ 테이블 이름에 해당하는 모델 클래스를 반환합니다. Args: table_name: 테이블 이름 Returns: 모델 클래스 """ return MODEL_REGISTRY.get(table_name)