from sqlalchemy.orm import Session,defer from sqlalchemy import func, or_ from sqlalchemy import desc from app import models, schemas from .utils import hash_password, verify_password from typing import List, Optional, Dict, Any import datetime import re # Status mapping STATUS_NAMES = { 1: "created", 2: "active", 3: "disabled", } def authenticate_user(db: Session, email: str, password: str) -> Optional[models.User]: """Authenticate user by email and password. Returns user if credentials are valid, None otherwise. """ user = db.query(models.User).filter(models.User.email == email).first() if not user: return None # print(user.hashed_password) if not verify_password(password, user.hashed_password or ""): return None return user def get_unit_name(db: Session, unit_id: int) -> Optional[str]: """Get unit name by unit_id.""" if not unit_id: return None unit = db.query(models.Unit).filter(models.Unit.id == unit_id).first() return unit.name if unit else None def get_system_name(db: Session, system_id: int) -> Optional[str]: """Get system name by system_id.""" if not system_id: return None system = db.query(models.ManageSystem).filter(models.ManageSystem.id == system_id).first() return system.name if system else None def get_system_type_name(system_type: Optional[int]) -> Optional[str]: """Get system type name by system_type.""" types = {1: "Trọng điểm", 2: "Cốt lõi", 3: "Nền tảng", 4: "Vệ tinh", 5: "Khác"} return types.get(system_type) def get_soc_type_name(db: Session, type_id: Optional[int]) -> Optional[str]: """Get SOC target type name by id.""" if not type_id: return None row = db.query(models.SocTargetType).filter(models.SocTargetType.id == type_id).first() return row.name if row else None def list_soc_target_types(db: Session) -> List[models.SocTargetType]: """List all SOC target types.""" return db.query(models.SocTargetType).all() def get_ticket_related_list(db: Session, target_id: int) -> List[schemas.TicketRelated]: """Get related tickets for a target.""" rows = db.query(models.TicketRelated).filter(models.TicketRelated.soc_target_id == target_id).all() res = [] for r in rows: tr = schemas.TicketRelated.model_validate(r) # Fetch related ticket details for name/code related = db.query(models.SocTarget).filter(models.SocTarget.id == r.related_soc_target_id).first() if related: tr.related_ticket_name = related.name tr.related_ticket_code = related.code res.append(tr) return res if system_type is None: return None mapping = { 1: "Trọng điểm", 2: "Cốt lõi", 3: "Nền tảng", 4: "Vệ tinh", 5: "Khác" } return mapping.get(system_type) def get_user_with_details(db: Session, user: models.User) -> Dict[str, Any]: """Convert user model to dict with unit_name and status_name.""" unit_name = get_unit_name(db, user.unit_id) status_name = STATUS_NAMES.get(user.status, "unknown") role_name = None if getattr(user, "role_id", None): r = db.query(models.Role).filter(models.Role.id == user.role_id).first() role_name = r.name if r else None return { "id": user.id, "email": user.email, "fullname": user.fullname, "unit_id": user.unit_id, "unit_name": unit_name, "role_id": getattr(user, "role_id", None), "role_name": role_name, "status": user.status, "status_name": status_name, "created_at": user.created_at, "updated_at": user.updated_at, } # Role def create_role(db: Session, role_in: schemas.RoleCreate) -> models.Role: db_obj = models.Role(name=role_in.name, description=role_in.description) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def bulk_create_log_sources(db: Session, items: list[schemas.LogSourceCreate]) -> int: """Batch insert log source records.""" if not items: return 0 db_objs = [ models.LogSource( month_id=item.month_id, unit_id=item.unit_id, log_id=item.log_id, name=item.name, description=item.description, is_enabled=item.is_enabled if item.is_enabled is not None else True, source_type_id=item.source_type_id, status=item.status, message=item.message, other=item.other, file_id=item.file_id, created_at=item.created_at if getattr(item, "created_at", None) is not None else None, ) for item in items ] db.add_all(db_objs) db.commit() return len(db_objs) # -------- PentestService CRUD -------- PENTEST_STATUS_NAMES = { 0: "Nháp", 1: "Gửi yêu cầu", 2: "Tiếp nhận", 3: "Đang xử lý", 4: "Đóng", 5: "Hủy" } def generate_pentest_code(db: Session) -> str: now = datetime.datetime.now() month_str = now.strftime("%m%Y") prefix = f"ANTT.{month_str}." # Find the latest code for this month to avoid Duplicate Entry errors last_record = db.query(models.PentestService.code).filter( models.PentestService.code.like(f"{prefix}%") ).order_by(desc(models.PentestService.code)).first() if last_record: try: # Extract number from format ANTT.MMYYYY.NNN last_code = last_record[0] num_part = last_code.split('.')[-1] new_number = int(num_part) + 1 except (ValueError, IndexError): # Fallback if parsing fails new_number = db.query(models.PentestService).filter(models.PentestService.code.like(f"{prefix}%")).count() + 1 else: new_number = 1 return f"{prefix}{new_number:03d}" def create_pentest_service( db: Session, obj_in: schemas.PentestServiceCreate, user_id: int, is_admin_user: bool = False ) -> models.PentestService: # Rule check: Chỉ Admin/Administrator mới được set target_id khi tạo if obj_in.target_id is not None and not is_admin_user: raise ValueError("Bạn không có quyền thiết lập target_id.") # Rule check: list_process chỉ nhóm Admin/Administrator có quyền if obj_in.list_process and not is_admin_user: raise ValueError("Bạn không có quyền gán người dùng.") # Nếu là admin và có truyền user_id thì dùng user_id đó, ngược lại dùng user_id của người tạo final_user_id = obj_in.user_id if is_admin_user and obj_in.user_id is not None else user_id code = generate_pentest_code(db) # Loại bỏ user_id và list_process khỏi dict để tránh duplicate keyword argument obj_dict = obj_in.dict() obj_dict.pop("user_id", None) list_process = obj_dict.pop("list_process", None) other_assign = obj_dict.pop("other_assign", None) db_obj = models.PentestService( **obj_dict, code=code, status=0, # Default is draft user_id=final_user_id ) db.add(db_obj) db.commit() db.refresh(db_obj) # Lưu danh sách gán nếu có (user_id) if list_process: for uid in list_process: db.add(models.PentestServiceAssign( user_id=uid, pentest_service_id=db_obj.id )) # Lưu danh sách gán email nếu có (other_assign) if other_assign: for email in other_assign: db.add(models.PentestServiceAssign( email=email, pentest_service_id=db_obj.id )) if list_process or other_assign: db.commit() # Log history history = models.PentestServiceHistory( pentest_service_id=db_obj.id, new_status=0, user_id=user_id # Người thực hiện hành động ) db.add(history) db.commit() return db_obj def get_pentest_service(db: Session, id: int) -> models.PentestService | None: return db.query(models.PentestService).filter(models.PentestService.id == id).first() def get_pentest_assigned_emails(db: Session, service_id: int) -> list[str]: """Get emails of users assigned to a pentest service.""" rows = db.query(models.User.email).join( models.PentestServiceAssign, models.User.id == models.PentestServiceAssign.user_id ).filter(models.PentestServiceAssign.pentest_service_id == service_id).all() return [r[0] for r in rows] def get_pentest_assigned_names(db: Session, service_id: int) -> list[str]: """Get fullnames of users assigned to a pentest service.""" rows = db.query(models.User.fullname).join( models.PentestServiceAssign, models.User.id == models.PentestServiceAssign.user_id ).filter(models.PentestServiceAssign.pentest_service_id == service_id).all() return [r[0] for r in rows] def get_pentest_assigned_user_ids(db: Session, service_id: int) -> list[int]: """Get IDs of users assigned to a pentest service.""" rows = db.query(models.PentestServiceAssign.user_id).filter( models.PentestServiceAssign.pentest_service_id == service_id, models.PentestServiceAssign.user_id.isnot(None) ).all() return [int(r[0]) for r in rows if r[0] is not None] def get_pentest_other_assigned_emails(db: Session, service_id: int) -> list[str]: """Get other assigned emails (where user_id is null) for a pentest service.""" rows = db.query(models.PentestServiceAssign.email).filter( models.PentestServiceAssign.pentest_service_id == service_id, models.PentestServiceAssign.user_id == None ).all() return [r[0] for r in rows if r[0]] def list_pentest_services( db: Session, unit_id: int | None = None, user_id: int | None = None, status: int | None = None, month_id: int | None = None, skip: int = 0, limit: int = 100 ) -> tuple[list[models.PentestService], int]: query = db.query(models.PentestService) if unit_id is not None: query = query.filter(models.PentestService.unit_id == unit_id) if user_id is not None: # Show tasks created by the user OR assigned to the user assigned_ids = db.query(models.PentestServiceAssign.pentest_service_id).filter( models.PentestServiceAssign.user_id == user_id ).subquery() query = query.filter( or_( models.PentestService.user_id == user_id, models.PentestService.id.in_(assigned_ids) ) ) if status is not None: query = query.filter(models.PentestService.status == status) if month_id is not None: month_obj = db.query(models.Month).filter(models.Month.id == month_id).first() if month_obj: query = query.filter( models.PentestService.created_at >= month_obj.from_date, models.PentestService.created_at <= month_obj.end_date ) total = query.count() items = query.order_by(models.PentestService.id.desc()).offset(skip).limit(limit).all() return items, total def update_pentest_service( db: Session, id: int, obj_in: schemas.PentestServiceUpdate, user_id: int, is_admin_user: bool = False ) -> models.PentestService | None: db_obj = get_pentest_service(db, id) if not db_obj: return None old_status = db_obj.status new_status = obj_in.status if obj_in.status is not None else old_status # Rule check: Đã update sang 1 (Tạo mới) thì không chỉnh sửa được nội dung task. if old_status >= 1 and any(v is not None for k, v in obj_in.dict(exclude={"status", "list_process"}).items() if v is not None): if not is_admin_user: # Admin might still be able to edit? Request says "không chỉnh sửa được nội dung task" raise ValueError("Task đã được gửi, không thể chỉnh sửa nội dung.") # Rule check: Update status từ 0 sang 1 thì chỉ nhóm quyền Manager, Member. # Không cho phép update từ 1 về 0. if old_status == 0 and new_status == 1: # Check permissions in router, but logic here: pass elif old_status >= 1 and new_status == 0: raise ValueError("Không thể chuyển trạng thái từ đã gửi về nháp.") # Rule check: Update các trạng thái 2,3,4,5 thì chỉ nhóm Administator, Admin được phép. if new_status in [2, 3, 4, 5] and not is_admin_user: raise ValueError("Bạn không có quyền chuyển sang trạng thái này.") # Rule check: Chỉ Admin/Administrator mới được cập nhật target_id if obj_in.target_id is not None and not is_admin_user: raise ValueError("Bạn không có quyền cập nhật target_id.") # Rule check: list_process chỉ Admin/Administrator có quyền if obj_in.list_process and not is_admin_user: raise ValueError("Bạn không có quyền gán người dùng.") data = obj_in.dict(exclude_unset=True) list_process = data.pop("list_process", None) other_assign = data.pop("other_assign", None) # Nếu không phải admin, không cho phép cập nhật unit_id và user_id if not is_admin_user: data.pop("unit_id", None) data.pop("user_id", None) for k, v in data.items(): setattr(db_obj, k, v) # Cập nhật danh sách gán nếu là admin if list_process is not None or other_assign is not None: # Xóa các gán cũ db.query(models.PentestServiceAssign).filter( models.PentestServiceAssign.pentest_service_id == id ).delete() # Thêm các gán mới (user_id) if list_process: for uid in list_process: db.add(models.PentestServiceAssign( user_id=uid, pentest_service_id=id )) # Thêm các gán mới (email) if other_assign: for email in other_assign: db.add(models.PentestServiceAssign( email=email, pentest_service_id=id )) db.commit() db.add(db_obj) if old_status != new_status: history = models.PentestServiceHistory( pentest_service_id=id, old_status=old_status, new_status=new_status, user_id=user_id ) db.add(history) db.commit() db.refresh(db_obj) return db_obj def delete_pentest_service(db: Session, id: int) -> bool: db_obj = get_pentest_service(db, id) if not db_obj: return False if db_obj.status != 0: raise ValueError("Chỉ có thể xóa task ở trạng thái nháp.") # Xóa comments liên quan db.query(models.PentestServiceComment).filter(models.PentestServiceComment.pentest_service_id == id).delete() # Xóa history liên quan db.query(models.PentestServiceHistory).filter(models.PentestServiceHistory.pentest_service_id == id).delete() # Xóa gán liên quan db.query(models.PentestServiceAssign).filter(models.PentestServiceAssign.pentest_service_id == id).delete() db.delete(db_obj) db.commit() return True # Comment CRUD def create_pentest_comment(db: Session, obj_in: schemas.PentestServiceCommentCreate, user_id: int) -> models.PentestServiceComment: db_obj = models.PentestServiceComment( **obj_in.dict(), user_id=user_id ) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def list_pentest_comments(db: Session, pentest_service_id: int) -> list[models.PentestServiceComment]: return db.query(models.PentestServiceComment).filter( models.PentestServiceComment.pentest_service_id == pentest_service_id ).order_by(models.PentestServiceComment.id.asc()).all() def delete_pentest_comment(db: Session, id: int) -> bool: db_obj = db.query(models.PentestServiceComment).filter(models.PentestServiceComment.id == id).first() if not db_obj: return False db.delete(db_obj) db.commit() return True # History CRUD def list_pentest_histories(db: Session, pentest_service_id: int) -> list[models.PentestServiceHistory]: return db.query(models.PentestServiceHistory).filter( models.PentestServiceHistory.pentest_service_id == pentest_service_id ).order_by(models.PentestServiceHistory.id.desc()).all() def list_roles(db: Session, skip: int = 0, limit: int = 100) -> List[models.Role]: return db.query(models.Role).offset(skip).limit(limit).all() def get_role(db: Session, role_id: int) -> Optional[models.Role]: return db.query(models.Role).filter(models.Role.id == role_id).first() def update_role(db: Session, role_id: int, role_in: schemas.RoleCreate) -> Optional[models.Role]: """Update a role by ID.""" db_obj = db.query(models.Role).filter(models.Role.id == role_id).first() if not db_obj: return None db_obj.name = role_in.name db_obj.description = role_in.description db.commit() db.refresh(db_obj) return db_obj def delete_role(db: Session, role_id: int) -> bool: """Delete a role by ID. Returns True if deleted, False if not found.""" db_obj = db.query(models.Role).filter(models.Role.id == role_id).first() if not db_obj: return False db.delete(db_obj) db.commit() return True # Units & Users def create_unit(db: Session, unit_in: schemas.UnitCreate) -> models.Unit: db_obj = models.Unit(name=unit_in.name, description=unit_in.description) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def list_units(db: Session, skip: int = 0, limit: int = 100) -> List[models.Unit]: return db.query(models.Unit).offset(skip).limit(limit).all() def get_unit(db: Session, unit_id: int) -> Optional[models.Unit]: return db.query(models.Unit).filter(models.Unit.id == unit_id).first() def update_unit(db: Session, unit_id: int, unit_in: schemas.UnitCreate) -> Optional[models.Unit]: db_obj = db.query(models.Unit).filter(models.Unit.id == unit_id).first() if not db_obj: return None db_obj.name = unit_in.name db_obj.description = unit_in.description db.commit() db.refresh(db_obj) return db_obj def delete_unit(db: Session, unit_id: int) -> bool: db_obj = db.query(models.Unit).filter(models.Unit.id == unit_id).first() if not db_obj: return False db.delete(db_obj) db.commit() return True def create_user(db: Session, user_in: schemas.UserCreate) -> models.User: hashed = None if getattr(user_in, 'password', None): pw = user_in.password or "" email = user_in.email if len(pw) < 8 or not re.search(r"[A-Z]", pw) or not re.search(r"[a-z]", pw) or not re.search(r"\d", pw) or not re.search(r"[^\w\s]", pw) or (email and email.lower() in pw.lower()): raise ValueError("Mật khẩu không đủ mạnh") hashed = hash_password(user_in.password) db_obj = models.User( email=user_in.email, fullname=user_in.fullname, unit_id=user_in.unit_id, role_id=user_in.role_id, hashed_password=hashed, ) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def get_user_by_email(db: Session, email: str) -> Optional[models.User]: return db.query(models.User).filter(models.User.email == email).first() def list_users(db: Session, skip: int = 0, limit: int = 100, unit_id: Optional[int] = None) -> List[models.User]: query = db.query(models.User) if unit_id is not None: query = query.filter(models.User.unit_id == unit_id) return query.order_by(models.User.id.desc()).offset(skip).limit(limit).all() def search_users(db: Session, query: str, skip: int = 0, limit: int = 100) -> List[models.User]: """Search users by email or fullname.""" search = f"%{query}%" return db.query(models.User).filter( or_( models.User.email.ilike(search), models.User.fullname.ilike(search) ) ).order_by(models.User.id.desc()).offset(skip).limit(limit).all() def get_user(db: Session, user_id: int) -> Optional[models.User]: return db.query(models.User).filter(models.User.id == user_id).first() def list_user_emails_by_role(db: Session, role_id: int) -> List[str]: rows = db.query(models.User.email).filter(models.User.role_id == role_id).all() return [r[0] for r in rows] def update_user(db: Session, user_id: int, user_in: schemas.UserUpdate) -> Optional[models.User]: db_obj = db.query(models.User).filter(models.User.id == user_id).first() if not db_obj: return None if user_in.email and user_in.email != db_obj.email: existing = db.query(models.User).filter( models.User.email == user_in.email, models.User.id != user_id ).first() if existing: raise ValueError("Email already registered") db_obj.email = user_in.email if user_in.fullname is not None: db_obj.fullname = user_in.fullname if user_in.unit_id is not None: db_obj.unit_id = user_in.unit_id if getattr(user_in, "role_id", None) is not None: db_obj.role_id = user_in.role_id if user_in.status is not None: db_obj.status = user_in.status if user_in.password: pw = user_in.password or "" email = user_in.email or db_obj.email if len(pw) < 8 or not re.search(r"[A-Z]", pw) or not re.search(r"[a-z]", pw) or not re.search(r"\d", pw) or not re.search(r"[^\w\s]", pw) or (email and email.lower() in pw.lower()): raise ValueError("Mật khẩu không đủ mạnh") db_obj.hashed_password = hash_password(user_in.password) db.commit() db.refresh(db_obj) return db_obj # Category & Posts def create_category(db: Session, cat_in: schemas.CategoryCreate) -> models.Category: db_obj = models.Category(name=cat_in.name) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def list_categories(db: Session, skip: int = 0, limit: int = 100) -> List[models.Category]: return db.query(models.Category).offset(skip).limit(limit).all() # Thêm mới: CRUD cho Category def get_category(db: Session, category_id: int) -> Optional[models.Category]: return db.query(models.Category).filter(models.Category.id == category_id).first() def get_category_by_name(db: Session, name: str) -> Optional[models.Category]: return db.query(models.Category).filter(models.Category.name == name).first() def update_category(db: Session, category_id: int, cat_in: schemas.CategoryCreate) -> Optional[models.Category]: db_obj = db.query(models.Category).filter(models.Category.id == category_id).first() if not db_obj: return None db_obj.name = cat_in.name db.commit() db.refresh(db_obj) return db_obj def delete_category(db: Session, category_id: int) -> bool: db_obj = db.query(models.Category).filter(models.Category.id == category_id).first() if not db_obj: return False db.delete(db_obj) db.commit() return True def create_post(db: Session, post_in: schemas.PostCreate) -> models.Post: db_obj = models.Post( content=post_in.content, title=post_in.title, created_by=post_in.created_by, category_id=post_in.category_id, status=post_in.status, thumbnail=post_in.thumbnail, ) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def create_email_role(db: Session, obj_in: schemas.EmailRoleCreate) -> models.EmailRole: db_obj = models.EmailRole(name=obj_in.name, description=obj_in.description) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def get_email_role(db: Session, id: int) -> models.EmailRole | None: return db.query(models.EmailRole).filter(models.EmailRole.id == id).first() def update_email_role(db: Session, id: int, obj_in: schemas.EmailRoleUpdate) -> models.EmailRole | None: db_obj = get_email_role(db, id) if not db_obj: return None data = obj_in.dict(exclude_unset=True) for k, v in data.items(): setattr(db_obj, k, v) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def delete_email_role(db: Session, id: int) -> bool: db_obj = get_email_role(db, id) if not db_obj: return False db.delete(db_obj) db.commit() return True def list_email_users_by_role(db: Session, role_id: int) -> list[models.EmailUser]: return db.query(models.EmailUser).filter(models.EmailUser.role_id == role_id).all() def list_email_users_by_role_paginated( db: Session, role_id: int, skip: int = 0, limit: int = 100, ) -> list[models.EmailUser]: return ( db.query(models.EmailUser) .filter(models.EmailUser.role_id == role_id) .order_by(models.EmailUser.id.desc()) .offset(skip) .limit(limit) .all() ) def count_email_users_by_role(db: Session, role_id: int) -> int: return int(db.query(func.count(models.EmailUser.id)).filter(models.EmailUser.role_id == role_id).scalar() or 0) def list_email_roles(db: Session, skip: int = 0, limit: int = 100) -> list[models.EmailRole]: return ( db.query(models.EmailRole) .order_by(models.EmailRole.id.desc()) .offset(skip) .limit(limit) .all() ) def create_email_user(db: Session, obj_in: schemas.EmailUserCreate) -> models.EmailUser: db_obj = models.EmailUser(email=obj_in.email, name=obj_in.name, role_id=obj_in.role_id, type=obj_in.type) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def get_email_user(db: Session, id: int) -> models.EmailUser | None: return db.query(models.EmailUser).filter(models.EmailUser.id == id).first() def get_email_user_by_email_and_role(db: Session, email: str, role_id: int) -> models.EmailUser | None: return ( db.query(models.EmailUser) .filter(models.EmailUser.email == email, models.EmailUser.role_id == role_id) .first() ) def update_email_user(db: Session, id: int, obj_in: schemas.EmailUserUpdate) -> models.EmailUser | None: db_obj = get_email_user(db, id) if not db_obj: return None data = obj_in.dict(exclude_unset=True) for k, v in data.items(): setattr(db_obj, k, v) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def delete_email_user(db: Session, id: int) -> bool: db_obj = get_email_user(db, id) if not db_obj: return False db.delete(db_obj) db.commit() return True def list_email_users( db: Session, skip: int = 0, limit: int = 100, role_id: Optional[int] = None, ) -> list[models.EmailUser]: q = db.query(models.EmailUser) if role_id is not None: q = q.filter(models.EmailUser.role_id == role_id) return q.order_by(models.EmailUser.id.desc()).offset(skip).limit(limit).all() def list_posts(db: Session, skip: int = 0, limit: int = 100) -> List[models.Post]: return ( db.query(models.Post) .order_by(models.Post.id.desc()) .offset(skip) .limit(limit) .all() ) def list_posts_by_category(db: Session, category_id: int, skip: int = 0, limit: int = 100) -> List[models.Post]: return ( db.query(models.Post) .filter(models.Post.category_id == category_id) .order_by(models.Post.id.desc()) .offset(skip) .limit(limit) .all() ) def get_user_fullname(db: Session, user_id: Optional[int]) -> Optional[str]: if not user_id: return None user = db.query(models.User).filter(models.User.id == user_id).first() return user.fullname if user else None # Month & stats def create_month(db: Session, m_in: schemas.MonthCreate) -> models.Month: db_obj = models.Month(name=m_in.name, from_date=m_in.from_date, end_date=m_in.end_date) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def list_months(db: Session, skip: int = 0, limit: int = 100) -> List[models.Month]: return ( db.query(models.Month) .order_by(models.Month.id.desc()) .offset(skip) .limit(limit) .all() ) def get_month(db: Session, month_id: int) -> Optional[models.Month]: return db.query(models.Month).filter(models.Month.id == month_id).first() def update_month(db: Session, month_id: int, m_in: schemas.MonthCreate) -> Optional[models.Month]: db_obj = db.query(models.Month).filter(models.Month.id == month_id).first() if not db_obj: return None db_obj.name = m_in.name db_obj.from_date = m_in.from_date db_obj.end_date = m_in.end_date db.commit() db.refresh(db_obj) return db_obj def delete_month(db: Session, month_id: int) -> bool: db_obj = db.query(models.Month).filter(models.Month.id == month_id).first() if not db_obj: return False db.delete(db_obj) db.commit() return True def create_nac(db: Session, nac_in: schemas.NACCreate) -> models.NAC: db_obj = models.NAC(month_id=nac_in.month_id, unit_id=nac_in.unit_id, total=nac_in.total, installed=nac_in.installed, ignored=nac_in.ignored) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def get_nac_stats_by_month(db: Session, month_id: int) -> Dict[str, Any]: total, installed, ignored, rate = db.query( func.coalesce(func.sum(models.NAC.total), 0), func.coalesce(func.sum(models.NAC.installed), 0), func.coalesce(func.sum(models.NAC.ignored), 0), func.coalesce(func.avg(models.NAC.rate), 0.0), ).filter(models.NAC.month_id == month_id).one() return { "total": int(total or 0), "installed": int(installed or 0), "ignored": int(ignored or 0), "rate": round(float(rate or 0.0), 2), } def get_smartir_stats_by_month(db: Session, month_id: int) -> Dict[str, Any]: total, installed, new_install, ignored, rate = db.query( func.coalesce(func.sum(models.SmartIR.total), 0), func.coalesce(func.sum(models.SmartIR.installed), 0), func.coalesce(func.sum(models.SmartIR.new_install), 0), func.coalesce(func.sum(models.SmartIR.ignored), 0), func.coalesce(func.avg(models.SmartIR.rate), 0.0), ).filter(models.SmartIR.month_id == month_id).one() return { "total": int(total or 0), "installed": int(installed or 0), "new_install": int(new_install or 0), "ignored": int(ignored or 0), "rate": round(float(rate or 0.0), 2), } def get_nac_stats_by_month_grouped_by_unit( db: Session, month_id: int, unit_id: Optional[int] = None ) -> List[Dict[str, Any]]: q = db.query( models.NAC.unit_id, func.coalesce(func.sum(models.NAC.total), 0), func.coalesce(func.sum(models.NAC.installed), 0), func.coalesce(func.sum(models.NAC.ignored), 0), func.coalesce(func.avg(models.NAC.rate), 0.0), ).filter(models.NAC.month_id == month_id) if unit_id is not None: q = q.filter(models.NAC.unit_id == unit_id) rows = q.group_by(models.NAC.unit_id).order_by(models.NAC.unit_id.asc()).all() data: List[Dict[str, Any]] = [] for uid, total, installed, ignored, rate in rows: t = int(total or 0) ins = int(installed or 0) ign = int(ignored or 0) data.append({ "unit_id": int(uid), "unit_name": get_unit_name(db, int(uid)) if uid is not None else None, "total": t, "installed": ins, "ignored": ign, "rate": round(float(rate or 0.0), 2), }) return data # -------- SOC Target CRUD -------- SOC_STATUS_NAMES = { 0: "Open", 1: "In Progress", 2: "Resolved", 3: "Closed" } def generate_soc_code(db: Session) -> str: now = datetime.datetime.now() month_str = now.strftime("%Y%m") prefix = f"SOC.{month_str}." # Find the latest code for this month to avoid Duplicate Entry errors last_record = db.query(models.SocTarget.code).filter( models.SocTarget.code.like(f"{prefix}%") ).order_by(desc(models.SocTarget.code)).first() if last_record: try: # Extract number from format SOC.YYYYMM.NNN last_code = last_record[0] num_part = last_code.split('.')[-1] new_number = int(num_part) + 1 except (ValueError, IndexError): # Fallback if parsing fails new_number = db.query(models.SocTarget).filter(models.SocTarget.code.like(f"{prefix}%")).count() + 1 else: new_number = 1 generated_code = f"{prefix}{new_number:03d}" return generated_code def calculate_deadline(priority: int) -> datetime.datetime: now = datetime.datetime.utcnow() if priority == 1: return now + datetime.timedelta(hours=24) elif priority == 2: return now + datetime.timedelta(hours=48) elif priority == 3: return now + datetime.timedelta(days=5) else: return now + datetime.timedelta(days=7) # Default for other priorities def get_sla_status(deadline: datetime.datetime, status: int, completion_time: Optional[datetime.datetime] = None) -> str: if not deadline: return "Trong hạn" # Nếu đã Resolved (2) hoặc Closed (3), dùng thời gian hoàn thành để so sánh if status >= 2: if not completion_time: return "Đúng hạn" return "Đúng hạn" if completion_time <= deadline else "Quá hạn" # Nếu chưa hoàn thành, dùng thời gian hiện tại để so sánh now = datetime.datetime.utcnow() if now > deadline: return "Quá hạn" if (deadline - now).total_seconds() < 24 * 3600: # Less than 24 hours return "Sắp đến hạn" return "Trong hạn" def get_soc_completion_time(db: Session, target_id: int) -> Optional[datetime.datetime]: # Lấy bản ghi lịch sử cập nhật status sang Resolved (2) hoặc Closed (3) gần nhất history = db.query(models.SocTargetHistory).filter( models.SocTargetHistory.target_id == target_id, models.SocTargetHistory.field_name == "status", models.SocTargetHistory.new_value.in_(["2", "3"]) ).order_by(desc(models.SocTargetHistory.changed_at)).first() return history.changed_at if history else None def create_soc_target_attachment(db: Session, target_id: int, file_name: str, file_path: str, file_size: int, file_type: str, uploaded_by: int): attachment = models.SocTargetAttachment( target_id=target_id, file_name=file_name, file_path=file_path, file_size=file_size, file_type=file_type, uploaded_by=uploaded_by ) db.add(attachment) db.commit() db.refresh(attachment) return attachment def create_soc_target(db: Session, obj_in: schemas.SocTargetCreate, user_id: int) -> models.SocTarget: obj_dict = obj_in.dict() assign_internal = obj_dict.pop("assign_internal", None) assign_external = obj_dict.pop("assign_external", None) related_ticket_ids = obj_dict.pop("related_ticket_ids", None) # Generate code if not provided if not obj_in.code or obj_in.code.strip() == "": obj_dict["code"] = generate_soc_code(db) # Calculate deadline if not provided if not obj_in.deadline: obj_dict["deadline"] = calculate_deadline(obj_dict["priority"]) db_obj = models.SocTarget(**obj_dict, created_by=user_id) db.add(db_obj) db.commit() db.refresh(db_obj) # Process related tickets if related_ticket_ids: r_ids = [r.strip() for r in related_ticket_ids.split(",") if r.strip()] for r_id in r_ids: try: db.add(models.TicketRelated( soc_target_id=db_obj.id, related_soc_target_id=int(r_id) )) except ValueError: pass db.commit() # Process assignees if assign_internal: uids = [u.strip() for u in assign_internal.split(",") if u.strip()] for uid in uids: try: db.add(models.SocTargetAssign( target_id=db_obj.id, user_id=int(uid) )) except ValueError: pass if assign_external: emails = [e.strip() for e in assign_external.split(",") if e.strip()] for email in emails: db.add(models.SocTargetAssign( target_id=db_obj.id, email=email )) if assign_internal or assign_external: db.commit() # Log history for creation db.add(models.SocTargetHistory( target_id=db_obj.id, field_name="status", new_value=str(db_obj.status), changed_by=user_id )) db.commit() return db_obj def get_soc_target(db: Session, target_id: int) -> models.SocTarget | None: return db.query(models.SocTarget).filter(models.SocTarget.id == target_id).first() def update_soc_target(db: Session, db_obj: models.SocTarget, obj_in: schemas.SocTargetUpdate, user_id: int) -> models.SocTarget: old_data = {c.name: getattr(db_obj, c.name) for c in db_obj.__table__.columns} update_data = obj_in.dict(exclude_unset=True) assign_internal = update_data.pop("assign_internal", None) assign_external = update_data.pop("assign_external", None) related_ticket_ids = update_data.pop("related_ticket_ids", None) for field, value in update_data.items(): if field in old_data and old_data[field] != value: db.add(models.SocTargetHistory( target_id=db_obj.id, field_name=field, old_value=str(old_data[field]) if old_data[field] is not None else None, new_value=str(value) if value is not None else None, changed_by=user_id )) setattr(db_obj, field, value) db.add(db_obj) # Process related tickets if provided if related_ticket_ids is not None: db.query(models.TicketRelated).filter(models.TicketRelated.soc_target_id == db_obj.id).delete() if related_ticket_ids: r_ids = [r.strip() for r in related_ticket_ids.split(",") if r.strip()] for r_id in r_ids: try: db.add(models.TicketRelated(soc_target_id=db_obj.id, related_soc_target_id=int(r_id))) except ValueError: pass db.commit() # Process assignees if provided if assign_internal is not None or assign_external is not None: # Get existing assignees to preserve if one of the inputs is None existing_assignees = db.query(models.SocTargetAssign).filter(models.SocTargetAssign.target_id == db_obj.id).all() existing_internal = [str(a.user_id) for a in existing_assignees if a.user_id is not None] existing_external = [a.email for a in existing_assignees if a.email is not None] final_internal = assign_internal if assign_internal is not None else ",".join(existing_internal) final_external = assign_external if assign_external is not None else ",".join(existing_external) # Clear existing assignees db.query(models.SocTargetAssign).filter(models.SocTargetAssign.target_id == db_obj.id).delete() if final_internal: uids = [u.strip() for u in final_internal.split(",") if u.strip()] for uid in uids: try: db.add(models.SocTargetAssign(target_id=db_obj.id, user_id=int(uid))) except ValueError: pass if final_external: emails = [e.strip() for e in final_external.split(",") if e.strip()] for email in emails: db.add(models.SocTargetAssign(target_id=db_obj.id, email=email)) db.commit() db.refresh(db_obj) return db_obj def delete_soc_target(db: Session, target_id: int): # 1. Delete physical files and attachment records attachments = db.query(models.SocTargetAttachment).filter(models.SocTargetAttachment.target_id == target_id).all() for att in attachments: if att.file_path and os.path.exists(att.file_path): try: os.remove(att.file_path) except Exception: pass db.query(models.SocTargetAttachment).filter(models.SocTargetAttachment.target_id == target_id).delete(synchronize_session=False) # 2. Delete related records in other tables db.query(models.SocTargetComment).filter(models.SocTargetComment.target_id == target_id).delete(synchronize_session=False) db.query(models.SocTargetHistory).filter(models.SocTargetHistory.target_id == target_id).delete(synchronize_session=False) db.query(models.SocTargetAssign).filter(models.SocTargetAssign.target_id == target_id).delete(synchronize_session=False) # 3. Delete the main ticket record db.query(models.SocTarget).filter(models.SocTarget.id == target_id).delete(synchronize_session=False) db.commit() def list_soc_targets( db: Session, unit_id: int | None = None, status: int | None = None, priority: int | None = None, sla_status: str | None = None, from_date: datetime.datetime | None = None, to_date: datetime.datetime | None = None, q: str | None = None, skip: int = 0, limit: int = 10 ): query = db.query(models.SocTarget) if unit_id is not None: query = query.filter(models.SocTarget.unit_id == unit_id) if status is not None: query = query.filter(models.SocTarget.status == status) if priority is not None: query = query.filter(models.SocTarget.priority == priority) if from_date: query = query.filter(models.SocTarget.created_at >= from_date) if to_date: query = query.filter(models.SocTarget.created_at <= to_date) if q: search_filter = or_( models.SocTarget.name.ilike(f"%{q}%"), models.SocTarget.description.ilike(f"%{q}%"), models.SocTarget.source_ip.ilike(f"%{q}%"), models.SocTarget.destination_ip.ilike(f"%{q}%"), models.SocTarget.id.in_( db.query(models.SocTargetComment.target_id).filter(models.SocTargetComment.content.ilike(f"%{q}%")) ) ) query = query.filter(search_filter) query = query.order_by(desc(models.SocTarget.created_at)) if sla_status: # If filtering by SLA status, we have to fetch more items to filter in memory # This is a trade-off for complex calculated fields. all_items = query.all() filtered_items = [] for item in all_items: completion_time = get_soc_completion_time(db, item.id) if item.status >= 2 else None current_sla = get_sla_status(item.deadline, item.status, completion_time) if current_sla == sla_status: filtered_items.append(item) total = len(filtered_items) items = filtered_items[skip:skip+limit] else: total = query.count() items = query.offset(skip).limit(limit).all() return items, total def get_soc_target_assignees(db: Session, target_id: int) -> List[models.SocTargetAssign]: return db.query(models.SocTargetAssign).filter(models.SocTargetAssign.target_id == target_id).all() def get_soc_target_attachments(db: Session, target_id: int) -> List[models.SocTargetAttachment]: return db.query(models.SocTargetAttachment).filter(models.SocTargetAttachment.target_id == target_id).all() def add_soc_comment(db: Session, target_id: int, content: str, user_id: int | None = None, email: str | None = None): comment = models.SocTargetComment( target_id=target_id, content=content, user_id=user_id, email=email ) db.add(comment) db.commit() db.refresh(comment) return comment def get_soc_comments(db: Session, target_id: int) -> List[models.SocTargetComment]: return db.query(models.SocTargetComment).filter(models.SocTargetComment.target_id == target_id).order_by(models.SocTargetComment.created_at).all() def get_soc_history(db: Session, target_id: int) -> List[models.SocTargetHistory]: return db.query(models.SocTargetHistory).filter(models.SocTargetHistory.target_id == target_id).order_by(desc(models.SocTargetHistory.changed_at)).all() def get_soc_last_updated_by(db: Session, target_id: int) -> Optional[int]: """Lấy ID của người cập nhật cuối cùng từ bảng history.""" last_history = db.query(models.SocTargetHistory).filter( models.SocTargetHistory.target_id == target_id ).order_by(desc(models.SocTargetHistory.changed_at)).first() return last_history.changed_by if last_history else None def is_user_assigned_to_soc(db: Session, target_id: int, user_id: int) -> bool: return db.query(models.SocTargetAssign).filter( models.SocTargetAssign.target_id == target_id, models.SocTargetAssign.user_id == user_id ).first() is not None def get_smartir_stats_by_month_grouped_by_unit( db: Session, month_id: int, unit_id: Optional[int] = None ) -> List[Dict[str, Any]]: q = db.query( models.SmartIR.unit_id, func.coalesce(func.sum(models.SmartIR.total), 0), func.coalesce(func.sum(models.SmartIR.installed), 0), func.coalesce(func.sum(models.SmartIR.new_install), 0), func.coalesce(func.sum(models.SmartIR.ignored), 0), func.coalesce(func.avg(models.SmartIR.rate), 0.0), ).filter(models.SmartIR.month_id == month_id) if unit_id is not None: q = q.filter(models.SmartIR.unit_id == unit_id) rows = q.group_by(models.SmartIR.unit_id).order_by(models.SmartIR.unit_id.asc()).all() data: List[Dict[str, Any]] = [] for uid, total, installed, new_install, ignored, rate in rows: t = int(total or 0) ins = int(installed or 0) newi = int(new_install or 0) ign = int(ignored or 0) data.append({ "unit_id": int(uid), "unit_name": get_unit_name(db, int(uid)) if uid is not None else None, "total": t, "installed": ins, "new_install": newi, "ignored": ign, "rate": round(float(rate or 0.0), 2), }) return data def get_compliance_stats_by_month_grouped_by_unit( db: Session, month_id: int, unit_id: Optional[int] = None ) -> List[Dict[str, Any]]: q = db.query( models.Compliance.unit_id, func.coalesce(func.sum(models.Compliance.windows_key), 0), func.coalesce(func.sum(models.Compliance.office), 0), func.coalesce(func.sum(models.Compliance.ms17010), 0), func.coalesce(func.sum(models.Compliance.firewall), 0), func.coalesce(func.sum(models.Compliance.uac), 0), func.coalesce(func.sum(models.Compliance.winrar), 0), func.coalesce(func.sum(models.Compliance.antivirus), 0), func.coalesce(func.sum(models.Compliance.update_win), 0), ).filter(models.Compliance.month_id == month_id) if unit_id is not None: q = q.filter(models.Compliance.unit_id == unit_id) rows = q.group_by(models.Compliance.unit_id).order_by(models.Compliance.unit_id.asc()).all() data: List[Dict[str, Any]] = [] for ( uid, windows_key, office, ms17010, firewall, uac, winrar, antivirus, update_win, ) in rows: data.append({ "unit_id": int(uid), "unit_name": get_unit_name(db, int(uid)) if uid is not None else None, "windows_key": int(windows_key or 0), "office": int(office or 0), "ms17010": int(ms17010 or 0), "firewall": int(firewall or 0), "uac": int(uac or 0), "winrar": int(winrar or 0), "antivirus": int(antivirus or 0), "update_win": int(update_win or 0), }) return data # Post Comments CRUD def create_post_comment(db: Session, post_id: int, user_id: int, content: str) -> models.PostComment: comment = models.PostComment( post_id=post_id, user_id=user_id, content=content ) db.add(comment) db.commit() db.refresh(comment) return comment def get_post_comment(db: Session, comment_id: int) -> Optional[models.PostComment]: return db.query(models.PostComment).filter(models.PostComment.id == comment_id).first() def update_post_comment(db: Session, comment_id: int, content: str) -> Optional[models.PostComment]: comment = db.query(models.PostComment).filter(models.PostComment.id == comment_id).first() if comment: comment.content = content db.add(comment) db.commit() db.refresh(comment) return comment def delete_post_comment(db: Session, comment_id: int): db.query(models.PostComment).filter(models.PostComment.id == comment_id).delete() db.commit() def list_post_comments(db: Session, post_id: int) -> List[models.PostComment]: return db.query(models.PostComment).filter(models.PostComment.post_id == post_id).order_by(models.PostComment.created_at).all() def get_compliance_heatmap_by_month( db: Session, month_id: int, unit_id: Optional[int] = None ) -> Dict[str, Any]: rows = get_compliance_stats_by_month_grouped_by_unit(db, month_id, unit_id=unit_id) metrics = [ "ms17010", "firewall", "uac", "winrar", "antivirus", "update_win", ] units = [{"unit_id": r["unit_id"], "unit_name": r["unit_name"]} for r in rows] values = [[int(r[m]) for m in metrics] for r in rows] return {"metrics": metrics, "units": units, "values": values} def get_attack_statics_by_month(db: Session, month_id: int) -> Dict[str, Any]: siem_fintech, siem_media, ticket = db.query( func.coalesce(func.sum(models.AttackStatics.siem_fintech), 0), func.coalesce(func.sum(models.AttackStatics.siem_media), 0), func.coalesce(func.sum(models.AttackStatics.ticket), 0), ).filter(models.AttackStatics.month_id == month_id).one() return { "siem_fintech": int(siem_fintech or 0), "siem_media": int(siem_media or 0), "ticket": int(ticket or 0), } def get_root_access_by_month_grouped_by_unit( db: Session, month_id: int, unit_id: Optional[int] = None ) -> List[Dict[str, Any]]: q = db.query( models.RootAccess.unit_id, func.coalesce(func.sum(models.RootAccess.number), 0), ).filter(models.RootAccess.month_id == month_id) if unit_id is not None: q = q.filter(models.RootAccess.unit_id == unit_id) rows = q.group_by(models.RootAccess.unit_id).order_by(models.RootAccess.unit_id.asc()).all() data: List[Dict[str, Any]] = [] for uid, number in rows: data.append({ "unit_id": int(uid), "unit_name": get_unit_name(db, int(uid)) if uid is not None else None, "number": int(number or 0), }) return data # Ticket def create_ticket(db: Session, t_in: schemas.TicketCreate) -> models.Ticket: db_obj = models.Ticket(unit_id=t_in.unit_id, month_id=t_in.month_id, open_count=t_in.open_count, in_process=t_in.in_process, completed=t_in.completed) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj # Mail & uploads def create_mail_history(db: Session, m_in: schemas.MailHistoryCreate) -> models.MailHistory: db_obj = models.MailHistory( post_id=m_in.post_id, subject=m_in.subject, role_id=m_in.role_id, content=m_in.content, status=m_in.status, ) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj # def list_mail_histories( # db: Session, # skip: int = 0, # limit: int = 100, # role_id: Optional[int] = None, # post_id: Optional[int] = None, # subject: Optional[str] = None, # ) -> List[models.MailHistory]: # q = db.query(models.MailHistory) # if role_id is not None: # q = q.filter(models.MailHistory.role_id == role_id) # if post_id is not None: # q = q.filter(models.MailHistory.post_id == post_id) # if subject is not None: # q = q.filter(models.MailHistory.subject == subject) # return q.order_by(models.MailHistory.id.desc()).offset(skip).limit(limit).all() def get_mail_history(db: Session, mh_id: int) -> Optional[models.MailHistory]: return db.query(models.MailHistory).filter(models.MailHistory.id == mh_id).first() def list_mail_histories( db: Session, skip: int = 0, limit: int = 100, role_id: Optional[int] = None, post_id: Optional[int] = None, subject: Optional[str] = None, ): q = db.query( models.MailHistory.id, models.MailHistory.subject, models.MailHistory.role_id, models.MailHistory.post_id, models.MailHistory.status, models.MailHistory.created_at, ) if role_id is not None: q = q.filter(models.MailHistory.role_id == role_id) if post_id is not None: q = q.filter(models.MailHistory.post_id == post_id) if subject is not None: q = q.filter(models.MailHistory.subject == subject) return ( q.order_by(desc(models.MailHistory.id)) .offset(skip) .limit(limit) .all() ) def update_mail_history(db: Session, mh_id: int, m_in: schemas.MailHistoryUpdate) -> Optional[models.MailHistory]: db_obj = db.query(models.MailHistory).filter(models.MailHistory.id == mh_id).first() if not db_obj: return None if m_in.post_id is not None: db_obj.post_id = m_in.post_id if m_in.subject is not None: db_obj.subject = m_in.subject if m_in.role_id is not None: db_obj.role_id = m_in.role_id if m_in.content is not None: db_obj.content = m_in.content if m_in.status is not None: db_obj.status = m_in.status db.commit() db.refresh(db_obj) return db_obj def delete_mail_history(db: Session, mh_id: int) -> bool: db_obj = db.query(models.MailHistory).filter(models.MailHistory.id == mh_id).first() if not db_obj: return False db.delete(db_obj) db.commit() return True def create_file_upload(db: Session, f_in: schemas.FileUploadCreate) -> models.FileUpload: db_obj = models.FileUpload(path=f_in.path, table_name=f_in.table_name, table_id=f_in.table_id) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def create_import_history(db: Session, obj_in: schemas.ImportHistoryCreate) -> models.ImportHistory: db_obj = models.ImportHistory(type=obj_in.type, file_path=obj_in.file_path, month_id=obj_in.month_id) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def list_import_history( db: Session, type: Optional[str] = None, month_id: Optional[int] = None, skip: int = 0, limit: int = 100, ) -> list[models.ImportHistory]: q = db.query(models.ImportHistory) if type is not None: q = q.filter(models.ImportHistory.type == type) if month_id is not None: q = q.filter(models.ImportHistory.month_id == month_id) return q.order_by(models.ImportHistory.id.desc()).offset(skip).limit(limit).all() def delete_user(db: Session, user_id: int) -> bool: db_obj = db.query(models.User).filter(models.User.id == user_id).first() if not db_obj: return False db.delete(db_obj) db.commit() return True def get_post(db: Session, post_id: int) -> Optional[models.Post]: return db.query(models.Post).filter(models.Post.id == post_id).first() def update_post(db: Session, post_id: int, post_in: schemas.PostUpdate) -> Optional[models.Post]: db_obj = db.query(models.Post).filter(models.Post.id == post_id).first() if not db_obj: return None if post_in.content is not None: db_obj.content = post_in.content if post_in.title is not None: db_obj.title = post_in.title if post_in.created_by is not None: db_obj.created_by = post_in.created_by if post_in.category_id is not None: db_obj.category_id = post_in.category_id if post_in.status is not None: db_obj.status = post_in.status if post_in.thumbnail is not None: db_obj.thumbnail = post_in.thumbnail db.commit() db.refresh(db_obj) return db_obj def delete_post(db: Session, post_id: int) -> bool: db_obj = db.query(models.Post).filter(models.Post.id == post_id).first() if not db_obj: return False db.delete(db_obj) db.commit() return True def delete_file_upload_by_path(db: Session, path: str) -> bool: obj = db.query(models.FileUpload).filter(models.FileUpload.path == path).first() if not obj: return False db.delete(obj) db.commit() return True def delete_file_upload_by_filename(db: Session, filename: str) -> bool: obj = db.query(models.FileUpload).filter(models.FileUpload.path == filename).first() if not obj: return False db.delete(obj) db.commit() return True def create_document(db: Session, obj_in: schemas.DocumentCreate) -> models.Document: db_obj = models.Document( name=obj_in.name, number_doc=obj_in.number_doc, sign_date=obj_in.sign_date, level=obj_in.level, validate_date=obj_in.validate_date, file_path=obj_in.file_path, ) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def get_document(db: Session, id: int) -> models.Document | None: return db.query(models.Document).filter(models.Document.id == id).first() def update_document(db: Session, id: int, obj_in: schemas.DocumentUpdate) -> models.Document | None: db_obj = get_document(db, id) if not db_obj: return None data = obj_in.dict(exclude_unset=True) for k, v in data.items(): setattr(db_obj, k, v) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def delete_document(db: Session, id: int) -> bool: db_obj = get_document(db, id) if not db_obj: return False db.delete(db_obj) db.commit() return True def list_documents( db: Session, skip: int = 0, limit: int = 100, ) -> list[models.Document]: return ( db.query(models.Document) .order_by(models.Document.id.desc()) .offset(skip) .limit(limit) .all() ) def list_file_filenames(db: Session, table_name: str, table_id: int) -> List[str]: q = db.query(models.FileUpload).filter( models.FileUpload.table_name == table_name, models.FileUpload.table_id == table_id ).all() return [f.path for f in q] def create_mm_fintech(db: Session, obj_in: schemas.MMFintechCreate) -> models.MMFintech: db_obj = models.MMFintech( month_id=obj_in.month_id, unit_id=obj_in.unit_id, file_path=obj_in.file_path, ) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def search_documents(db: Session, keyword: str, skip: int = 0, limit: int = 100) -> list[models.Document]: pattern = f"%{keyword}%" return ( db.query(models.Document) .filter( or_( models.Document.name.ilike(pattern), models.Document.number_doc.ilike(pattern), ) ) .order_by(models.Document.id.desc()) .offset(skip) .limit(limit) .all() ) def list_mm_fintech( db: Session, month_id: int, unit_id: int | None = None, skip: int = 0, limit: int | None = 100, ) -> List[models.MMFintech]: q = db.query(models.MMFintech).filter(models.MMFintech.month_id == month_id) if unit_id is not None: q = q.filter(models.MMFintech.unit_id == unit_id) q = q.order_by(models.MMFintech.id.desc()).offset(skip) if limit is not None: q = q.limit(limit) return q.all() def get_mm_fintech(db: Session, item_id: int) -> models.MMFintech | None: return db.query(models.MMFintech).filter(models.MMFintech.id == item_id).first() def get_mm_fintech_latest_by_month(db: Session, month_id: int) -> models.MMFintech | None: return ( db.query(models.MMFintech) .filter(models.MMFintech.month_id == month_id) .order_by(models.MMFintech.id.desc()) .first() ) def delete_mm_fintech(db: Session, item_id: int) -> bool: obj = get_mm_fintech(db, item_id) if not obj: return False db.delete(obj) db.commit() return True def create_convert_hardening(db: Session, obj_in: schemas.ConvertHardeningCreate) -> models.ConvertHardening: db_obj = models.ConvertHardening(filename=obj_in.filename, user_id=obj_in.user_id) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def list_convert_hardening_by_user(db: Session, user_id: int, skip: int = 0, limit: int = 100) -> list[models.ConvertHardening]: return ( db.query(models.ConvertHardening) .filter(models.ConvertHardening.user_id == user_id) .order_by(models.ConvertHardening.id.desc()) .offset(skip) .limit(limit) .all() ) def get_convert_hardening(db: Session, item_id: int) -> models.ConvertHardening | None: return db.query(models.ConvertHardening).filter(models.ConvertHardening.id == item_id).first() # -------- SystemGroup CRUD -------- def create_system_group(db: Session, obj_in: schemas.SystemGroupCreate) -> models.SystemGroup: db_obj = models.SystemGroup(**obj_in.dict()) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def list_system_groups(db: Session, name: str | None = None, skip: int = 0, limit: int = 100) -> list[models.SystemGroup]: sg = models.SystemGroup sys = models.System query = ( db.query(sg, func.count(sys.id).label("count")) .outerjoin(sys, sg.id == sys.system_group_id) ) if name: query = query.filter(sg.name.ilike(f"%{name}%")) rows = ( query.group_by(sg.id) .order_by(sg.id.desc()) .offset(skip) .limit(limit) .all() ) items: list[models.SystemGroup] = [] for obj, cnt in rows: setattr(obj, "count", int(cnt or 0)) items.append(obj) return items def get_system_group(db: Session, id: int) -> models.SystemGroup | None: return db.query(models.SystemGroup).filter(models.SystemGroup.id == id).first() def update_system_group(db: Session, id: int, obj_in: schemas.SystemGroupUpdate) -> models.SystemGroup | None: db_obj = get_system_group(db, id) if not db_obj: return None data = obj_in.dict(exclude_unset=True) for k, v in data.items(): setattr(db_obj, k, v) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def delete_system_group(db: Session, id: int) -> bool: db_obj = get_system_group(db, id) if not db_obj: return False db.delete(db_obj) db.commit() return True # -------- System CRUD -------- def create_system(db: Session, obj_in: schemas.SystemCreate) -> models.System: db_obj = models.System(**obj_in.dict()) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def list_systems( db: Session, name: str | None = None, url_ip: str | None = None, unit_id: int | None = None, system_group_id: int | None = None, status: int | None = None, skip: int = 0, limit: int = 100, ) -> list[models.System]: query = db.query(models.System) if name: query = query.filter(models.System.name.ilike(f"%{name}%")) if url_ip: query = query.filter(models.System.url_ip.ilike(f"%{url_ip}%")) if unit_id is not None: query = query.filter(models.System.unit_id == unit_id) if system_group_id is not None: query = query.filter(models.System.system_group_id == system_group_id) if status is not None: query = query.filter(models.System.status == status) return query.order_by(models.System.id.desc()).offset(skip).limit(limit).all() def get_system(db: Session, id: int) -> models.System | None: return db.query(models.System).filter(models.System.id == id).first() def update_system(db: Session, id: int, obj_in: schemas.SystemUpdate) -> models.System | None: db_obj = get_system(db, id) if not db_obj: return None data = obj_in.dict(exclude_unset=True) for k, v in data.items(): setattr(db_obj, k, v) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def delete_system(db: Session, id: int) -> bool: db_obj = get_system(db, id) if not db_obj: return False db.delete(db_obj) db.commit() return True def search_systems_by_url_ip(db: Session, url_ip: str, skip: int = 0, limit: int = 100) -> list[models.System]: return ( db.query(models.System) .filter(models.System.url_ip.ilike(f"%{url_ip}%")) .order_by(models.System.id.desc()) .offset(skip) .limit(limit) .all() ) # -------- ManageSystem CRUD -------- def get_manage_system(db: Session, id: int) -> models.ManageSystem | None: return db.query(models.ManageSystem).filter(models.ManageSystem.id == id).first() def get_manage_system_by_name(db: Session, name: str) -> models.ManageSystem | None: return db.query(models.ManageSystem).filter(models.ManageSystem.name == name).first() def create_manage_system(db: Session, obj_in: schemas.ManageSystemCreate) -> models.ManageSystem: db_obj = models.ManageSystem(**obj_in.dict()) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def update_manage_system(db: Session, id: int, obj_in: schemas.ManageSystemUpdate) -> models.ManageSystem | None: db_obj = get_manage_system(db, id) if not db_obj: return None data = obj_in.dict(exclude_unset=True) for k, v in data.items(): setattr(db_obj, k, v) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def delete_manage_system(db: Session, id: int) -> bool: db_obj = get_manage_system(db, id) if not db_obj: return False db.delete(db_obj) db.commit() return True def list_manage_systems( db: Session, unit_id: int | None = None, parent_id: int | None = None, level: int | None = None, q: str | None = None, skip: int = 0, limit: int = 100, ) -> list[models.ManageSystem]: query = db.query(models.ManageSystem) if unit_id is not None: query = query.filter(models.ManageSystem.unit_id == unit_id) if parent_id is not None: query = query.filter(models.ManageSystem.parent_id == parent_id) if level is not None: query = query.filter(models.ManageSystem.level == level) if q: like = f"%{q}%" query = query.filter(or_(models.ManageSystem.name.ilike(like), models.ManageSystem.url_ip.ilike(like))) return query.order_by(models.ManageSystem.id.desc()).offset(skip).limit(limit).all() def create_log_source(db: Session, obj_in: schemas.LogSourceCreate) -> models.LogSource: db_obj = models.LogSource( month_id=obj_in.month_id, unit_id=obj_in.unit_id, log_id=obj_in.log_id, name=obj_in.name, description=obj_in.description, is_enabled=obj_in.is_enabled if obj_in.is_enabled is not None else True, source_type_id=obj_in.source_type_id, status=obj_in.status, # lưu số message=obj_in.message, other=obj_in.other, file_id=obj_in.file_id, created_at=obj_in.created_at if getattr(obj_in, "created_at", None) is not None else None, ) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def list_log_sources_by_month( db: Session, month_id: int, unit_id: int | None = None, is_enabled: bool | None = None, status: int | None = None, file_id: int | None = None, skip: int = 0, limit: int = 100, ) -> list[models.LogSource]: q = db.query(models.LogSource).filter(models.LogSource.month_id == month_id) if unit_id is not None: q = q.filter(models.LogSource.unit_id == unit_id) if is_enabled is not None: q = q.filter(models.LogSource.is_enabled == is_enabled) if status is not None: try: q = q.filter(models.LogSource.status == int(status)) except Exception: pass if file_id is not None: q = q.filter(models.LogSource.file_id == file_id) return q.order_by(models.LogSource.id.desc()).offset(skip).limit(limit).all() def get_log_source_filenames_by_month(db: Session, month_id: int) -> List[Dict[str, Any]]: # Get all unique file_ids for logsource in this month file_ids = db.query(models.LogSource.file_id).filter( models.LogSource.month_id == month_id, models.LogSource.file_id.isnot(None) ).distinct().all() ids = [f[0] for f in file_ids] if not ids: return [] # Join with ImportHistory to get file paths/names histories = db.query(models.ImportHistory).filter(models.ImportHistory.id.in_(ids)).all() return [{"file_id": h.id, "filename": h.file_path, "created_at": h.created_at} for h in histories] def delete_log_sources_by_file_id(db: Session, file_id: int) -> int: # Get all logsource IDs belonging to this file_id ls_ids = [r[0] for r in db.query(models.LogSource.id).filter(models.LogSource.file_id == file_id).all()] # Delete all comments associated with these logsource IDs if ls_ids: db.query(models.LogSourceComment).filter(models.LogSourceComment.logsource_id.in_(ls_ids)).delete(synchronize_session=False) # Delete the logsource records cnt = db.query(models.LogSource).filter(models.LogSource.file_id == file_id).delete(synchronize_session=False) # Delete from import_history db.query(models.ImportHistory).filter(models.ImportHistory.id == file_id).delete(synchronize_session=False) db.commit() return int(cnt or 0) def add_logsource_comment(db: Session, logsource_id: int, user_id: int, comment_text: str) -> models.LogSourceComment: db_obj = models.LogSourceComment( logsource_id=logsource_id, user_id=user_id, comment=comment_text ) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def get_logsource_comments(db: Session, logsource_id: int) -> List[models.LogSourceComment]: return db.query(models.LogSourceComment).filter( models.LogSourceComment.logsource_id == logsource_id ).order_by(models.LogSourceComment.created_at.asc()).all() def get_log_source_stats_by_month( db: Session, month_id: int, unit_id: int | None = None, is_enabled: bool | None = None, ) -> Dict[str, Any]: q = db.query(models.LogSource).filter(models.LogSource.month_id == month_id) if unit_id is not None: q = q.filter(models.LogSource.unit_id == unit_id) if is_enabled is not None: q = q.filter(models.LogSource.is_enabled == is_enabled) total = q.count() q_enabled = db.query(func.count(models.LogSource.id)).filter(models.LogSource.month_id == month_id, models.LogSource.is_enabled == True) q_disabled = db.query(func.count(models.LogSource.id)).filter(models.LogSource.month_id == month_id, models.LogSource.is_enabled == False) if unit_id is not None: q_enabled = q_enabled.filter(models.LogSource.unit_id == unit_id) q_disabled = q_disabled.filter(models.LogSource.unit_id == unit_id) enabled_count = q_enabled.scalar() or 0 disabled_count = q_disabled.scalar() or 0 status_rows = db.query(models.LogSource.status, func.count(models.LogSource.id)).filter(models.LogSource.month_id == month_id) if unit_id is not None: status_rows = status_rows.filter(models.LogSource.unit_id == unit_id) if is_enabled is not None: status_rows = status_rows.filter(models.LogSource.is_enabled == is_enabled) status_rows = status_rows.group_by(models.LogSource.status).all() by_status: Dict[int, int] = {int(s or 0): int(c or 0) for s, c in status_rows} return { "total": int(total or 0), "enabled": int(enabled_count or 0), "disabled": int(disabled_count or 0), "by_status": by_status, } def get_log_source_status_by_month_grouped_by_unit( db: Session, month_id: int, ) -> List[Dict[str, Any]]: rows = ( db.query(models.LogSource.unit_id, models.LogSource.status, func.count(models.LogSource.id)) .filter(models.LogSource.month_id == month_id) .group_by(models.LogSource.unit_id, models.LogSource.status) .all() ) acc: Dict[int, Dict[int, int]] = {} for uid, status, cnt in rows: u = int(uid) if uid is not None else None s = int(status or 0) c = int(cnt or 0) if u is None: continue if u not in acc: acc[u] = {} acc[u][s] = acc[u].get(s, 0) + c result: List[Dict[str, Any]] = [] for uid, stmap in acc.items(): result.append({ "unit_id": uid, "unit_name": get_unit_name(db, uid), "by_status": { 1: int(stmap.get(1, 0)), 2: int(stmap.get(2, 0)), 3: int(stmap.get(3, 0)), 4: int(stmap.get(4, 0)), }, }) return result def delete_log_sources_by_month(db: Session, month_id: int) -> int: # Get all logsource IDs belonging to this month_id ls_ids = [r[0] for r in db.query(models.LogSource.id).filter(models.LogSource.month_id == month_id).all()] # Delete all comments associated with these logsource IDs if ls_ids: db.query(models.LogSourceComment).filter(models.LogSourceComment.logsource_id.in_(ls_ids)).delete(synchronize_session=False) # Delete the logsource records cnt = db.query(models.LogSource).filter(models.LogSource.month_id == month_id).delete(synchronize_session=False) # Delete from import_history (only type logsource) db.query(models.ImportHistory).filter(models.ImportHistory.month_id == month_id).filter(models.ImportHistory.type == "logsource").delete(synchronize_session=False) db.commit() return int(cnt or 0) def create_smartir_detail(db: Session, obj_in: schemas.SmartIRDetailCreate) -> models.SmartIRDetail: db_obj = models.SmartIRDetail( month_id=obj_in.month_id, unit_id=obj_in.unit_id, vnpt_ma_nhan_vien=obj_in.vnpt_ma_nhan_vien, name=obj_in.name, email=obj_in.email, phong_ban=obj_in.phong_ban, ip=obj_in.ip, pc_name=obj_in.pc_name, mac=obj_in.mac, agent_version=obj_in.agent_version, last_online=obj_in.last_online, status=obj_in.status, ) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def list_smartir_detail_by_month( db: Session, month_id: int, unit_id: Optional[int] = None, skip: int = 0, limit: int = 100, ) -> List[models.SmartIRDetail]: q = db.query(models.SmartIRDetail).filter(models.SmartIRDetail.month_id == month_id) if unit_id is not None: q = q.filter(models.SmartIRDetail.unit_id == unit_id) return q.order_by(models.SmartIRDetail.id.desc()).offset(skip).limit(limit).all() def upsert_smartir_by_month_unit( db: Session, month_id: int, unit_id: int, total: int, installed: int, new_install: int, ignored: int, rate: float, created_at: Optional[datetime.datetime] = None, ) -> models.SmartIR: existing = ( db.query(models.SmartIR) .filter(models.SmartIR.month_id == month_id) .filter(models.SmartIR.unit_id == unit_id) .first() ) if existing: existing.total = int(total or 0) existing.installed = int(installed or 0) existing.new_install = int(new_install or 0) existing.ignored = int(ignored or 0) existing.rate = float(rate or 0.0) if created_at: existing.created_at = created_at db.add(existing) db.commit() db.refresh(existing) return existing db_obj = models.SmartIR( month_id=month_id, unit_id=unit_id, total=int(total or 0), installed=int(installed or 0), new_install=int(new_install or 0), ignored=int(ignored or 0), rate=float(rate or 0.0), created_at=created_at or datetime.datetime.utcnow(), ) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def upsert_nac_by_month_unit( db: Session, month_id: int, unit_id: int, total: int, installed: int, ignored: int, rate: float, created_at: Optional[datetime.datetime] = None, ) -> models.NAC: existing = ( db.query(models.NAC) .filter(models.NAC.month_id == month_id) .filter(models.NAC.unit_id == unit_id) .first() ) if existing: existing.total = int(total or 0) existing.installed = int(installed or 0) existing.ignored = int(ignored or 0) existing.rate = float(rate or 0.0) if created_at: existing.created_at = created_at db.add(existing) db.commit() db.refresh(existing) return existing db_obj = models.NAC( month_id=month_id, unit_id=unit_id, total=int(total or 0), installed=int(installed or 0), ignored=int(ignored or 0), rate=float(rate or 0.0), created_at=created_at or datetime.datetime.utcnow(), ) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def upsert_compliance_by_month_unit( db: Session, month_id: int, unit_id: int, windows_key: int, office: int, ms17010: int, firewall: int, uac: int, winrar: int, antivirus: int, update_win: int, created_at: Optional[datetime.datetime] = None, ) -> models.Compliance: existing = ( db.query(models.Compliance) .filter(models.Compliance.month_id == month_id) .filter(models.Compliance.unit_id == unit_id) .first() ) if existing: existing.windows_key = int(windows_key or 0) existing.office = int(office or 0) existing.ms17010 = int(ms17010 or 0) existing.firewall = int(firewall or 0) existing.uac = int(uac or 0) existing.winrar = int(winrar or 0) existing.antivirus = int(antivirus or 0) existing.update_win = int(update_win or 0) if created_at: existing.created_at = created_at db.add(existing) db.commit() db.refresh(existing) return existing db_obj = models.Compliance( month_id=month_id, unit_id=unit_id, windows_key=int(windows_key or 0), office=int(office or 0), ms17010=int(ms17010 or 0), firewall=int(firewall or 0), uac=int(uac or 0), winrar=int(winrar or 0), antivirus=int(antivirus or 0), update_win=int(update_win or 0), created_at=created_at or datetime.datetime.utcnow(), ) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def upsert_attack_statics_by_month( db: Session, month_id: int, siem_fintech: int, siem_media: int, ticket: int, created_at: Optional[datetime.datetime] = None, ) -> models.AttackStatics: existing = ( db.query(models.AttackStatics) .filter(models.AttackStatics.month_id == month_id) .first() ) if existing: existing.siem_fintech = int(siem_fintech or 0) existing.siem_media = int(siem_media or 0) existing.ticket = int(ticket or 0) if created_at: existing.created_at = created_at db.add(existing) db.commit() db.refresh(existing) return existing db_obj = models.AttackStatics( month_id=month_id, siem_fintech=int(siem_fintech or 0), siem_media=int(siem_media or 0), ticket=int(ticket or 0), created_at=created_at or datetime.datetime.utcnow(), ) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def upsert_root_access_by_month_unit( db: Session, month_id: int, unit_id: int, number: int, created_at: Optional[datetime.datetime] = None, ) -> models.RootAccess: existing = ( db.query(models.RootAccess) .filter(models.RootAccess.month_id == month_id) .filter(models.RootAccess.unit_id == unit_id) .first() ) if existing: existing.number = int(number or 0) if created_at: existing.created_at = created_at db.add(existing) db.commit() db.refresh(existing) return existing db_obj = models.RootAccess( month_id=month_id, unit_id=unit_id, number=int(number or 0), created_at=created_at or datetime.datetime.utcnow(), ) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def delete_smartir_detail_by_month_unit(db: Session, month_id: int, unit_id: int) -> int: cnt = ( db.query(models.SmartIRDetail) .filter(models.SmartIRDetail.month_id == month_id) .filter(models.SmartIRDetail.unit_id == unit_id) .delete(synchronize_session=False) ) db.commit() return int(cnt or 0) def create_nac_detail(db: Session, obj_in: schemas.NACDetailCreate) -> models.NACDetail: db_obj = models.NACDetail( month_id=obj_in.month_id, unit_id=obj_in.unit_id, name=obj_in.name, email=obj_in.email, status=obj_in.status, ) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def list_nac_detail_by_month( db: Session, month_id: int, unit_id: Optional[int] = None, skip: int = 0, limit: int = 100, ) -> List[models.NACDetail]: q = db.query(models.NACDetail).filter(models.NACDetail.month_id == month_id) if unit_id is not None: q = q.filter(models.NACDetail.unit_id == unit_id) return q.order_by(models.NACDetail.id.desc()).offset(skip).limit(limit).all() def upsert_compliance_smartir(db: Session, obj_in: schemas.ComplianceSmartIRCreate) -> models.ComplianceSmartIR: existing = ( db.query(models.ComplianceSmartIR) .filter(models.ComplianceSmartIR.vnpt_ma_nhan_vien == obj_in.vnpt_ma_nhan_vien) .filter(models.ComplianceSmartIR.month_id == obj_in.month_id) .first() ) if existing: for k, v in obj_in.model_dump().items(): setattr(existing, k, v) db.add(existing) db.commit() db.refresh(existing) return existing db_obj = models.ComplianceSmartIR(**obj_in.model_dump()) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def list_compliance_smartir( db: Session, status: Optional[int] = None, unit_id: Optional[int] = None, month_id: Optional[int] = None, skip: int = 0, limit: int = 100, ) -> List[models.ComplianceSmartIR]: q = db.query(models.ComplianceSmartIR) if status is not None: q = q.filter(models.ComplianceSmartIR.status == status) if unit_id is not None: q = q.filter(models.ComplianceSmartIR.unit_id == unit_id) if month_id is not None: q = q.filter(models.ComplianceSmartIR.month_id == month_id) return q.order_by(models.ComplianceSmartIR.id.desc()).offset(skip).limit(limit).all() def create_compliance_detail(db: Session, obj_in: schemas.ComplianceDetailCreate) -> models.ComplianceDetail: data: Dict[str, Any] = { "month_id": obj_in.month_id, "unit_id": obj_in.unit_id, "vnpt_ma_nhan_vien": obj_in.vnpt_ma_nhan_vien, "name": obj_in.name, "email": obj_in.email, "phong_ban": obj_in.phong_ban, "windows_key": obj_in.windows_key, "office": obj_in.office, "ms17010": obj_in.ms17010, "firewall": obj_in.firewall, "uac": obj_in.uac, "winrar": obj_in.winrar, "antivirus": obj_in.antivirus, "update_win": obj_in.update_win, "status": obj_in.status, } clean = {k: v for k, v in data.items() if v is not None} db_obj = models.ComplianceDetail(**clean) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def list_compliance_detail_by_month( db: Session, month_id: int, unit_id: Optional[int] = None, status: Optional[int] = None, skip: int = 0, limit: int = 100, ) -> List[models.ComplianceDetail]: q = db.query(models.ComplianceDetail).filter(models.ComplianceDetail.month_id == month_id) if unit_id is not None: q = q.filter(models.ComplianceDetail.unit_id == unit_id) if status is not None: q = q.filter(models.ComplianceDetail.status == status) return q.order_by(models.ComplianceDetail.id.asc()).offset(skip).limit(limit).all() def create_security_index(db: Session, obj_in: schemas.SecurityIndexCreate) -> models.SecurityIndex: db_obj = models.SecurityIndex(**obj_in.dict()) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def list_security_index_by_month(db: Session, month_id: int) -> List[models.SecurityIndex]: return db.query(models.SecurityIndex).filter(models.SecurityIndex.month_id == month_id).all() def delete_security_index_by_month(db: Session, month_id: int): db.query(models.SecurityIndex).filter(models.SecurityIndex.month_id == month_id).delete(synchronize_session=False) db.commit()