handOver2/database/models.py

635 lines
18 KiB
Python

# -*- 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
alarm_time: 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)