1
0
mirror of https://gitlab.com/MoonTestUse1/AdministrationItDepartmens.git synced 2025-08-14 00:25:46 +02:00

Все подряд

This commit is contained in:
MoonTestUse1
2024-12-31 02:37:57 +06:00
parent 8e53bb6cb2
commit d5780b2eab
3258 changed files with 1087440 additions and 268 deletions

View File

@@ -1,9 +1,17 @@
"""Bot-specific configuration"""
from ..config import settings
import os
from dotenv import load_dotenv
load_dotenv()
BOT_TOKEN = settings.bot_token
CHAT_ID = settings.chat_id
# Telegram API credentials
API_ID = os.getenv('TELEGRAM_API_ID')
API_HASH = os.getenv('TELEGRAM_API_HASH')
# Request status constants
class RequestStatus:
"""Constants for request statuses"""

View File

@@ -1,45 +1,45 @@
"""Employee management database operations"""
from sqlalchemy.orm import Session
from ..models import employee as models
from ..schemas import tables
from ..utils.auth import get_password_hash
from ..utils.loggers import auth_logger
def get_employee(db: Session, employee_id: int):
"""Get employee by ID"""
return db.query(tables.Employee).filter(tables.Employee.id == employee_id).first()
def get_employee_by_lastname(db: Session, last_name: str):
"""Get employee by last name"""
return (
db.query(tables.Employee).filter(tables.Employee.last_name == last_name).first()
db.query(tables.Employee)
.filter(tables.Employee.last_name == last_name)
.first()
)
def get_employees(db: Session, skip: int = 0, limit: int = 100):
return db.query(tables.Employee).offset(skip).limit(limit).all()
def create_employee(db: Session, employee: models.EmployeeCreate):
hashed_password = get_password_hash(employee.password)
db_employee = tables.Employee(
first_name=employee.first_name,
last_name=employee.last_name,
department=employee.department,
office=employee.office,
password=hashed_password,
)
db.add(db_employee)
db.commit()
db.refresh(db_employee)
return db_employee
def update_employee(db: Session, employee_id: int, data: dict):
db_employee = get_employee(db, employee_id)
if db_employee:
for key, value in data.items():
if key == "password":
value = get_password_hash(value)
setattr(db_employee, key, value)
"""Create new employee"""
try:
hashed_password = get_password_hash(employee.password)
db_employee = tables.Employee(
first_name=employee.first_name,
last_name=employee.last_name,
department=employee.department,
office=employee.office,
password=hashed_password,
)
db.add(db_employee)
db.commit()
db.refresh(db_employee)
return db_employee
auth_logger.info(
"Employee created",
extra={"employee_id": db_employee.id}
)
return db_employee
except Exception as e:
db.rollback()
auth_logger.error(f"Error creating employee: {e}", exc_info=True)
raise

View File

