- 사용자 추가 -
- - -- 사용자 목록 -
- -로딩 중...
-diff --git a/system3-nonconformance/api/database/models.py b/system3-nonconformance/api/database/models.py index 6ab7da3..d3c9024 100644 --- a/system3-nonconformance/api/database/models.py +++ b/system3-nonconformance/api/database/models.py @@ -68,7 +68,6 @@ class User(Base): # Relationships issues = relationship("Issue", back_populates="reporter", foreign_keys="Issue.reporter_id") reviewed_issues = relationship("Issue", foreign_keys="Issue.reviewed_by_id") - daily_works = relationship("DailyWork", back_populates="created_by") page_permissions = relationship("UserPagePermission", back_populates="user", foreign_keys="UserPagePermission.user_id") class UserPagePermission(Base): @@ -184,37 +183,6 @@ class Project(Base): primaryjoin="Project.id == Issue.project_id", foreign_keys="[Issue.project_id]") -class DailyWork(Base): - __tablename__ = "qc_daily_works" - - id = Column(Integer, primary_key=True, index=True) - date = Column(DateTime, nullable=False, index=True) - worker_count = Column(Integer, nullable=False) - regular_hours = Column(Float, nullable=False) - overtime_workers = Column(Integer, default=0) - overtime_hours = Column(Float, default=0) - overtime_total = Column(Float, default=0) - total_hours = Column(Float, nullable=False) - created_by_id = Column(Integer, ForeignKey("sso_users.user_id")) - created_at = Column(DateTime, default=get_kst_now) - - # Relationships - created_by = relationship("User", back_populates="daily_works") - -class ProjectDailyWork(Base): - __tablename__ = "qc_project_daily_works" - - id = Column(Integer, primary_key=True, index=True) - date = Column(DateTime, nullable=False, index=True) - project_id = Column(Integer, ForeignKey("projects.project_id"), nullable=False) - hours = Column(Float, nullable=False) - created_by_id = Column(Integer, ForeignKey("sso_users.user_id")) - created_at = Column(DateTime, default=get_kst_now) - - # Relationships - project = relationship("Project") - created_by = relationship("User") - class DeletionLog(Base): __tablename__ = "qc_deletion_logs" diff --git a/system3-nonconformance/api/database/schemas.py b/system3-nonconformance/api/database/schemas.py index bce2760..556d776 100644 --- a/system3-nonconformance/api/database/schemas.py +++ b/system3-nonconformance/api/database/schemas.py @@ -49,16 +49,6 @@ class UserBase(BaseModel): role: UserRole = UserRole.user department: Optional[DepartmentType] = None -class UserCreate(UserBase): - password: str - -class UserUpdate(BaseModel): - full_name: Optional[str] = None - password: Optional[str] = None - role: Optional[UserRole] = None - department: Optional[DepartmentType] = None - is_active: Optional[bool] = None - class PasswordChange(BaseModel): current_password: str new_password: str @@ -286,13 +276,6 @@ class ProjectBase(BaseModel): job_no: str = Field(..., min_length=1, max_length=50) project_name: str = Field(..., min_length=1, max_length=200) -class ProjectCreate(ProjectBase): - pass - -class ProjectUpdate(BaseModel): - project_name: Optional[str] = Field(None, min_length=1, max_length=200) - is_active: Optional[bool] = None - class Project(ProjectBase): id: int created_by_id: Optional[int] = None @@ -303,50 +286,6 @@ class Project(ProjectBase): class Config: from_attributes = True -# DailyWork schemas -class DailyWorkBase(BaseModel): - date: datetime - worker_count: int = Field(gt=0) - overtime_workers: Optional[int] = 0 - overtime_hours: Optional[float] = 0 - -class DailyWorkCreate(DailyWorkBase): - pass - -class DailyWorkUpdate(BaseModel): - worker_count: Optional[int] = Field(None, gt=0) - overtime_workers: Optional[int] = None - overtime_hours: Optional[float] = None - -class DailyWork(DailyWorkBase): - id: int - regular_hours: float - overtime_total: float - total_hours: float - created_by_id: int - created_by: User - created_at: datetime - - class Config: - from_attributes = True - -class ProjectDailyWorkBase(BaseModel): - date: datetime - project_id: int - hours: float - -class ProjectDailyWorkCreate(ProjectDailyWorkBase): - pass - -class ProjectDailyWork(ProjectDailyWorkBase): - id: int - created_by_id: int - created_at: datetime - project: Project - - class Config: - from_attributes = True - # Report schemas class ReportRequest(BaseModel): start_date: datetime diff --git a/system3-nonconformance/api/routers/auth.py b/system3-nonconformance/api/routers/auth.py index dd9a1be..318b280 100644 --- a/system3-nonconformance/api/routers/auth.py +++ b/system3-nonconformance/api/routers/auth.py @@ -2,7 +2,6 @@ from fastapi import APIRouter, Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from sqlalchemy.orm import Session from typing import List -from pydantic import BaseModel from database.database import get_db from database.models import User, UserRole @@ -95,70 +94,6 @@ async def get_all_users( users = db.query(User).filter(User.is_active == True).all() return users -@router.post("/users", response_model=schemas.User) -async def create_user( - user: schemas.UserCreate, - current_admin: User = Depends(get_current_admin), - db: Session = Depends(get_db) -): - # 중복 확인 - db_user = db.query(User).filter(User.username == user.username).first() - if db_user: - raise HTTPException(status_code=400, detail="Username already registered") - - # 사용자 생성 - db_user = User( - username=user.username, - hashed_password=get_password_hash(user.password), - full_name=user.full_name, - role=user.role - ) - db.add(db_user) - db.commit() - db.refresh(db_user) - return db_user - -@router.put("/users/{user_id}", response_model=schemas.User) -async def update_user( - user_id: int, - user_update: schemas.UserUpdate, - current_admin: User = Depends(get_current_admin), - db: Session = Depends(get_db) -): - db_user = db.query(User).filter(User.id == user_id).first() - if not db_user: - raise HTTPException(status_code=404, detail="User not found") - - # 업데이트 - update_data = user_update.dict(exclude_unset=True) - if "password" in update_data: - update_data["hashed_password"] = get_password_hash(update_data.pop("password")) - - for field, value in update_data.items(): - setattr(db_user, field, value) - - db.commit() - db.refresh(db_user) - return db_user - -@router.delete("/users/{username}") -async def delete_user( - username: str, - current_admin: User = Depends(get_current_admin), - db: Session = Depends(get_db) -): - db_user = db.query(User).filter(User.username == username).first() - if not db_user: - raise HTTPException(status_code=404, detail="User not found") - - # hyungi 계정은 삭제 불가 - if db_user.username == "hyungi": - raise HTTPException(status_code=400, detail="Cannot delete primary admin user") - - db.delete(db_user) - db.commit() - return {"detail": "User deleted successfully"} - @router.post("/change-password") async def change_password( password_change: schemas.PasswordChange, @@ -177,24 +112,3 @@ async def change_password( db.commit() return {"detail": "Password changed successfully"} - -class PasswordReset(BaseModel): - new_password: str - -@router.post("/users/{user_id}/reset-password") -async def reset_user_password( - user_id: int, - password_reset: PasswordReset, - current_admin: User = Depends(get_current_admin), - db: Session = Depends(get_db) -): - """사용자 비밀번호 초기화 (관리자 전용)""" - db_user = db.query(User).filter(User.id == user_id).first() - if not db_user: - raise HTTPException(status_code=404, detail="User not found") - - # 새 비밀번호로 업데이트 - db_user.hashed_password = get_password_hash(password_reset.new_password) - db.commit() - - return {"detail": f"Password reset successfully for user {db_user.username}"} diff --git a/system3-nonconformance/api/routers/daily_work.py b/system3-nonconformance/api/routers/daily_work.py deleted file mode 100644 index fd9996f..0000000 --- a/system3-nonconformance/api/routers/daily_work.py +++ /dev/null @@ -1,163 +0,0 @@ -from fastapi import APIRouter, Depends, HTTPException -from sqlalchemy.orm import Session -from typing import List, Optional -from datetime import datetime, date, timezone, timedelta - -from database.database import get_db -from database.models import DailyWork, User, UserRole, KST -from database import schemas -from routers.auth import get_current_user - -router = APIRouter(prefix="/api/daily-work", tags=["daily-work"]) - -@router.post("/", response_model=schemas.DailyWork) -async def create_daily_work( - work: schemas.DailyWorkCreate, - current_user: User = Depends(get_current_user), - db: Session = Depends(get_db) -): - # 중복 확인 (같은 날짜) - existing = db.query(DailyWork).filter( - DailyWork.date == work.date.date() - ).first() - if existing: - raise HTTPException(status_code=400, detail="Daily work for this date already exists") - - # 계산 - regular_hours = work.worker_count * 8 # 정규 근무 8시간 - overtime_total = work.overtime_workers * work.overtime_hours - total_hours = regular_hours + overtime_total - - # 생성 - db_work = DailyWork( - date=work.date, - worker_count=work.worker_count, - regular_hours=regular_hours, - overtime_workers=work.overtime_workers, - overtime_hours=work.overtime_hours, - overtime_total=overtime_total, - total_hours=total_hours, - created_by_id=current_user.id - ) - db.add(db_work) - db.commit() - db.refresh(db_work) - return db_work - -@router.get("/", response_model=List[schemas.DailyWork]) -async def read_daily_works( - skip: int = 0, - limit: int = 100, - start_date: Optional[date] = None, - end_date: Optional[date] = None, - current_user: User = Depends(get_current_user), - db: Session = Depends(get_db) -): - query = db.query(DailyWork) - - if start_date: - query = query.filter(DailyWork.date >= start_date) - if end_date: - query = query.filter(DailyWork.date <= end_date) - - works = query.order_by(DailyWork.date.desc()).offset(skip).limit(limit).all() - return works - -@router.get("/{work_id}", response_model=schemas.DailyWork) -async def read_daily_work( - work_id: int, - current_user: User = Depends(get_current_user), - db: Session = Depends(get_db) -): - work = db.query(DailyWork).filter(DailyWork.id == work_id).first() - if not work: - raise HTTPException(status_code=404, detail="Daily work not found") - return work - -@router.put("/{work_id}", response_model=schemas.DailyWork) -async def update_daily_work( - work_id: int, - work_update: schemas.DailyWorkUpdate, - current_user: User = Depends(get_current_user), - db: Session = Depends(get_db) -): - work = db.query(DailyWork).filter(DailyWork.id == work_id).first() - if not work: - raise HTTPException(status_code=404, detail="Daily work not found") - - # 업데이트 - update_data = work_update.dict(exclude_unset=True) - - # 재계산 필요한 경우 - if any(key in update_data for key in ["worker_count", "overtime_workers", "overtime_hours"]): - worker_count = update_data.get("worker_count", work.worker_count) - overtime_workers = update_data.get("overtime_workers", work.overtime_workers) - overtime_hours = update_data.get("overtime_hours", work.overtime_hours) - - regular_hours = worker_count * 8 - overtime_total = overtime_workers * overtime_hours - total_hours = regular_hours + overtime_total - - update_data["regular_hours"] = regular_hours - update_data["overtime_total"] = overtime_total - update_data["total_hours"] = total_hours - - for field, value in update_data.items(): - setattr(work, field, value) - - db.commit() - db.refresh(work) - return work - -@router.delete("/{work_id}") -async def delete_daily_work( - work_id: int, - current_user: User = Depends(get_current_user), - db: Session = Depends(get_db) -): - work = db.query(DailyWork).filter(DailyWork.id == work_id).first() - if not work: - raise HTTPException(status_code=404, detail="Daily work not found") - - # 권한 확인 (관리자만 삭제 가능) - if current_user.role != UserRole.admin: - raise HTTPException(status_code=403, detail="Only admin can delete daily work") - - db.delete(work) - db.commit() - return {"detail": "Daily work deleted successfully"} - -@router.get("/stats/summary") -async def get_daily_work_stats( - start_date: Optional[date] = None, - end_date: Optional[date] = None, - current_user: User = Depends(get_current_user), - db: Session = Depends(get_db) -): - """일일 공수 통계""" - query = db.query(DailyWork) - - if start_date: - query = query.filter(DailyWork.date >= start_date) - if end_date: - query = query.filter(DailyWork.date <= end_date) - - works = query.all() - - if not works: - return { - "total_days": 0, - "total_hours": 0, - "total_overtime": 0, - "average_daily_hours": 0 - } - - total_hours = sum(w.total_hours for w in works) - total_overtime = sum(w.overtime_total for w in works) - - return { - "total_days": len(works), - "total_hours": total_hours, - "total_overtime": total_overtime, - "average_daily_hours": total_hours / len(works) - } diff --git a/system3-nonconformance/api/routers/page_permissions.py b/system3-nonconformance/api/routers/page_permissions.py index 6af34e1..87a6d3a 100644 --- a/system3-nonconformance/api/routers/page_permissions.py +++ b/system3-nonconformance/api/routers/page_permissions.py @@ -50,11 +50,8 @@ DEFAULT_PAGES = { 'issues_management': {'title': '관리함', 'default_access': False}, 'issues_archive': {'title': '폐기함', 'default_access': False}, 'issues_dashboard': {'title': '현황판', 'default_access': True}, - 'projects_manage': {'title': '프로젝트 관리', 'default_access': False}, - 'daily_work': {'title': '일일 공수', 'default_access': False}, 'reports': {'title': '보고서', 'default_access': False}, 'reports_daily': {'title': '일일보고서', 'default_access': False}, - 'users_manage': {'title': '사용자 관리', 'default_access': False} } diff --git a/system3-nonconformance/api/routers/projects.py b/system3-nonconformance/api/routers/projects.py index 81b6f1e..86e7f9c 100644 --- a/system3-nonconformance/api/routers/projects.py +++ b/system3-nonconformance/api/routers/projects.py @@ -1,6 +1,5 @@ from fastapi import APIRouter, Depends, HTTPException, Request, status from database.models import User, UserRole -from database.schemas import ProjectCreate, ProjectUpdate from routers.auth import get_current_user from utils.tkuser_client import get_token_from_request import utils.tkuser_client as tkuser_client @@ -10,30 +9,11 @@ router = APIRouter( tags=["projects"] ) -def check_admin_permission(current_user: User = Depends(get_current_user)): - """관리자 권한 확인""" - if current_user.role != UserRole.admin: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail="관리자 권한이 필요합니다." - ) - return current_user - @router.options("/") async def projects_options(): """OPTIONS preflight 요청 처리""" return {"message": "OK"} -@router.post("/") -async def create_project( - project: ProjectCreate, - request: Request, - current_user: User = Depends(check_admin_permission) -): - """프로젝트 생성 (관리자만) - tkuser API로 프록시""" - token = get_token_from_request(request) - return await tkuser_client.create_project(token, project.dict()) - @router.get("/") async def get_projects( request: Request, @@ -60,27 +40,3 @@ async def get_project( detail="프로젝트를 찾을 수 없습니다." ) return project - -@router.put("/{project_id}") -async def update_project( - project_id: int, - project_update: ProjectUpdate, - request: Request, - current_user: User = Depends(check_admin_permission) -): - """프로젝트 수정 (관리자만) - tkuser API로 프록시""" - token = get_token_from_request(request) - return await tkuser_client.update_project( - token, project_id, project_update.dict(exclude_unset=True) - ) - -@router.delete("/{project_id}") -async def delete_project( - project_id: int, - request: Request, - current_user: User = Depends(check_admin_permission) -): - """프로젝트 삭제 (비활성화) (관리자만) - tkuser API로 프록시""" - token = get_token_from_request(request) - await tkuser_client.delete_project(token, project_id) - return {"message": "프로젝트가 삭제되었습니다."} diff --git a/system3-nonconformance/api/routers/reports.py b/system3-nonconformance/api/routers/reports.py index 7351a6e..b8b1df1 100644 --- a/system3-nonconformance/api/routers/reports.py +++ b/system3-nonconformance/api/routers/reports.py @@ -14,7 +14,7 @@ from openpyxl.drawing.image import Image as XLImage import os from database.database import get_db -from database.models import Issue, DailyWork, IssueStatus, IssueCategory, User, UserRole, ReviewStatus +from database.models import Issue, IssueStatus, IssueCategory, User, UserRole, ReviewStatus from database import schemas from routers.auth import get_current_user from routers.page_permissions import check_page_access @@ -32,14 +32,9 @@ async def generate_report_summary( """보고서 요약 생성""" start_date = report_request.start_date end_date = report_request.end_date - - # 일일 공수 합계 - daily_works = db.query(DailyWork).filter( - DailyWork.date >= start_date.date(), - DailyWork.date <= end_date.date() - ).all() - total_hours = sum(w.total_hours for w in daily_works) - + + total_hours = 0 + # 이슈 통계 issues_query = db.query(Issue).filter( Issue.report_date >= start_date, @@ -118,29 +113,6 @@ async def get_report_issues( "detail_notes": issue.detail_notes } for issue in issues] -@router.get("/daily-works") -async def get_report_daily_works( - start_date: datetime, - end_date: datetime, - current_user: User = Depends(get_current_user), - db: Session = Depends(get_db) -): - """보고서용 일일 공수 목록""" - works = db.query(DailyWork).filter( - DailyWork.date >= start_date.date(), - DailyWork.date <= end_date.date() - ).order_by(DailyWork.date).all() - - return [{ - "date": work.date, - "worker_count": work.worker_count, - "regular_hours": work.regular_hours, - "overtime_workers": work.overtime_workers, - "overtime_hours": work.overtime_hours, - "overtime_total": work.overtime_total, - "total_hours": work.total_hours - } for work in works] - @router.get("/daily-preview") async def preview_daily_report( project_id: int, diff --git a/system3-nonconformance/web/admin.html b/system3-nonconformance/web/admin.html deleted file mode 100644 index 133240d..0000000 --- a/system3-nonconformance/web/admin.html +++ /dev/null @@ -1,913 +0,0 @@ - - -
- - -로딩 중...
-화면 크기:
-User Agent:
-현재 시간:
-