From eaf31bf7546f5006236dc90b3817590a0af5f3bc Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Mon, 14 Jul 2025 12:09:09 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20SQLAlchemy=20=EB=AA=A8=EB=8D=B8=20?= =?UTF-8?q?=EB=B0=8F=20=EC=8A=A4=ED=82=A4=EB=A7=88=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=99=84=EB=A3=8C=20-=20database.py:=20Po?= =?UTF-8?q?stgreSQL=20=EC=97=B0=EA=B2=B0=20=EC=84=A4=EC=A0=95=20=EB=B0=8F?= =?UTF-8?q?=20=EC=84=B8=EC=85=98=20=EA=B4=80=EB=A6=AC=20-=20models.py:=20P?= =?UTF-8?q?roject,=20File,=20Material=20SQLAlchemy=20=EB=AA=A8=EB=8D=B8=20?= =?UTF-8?q?=EC=A0=95=EC=9D=98=20-=20schemas.py:=20Pydantic=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD/=EC=9D=91=EB=8B=B5=20=EC=8A=A4=ED=82=A4=EB=A7=88=20?= =?UTF-8?q?=EC=A0=95=EC=9D=98=20-=20=EC=99=84=EC=A0=84=ED=95=9C=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=EB=B2=A0=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=EC=97=B0=EB=8F=99=20=EA=B5=AC=EC=A1=B0=20=EC=99=84=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/database.py | 24 +++++++++++++ backend/app/models.py | 74 +++++++++++++++++++++++++++++++++++++++++ backend/app/schemas.py | 36 ++++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 backend/app/database.py create mode 100644 backend/app/models.py create mode 100644 backend/app/schemas.py diff --git a/backend/app/database.py b/backend/app/database.py new file mode 100644 index 0000000..b425a14 --- /dev/null +++ b/backend/app/database.py @@ -0,0 +1,24 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +import os + +# 데이터베이스 URL +DATABASE_URL = "postgresql://tkmp_user:tkmp_password_2025@localhost:5432/tk_mp_bom" + +# SQLAlchemy 엔진 생성 +engine = create_engine(DATABASE_URL) + +# 세션 팩토리 생성 +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +# Base 클래스 생성 +Base = declarative_base() + +# 데이터베이스 의존성 함수 +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() diff --git a/backend/app/models.py b/backend/app/models.py new file mode 100644 index 0000000..59c76ef --- /dev/null +++ b/backend/app/models.py @@ -0,0 +1,74 @@ +from sqlalchemy import Column, Integer, String, Boolean, DateTime, Text, Numeric, ForeignKey +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import relationship +from datetime import datetime + +Base = declarative_base() + +class Project(Base): + __tablename__ = "projects" + + id = Column(Integer, primary_key=True, index=True) + official_project_code = Column(String(50), unique=True, index=True) + project_name = Column(String(200), nullable=False) + client_name = Column(String(100)) + design_project_code = Column(String(50)) + design_project_name = Column(String(200)) + is_code_matched = Column(Boolean, default=False) + matched_by = Column(String(100)) + matched_at = Column(DateTime) + status = Column(String(20), default='active') + created_at = Column(DateTime, default=datetime.utcnow) + updated_at = Column(DateTime, default=datetime.utcnow) + description = Column(Text) + notes = Column(Text) + + # 관계 설정 + files = relationship("File", back_populates="project") + +class File(Base): + __tablename__ = "files" + + id = Column(Integer, primary_key=True, index=True) + project_id = Column(Integer, ForeignKey("projects.id")) + filename = Column(String(255), nullable=False) + original_filename = Column(String(255), nullable=False) + file_path = Column(String(500), nullable=False) + revision = Column(String(20), default='Rev.0') + upload_date = Column(DateTime, default=datetime.utcnow) + uploaded_by = Column(String(100)) + file_type = Column(String(10)) + file_size = Column(Integer) + is_active = Column(Boolean, default=True) + + # 관계 설정 + project = relationship("Project", back_populates="files") + materials = relationship("Material", back_populates="file") + +class Material(Base): + __tablename__ = "materials" + + id = Column(Integer, primary_key=True, index=True) + file_id = Column(Integer, ForeignKey("files.id")) + line_number = Column(Integer) + original_description = Column(Text, nullable=False) + classified_category = Column(String(50)) + classified_subcategory = Column(String(100)) + material_grade = Column(String(50)) + schedule = Column(String(20)) + size_spec = Column(String(50)) + quantity = Column(Numeric(10, 3), nullable=False) + unit = Column(String(10), nullable=False) + drawing_name = Column(String(100)) + area_code = Column(String(20)) + line_no = Column(String(50)) + classification_confidence = Column(Numeric(3, 2)) + is_verified = Column(Boolean, default=False) + verified_by = Column(String(100)) + verified_at = Column(DateTime) + drawing_reference = Column(String(100)) + notes = Column(Text) + created_at = Column(DateTime, default=datetime.utcnow) + + # 관계 설정 + file = relationship("File", back_populates="materials") diff --git a/backend/app/schemas.py b/backend/app/schemas.py new file mode 100644 index 0000000..21c0cbb --- /dev/null +++ b/backend/app/schemas.py @@ -0,0 +1,36 @@ +from pydantic import BaseModel +from datetime import datetime +from typing import Optional, List +from decimal import Decimal + +# 프로젝트 스키마 +class ProjectBase(BaseModel): + official_project_code: Optional[str] = None + project_name: str + client_name: Optional[str] = None + design_project_code: Optional[str] = None + design_project_name: Optional[str] = None + description: Optional[str] = None + notes: Optional[str] = None + +class ProjectCreate(ProjectBase): + pass + +class ProjectUpdate(ProjectBase): + project_name: Optional[str] = None + +class Project(ProjectBase): + id: int + is_code_matched: bool + status: str + created_at: datetime + updated_at: datetime + + class Config: + from_attributes = True + +# 응답 스키마 +class ProjectResponse(BaseModel): + projects: List[Project] + total: int + message: str