@@ -1,119 +1,35 @@
"""Request management database operations"""
from sqlalchemy.orm import Session
from ..models import request as models
from ..schemas import tables
from sqlalchemy import select
from sqlalchemy.orm import Session
from ..schemas import tables
from datetime import datetime
from ..utils.loggers import request_logger
def create_request(db: Session, request: models.RequestCreate):
db_request = tables.Request(
employee_id=request.employee_id,
department=request.department,
request_type=request.request_type,
priority=request.priority,
description=request.description,
status="new",
)
db.add(db_request)
db.commit()
db.refresh(db_request)
return db_request
def get_requests(db: Session, skip: int = 0, limit: int = 100):
requests = (
db.query(
tables.Request,
tables.Employee.last_name.label("employee_last_name"),
tables.Employee.first_name.label("employee_first_name"),
)
.join(tables.Employee)
.offset(skip)
.limit(limit)
.all()
)
return [
{
"id": req[0].id,
"employee_id": req[0].employee_id,
"department": req[0].department,
"request_type": req[0].request_type,
"priority": req[0].priority,
"status": req[0].status,
"description": req[0].description,
"created_at": req[0].created_at,
"employee_last_name": req[1],
"employee_first_name": req[2],
}
for req in requests
]
def get_requests_by_employee_lastname(db: Session, last_name: str):
requests = (
db.query(
tables.Request,
tables.Employee.last_name.label("employee_last_name"),
tables.Employee.first_name.label("employee_first_name"),
)
.join(tables.Employee)
.filter(tables.Employee.last_name.ilike(f"%{last_name}%"))
.all()
)
return [
{
"id": req[0].id,
"employee_id": req[0].employee_id,
"department": req[0].department,
"request_type": req[0].request_type,
"priority": req[0].priority,
"status": req[0].status,
"description": req[0].description,
"created_at": req[0].created_at,
"employee_last_name": req[1],
"employee_first_name": req[2],
}
for req in requests
]
def update_request_status(
db: Session, request_id: int, new_status: models.RequestStatus
):
"""Create new request"""
try:
db_request = (
db.query(tables.Request).filter(tables.Request.id == request_id).first()
db_request = tables.Request(
employee_id=request.employee_id,
department=request.department,
request_type=request.request_type,
priority=request.priority,
description=request.description,
status="new",
)
if not db_request:
return None
# Define valid status transitions
valid_transitions = {
models.RequestStatus.NEW: [models.RequestStatus.IN_PROGRESS],
models.RequestStatus.IN_PROGRESS: [models.RequestStatus.RESOLVED],
models.RequestStatus.RESOLVED: [models.RequestStatus.CLOSED],
models.RequestStatus.CLOSED: [],
}
current_status = models.RequestStatus(db_request.status)
if new_status not in valid_transitions[current_status]:
raise ValueError(
f"Invalid status transition from {current_status} to {new_status}"
)
db_request.status = new_status
db.add(db_request)
db.commit()
db.refresh(db_request)
request_logger.info(
"Request created",
extra={"request_id": db_request.id}
)
return db_request
except Exception as e:
db.rollback()
raise e
request_logger.error(f"Error creating request: {e}", exc_info=True)
raise
def get_request_details(db: Session, request_id: int):
"""Get detailed request information including employee details"""
@@ -129,6 +45,7 @@ def get_request_details(db: Session, request_id: int):
return {
"id": request.id,
"employee_id": request.employee_id,
"employee_last_name": request.employee.last_name,
"employee_first_name": request.employee.first_name,
"department": request.department,
@@ -137,71 +54,5 @@ def get_request_details(db: Session, request_id: int):
"priority": request.priority,
"description": request.description,
"status": request.status,
"created_at": request.created_at.isoformat(),
}
from sqlalchemy.orm import Session
from ..schemas import tables
from datetime import datetime
def get_request_details(db: Session, request_id: int):
"""Get detailed request information including employee details"""
request = (
db.query(tables.Request)
.join(tables.Employee)
.filter(tables.Request.id == request_id)
.first()
)
if not request:
return None
return {
"id": request.id,
"employee_last_name": request.employee.last_name,
"employee_first_name": request.employee.first_name,
"department": request.department,
"office": request.employee.office,
"request_type": request.request_type,
"priority": request.priority,
"description": request.description,
"status": request.status,
"created_at": request.created_at.isoformat(),
}
def update_request_status(db: Session, request_id: int, new_status: str):
"""Update request status with validation"""
try:
# Define valid status transitions
valid_transitions = {
"new": ["in_progress"],
"in_progress": ["resolved"],
"resolved": ["closed"],
"closed": [],
}
db_request = (
db.query(tables.Request).filter(tables.Request.id == request_id).first()
)
if not db_request:
return None
current_status = db_request.status
if new_status not in valid_transitions.get(current_status, []):
raise ValueError(
f"Invalid status transition from {current_status} to {new_status}"
)
db_request.status = new_status
db.commit()
db.refresh(db_request)
# Get full request details after update
return get_request_details(db, request_id)
except Exception as e:
db.rollback()
raise e
"created_at": request.created_at.isoformat()
}

View File

@@ -1,4 +1,5 @@
"""Logging configuration for the application"""
import sys
logging_config = {
"version": 1,
@@ -7,10 +8,6 @@ logging_config = {
"default": {
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S"
},
"access": {
"format": "%(asctime)s - %(name)s - %(levelname)s - %(client_addr)s - %(request_line)s - %(status_code)s",
"datefmt": "%Y-%m-%d %H:%M:%S"
}
},
"handlers": {
@@ -18,37 +15,21 @@ logging_config = {
"class": "logging.StreamHandler",
"level": "INFO",
"formatter": "default",
"stream": "ext://sys.stdout"
},
"file": {
"class": "logging.handlers.RotatingFileHandler",
"level": "INFO",
"formatter": "default",
"filename": "logs/app.log",
"maxBytes": 10485760, # 10MB
"backupCount": 5
},
"access_file": {
"class": "logging.handlers.RotatingFileHandler",
"level": "INFO",
"formatter": "access",
"filename": "logs/access.log",
"maxBytes": 10485760, # 10MB
"backupCount": 5
"stream": sys.stdout
}
},
"loggers": {
"": { # Root logger
"handlers": ["console", "file"],
"handlers": ["console"],
"level": "INFO"
},
"app": { # Application logger
"handlers": ["console", "file"],
"handlers": ["console"],
"level": "INFO",
"propagate": False
},
"app.access": { # Access logger
"handlers": ["access_file"],
"handlers": ["console"],
"level": "INFO",
"propagate": False
}

View File

@@ -1,4 +1,5 @@
from fastapi import FastAPI, Depends, HTTPException
"""Main application module"""
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.openapi.docs import get_swagger_ui_html
from fastapi.openapi.utils import get_openapi
@@ -6,7 +7,7 @@ import logging
from logging.config import dictConfig
from .logging_config import logging_config
from .middleware import LoggingMiddleware
from .routers import auth, requests
from .routers import auth, employees, requests
# Configure logging
dictConfig(logging_config)
@@ -34,6 +35,7 @@ app.add_middleware(LoggingMiddleware)
# Include routers
app.include_router(auth.router, prefix="/api/auth", tags=["auth"])
app.include_router(employees.router, prefix="/api/employees", tags=["employees"])
app.include_router(requests.router, prefix="/api/requests", tags=["requests"])
# Custom OpenAPI documentation
@@ -41,8 +43,7 @@ app.include_router(requests.router, prefix="/api/requests", tags=["requests"])
async def custom_swagger_ui_html():
return get_swagger_ui_html(
openapi_url="/api/openapi.json",
title="Support Portal API Documentation",
swagger_favicon_url="/favicon.ico"
title="Support Portal API Documentation"
)
@app.get("/api/openapi.json", include_in_schema=False)

View File

@@ -8,13 +8,19 @@ logger = logging.getLogger("app.access")
class LoggingMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
"""Process request and log details"""
start_time = time.time()
# Get client info safely
client_host = "unknown"
if request.client and hasattr(request.client, "host"):
client_host = request.client.host
# Log request
logger.info(
"Request started1",
"Request started",
extra={
"client_addr": request.client.host,
"client_addr": client_host,
"request_line": f"{request.method} {request.url.path}",
"status_code": "PENDING"
}
@@ -29,7 +35,7 @@ class LoggingMiddleware(BaseHTTPMiddleware):
logger.info(
"Request completed",
extra={
"client_addr": request.client.host,
"client_addr": client_host,
"request_line": f"{request.method} {request.url.path}",
"status_code": response.status_code,
"process_time": f"{process_time:.2f}s"

View File

@@ -1,21 +1,20 @@
from pydantic import BaseModel
"""Employee models"""
from pydantic import BaseModel, Field
from datetime import datetime
from typing import Optional
class EmployeeBase(BaseModel):
last_name: str
first_name: str
department: str
office: str
first_name: str = Field(..., min_length=1)
last_name: str = Field(..., min_length=1)
department: str = Field(..., pattern="^(aho|gkh|general)$")
office: str = Field(..., min_length=1)
class EmployeeCreate(EmployeeBase):
password: str
password: str = Field(..., min_length=6)
class Employee(EmployeeBase):
id: int
created_at: datetime
class Config:
from_attributes = True
from_attributes = True

View File

@@ -3,40 +3,37 @@ from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from ..database import get_db
from ..crud import auth as auth_crud
from ..models.employee import EmployeeBase
from logging import getLogger
from ..utils.loggers import auth_logger
router = APIRouter()
logger = getLogger(__name__)
@router.post("/login")
async def login(credentials: dict, db: Session = Depends(get_db)):
"""Employee login endpoint"""
try:
# Validate required fields
if not credentials.get("lastName") or not credentials.get("password"):
raise HTTPException(
status_code=400,
detail="Необходимо указать фамилию и пароль"
)
employee = auth_crud.authenticate_employee(
db,
credentials.get("lastName"),
credentials.get("password")
credentials["lastName"],
credentials["password"]
)
if not employee:
raise HTTPException(status_code=401, detail="Неверные учетные данные")
raise HTTPException(
status_code=401,
detail="Неверные учетные данные"
)
return employee
except HTTPException:
raise
except Exception as e:
logger.error(f"Login error: {e}", exc_info=True)
raise HTTPException(status_code=500, detail="Ошибка сервера")
@router.post("/admin")
async def admin_login(credentials: dict, db: Session = Depends(get_db)):
"""Admin login endpoint"""
try:
is_valid = auth_crud.authenticate_admin(
db,
credentials.get("username"),
credentials.get("password")
)
if not is_valid:
raise HTTPException(status_code=401, detail="Неверные учетные данные")
return {"status": "success"}
except Exception as e:
logger.error(f"Admin login error: {e}", exc_info=True)
auth_logger.error(f"Login error: {e}", exc_info=True)
raise HTTPException(status_code=500, detail="Ошибка сервера")

View File

@@ -1,6 +1,7 @@
"""Employee management routes"""
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError
from ..database import get_db
from ..crud import employees as employees_crud
from ..models.employee import EmployeeCreate
@@ -12,11 +13,20 @@ router = APIRouter()
async def create_employee(employee: EmployeeCreate, db: Session = Depends(get_db)):
"""Create new employee"""
try:
# Check if employee already exists
existing = employees_crud.get_employee_by_lastname(db, employee.last_name)
if existing:
raise HTTPException(
status_code=400,
detail="Сотрудник с такой фамилией уже существует"
)
db_employee = employees_crud.create_employee(db, employee)
auth_logger.info(
"Employee created",
extra={"employee_id": db_employee.id}
)
return {
"id": db_employee.id,
"firstName": db_employee.first_name,
@@ -25,6 +35,14 @@ async def create_employee(employee: EmployeeCreate, db: Session = Depends(get_db
"office": db_employee.office,
"createdAt": db_employee.created_at
}
except HTTPException:
raise
except IntegrityError:
raise HTTPException(
status_code=400,
detail="Сотрудник с такими данными уже существует"
)
except Exception as e:
auth_logger.error(f"Error creating employee: {e}", exc_info=True)
raise HTTPException(status_code=500, detail="Ошибка при создании сотрудника")

View File

@@ -3,6 +3,7 @@ from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from ..database import get_db
from ..crud import requests as requests_crud
from ..crud import employees as employees_crud
from ..models.request import RequestCreate
from ..bot.notifications import send_notification
from ..utils.loggers import request_logger
@@ -13,13 +14,27 @@ router = APIRouter()
async def create_request(request: RequestCreate, db: Session = Depends(get_db)):
"""Create new request"""
try:
# Verify employee exists
employee = employees_crud.get_employee(db, request.employee_id)
if not employee:
raise HTTPException(
status_code=404,
detail="Сотрудник не найден"
)
db_request = requests_crud.create_request(db, request)
request_logger.info(
"Request created",
extra={"request_id": db_request.id, "employee_id": request.employee_id}
)
await send_notification(requests_crud.get_request_details(db, db_request.id))
request_details = requests_crud.get_request_details(db, db_request.id)
await send_notification(request_details)
return db_request
except HTTPException:
raise
except Exception as e:
request_logger.error(
f"Error creating request: {e}",