From e3860e249098f77bc8c7d2ef8665efd1675a5e5a Mon Sep 17 00:00:00 2001 From: itqop Date: Mon, 7 Apr 2025 10:18:31 +0300 Subject: [PATCH] test --- backend/app/core/config.py | 15 +--------- backend/app/db/session.py | 5 +--- backend/app/fuzzy_solver.py | 15 ++++------ backend/app/main.py | 32 +++++++-------------- backend/app/models/base.py | 2 -- backend/app/models/calculation.py | 1 - backend/app/models1.py | 20 ------------- backend/app/routers/calculation.py | 6 ++-- backend/app/schemas/calculation.py | 18 ++---------- backend/app/schemas/token.py | 4 +-- backend/app/services/calculation_service.py | 24 +--------------- 11 files changed, 25 insertions(+), 117 deletions(-) delete mode 100644 backend/app/models1.py diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 5813fef..08f0336 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -2,27 +2,14 @@ import os from pydantic_settings import BaseSettings from dotenv import load_dotenv -# Load .env file from the backend directory (one level up from core) -# BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -# load_dotenv(os.path.join(BASE_DIR, '..', '.env')) # Adjust path if needed - -# Or simply let pydantic-settings handle it by default if .env is in the root execution dir load_dotenv() - class Settings(BaseSettings): - DATABASE_URL: str = "sqlite:///./default.db" # Default value if not in .env - # Настройки JWT удалены - # SECRET_KEY: str = "default_secret" - # ALGORITHM: str = "HS256" - # ACCESS_TOKEN_EXPIRE_MINUTES: int = 30 + DATABASE_URL: str = "sqlite:///./default.db" class Config: env_file = ".env" env_file_encoding = 'utf-8' - # If your .env file is not in the root directory where you run uvicorn, - # you might need to specify the path explicitly: - # env_file = '../.env' # Example if running from inside 'app' dir settings = Settings() \ No newline at end of file diff --git a/backend/app/db/session.py b/backend/app/db/session.py index 870ca42..1bbdbd1 100644 --- a/backend/app/db/session.py +++ b/backend/app/db/session.py @@ -2,17 +2,14 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from app.core.config import settings -# Create SQLAlchemy engine engine = create_engine( settings.DATABASE_URL, - # Required for SQLite to prevent issues with FastAPI's async nature + connect_args={"check_same_thread": False} if "sqlite" in settings.DATABASE_URL else {} ) -# Create a session factory SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) -# Dependency to get DB session (we'll use this in routers) def get_db(): db = SessionLocal() try: diff --git a/backend/app/fuzzy_solver.py b/backend/app/fuzzy_solver.py index 1858b97..dd60b09 100644 --- a/backend/app/fuzzy_solver.py +++ b/backend/app/fuzzy_solver.py @@ -2,7 +2,7 @@ import asyncio import random from typing import Dict, Any -# Делаем функцию асинхронной + async def solve_cutting_problem(input_params: Dict[str, Any]) -> Dict[str, Any]: """ Асинхронная заглушка для имитации сложного расчета раскройки стекла. @@ -10,26 +10,23 @@ async def solve_cutting_problem(input_params: Dict[str, Any]) -> Dict[str, Any]: """ print(f"Received input for async fuzzy solver: {input_params}") - # Имитация времени расчета с использованием asyncio.sleep delay = random.uniform(0.5, 2.0) print(f"Simulating calculation for {delay:.2f} seconds...") - await asyncio.sleep(delay) # Используем await asyncio.sleep вместо time.sleep - - # Пример выходных данных (остается таким же) + await asyncio.sleep(delay) + output_data = { "layout": [ {"piece_id": 1, "x": 10, "y": 10, "width": 500, "height": 300}, {"piece_id": 2, "x": 520, "y": 10, "width": 400, "height": 300}, - # ... другие детали раскроя ... + ], "waste_percentage": round(random.uniform(5.0, 15.0), 2), "number_of_cuts": random.randint(5, 20), - "processing_time_ms": int(delay * 1000) # Используем задержку как время обработки + "processing_time_ms": int(delay * 1000) } - # Можно добавить логику на основе input_params, если нужно для тестов if input_params.get("optimize_for") == "speed": - output_data["number_of_cuts"] = random.randint(15, 25) # Больше резов - быстрее? + output_data["number_of_cuts"] = random.randint(15, 25) print(f"Async fuzzy solver generated output: {output_data}") return output_data \ No newline at end of file diff --git a/backend/app/main.py b/backend/app/main.py index 7d20618..0619937 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,37 +1,25 @@ from fastapi import FastAPI -from fastapi.middleware.cors import CORSMiddleware # Импортируем middleware -from app.routers import calculation # Импортируем роутер +from fastapi.middleware.cors import CORSMiddleware +from app.routers import calculation app = FastAPI(title="Glass Cutting Optimization API") -# --- Настройка CORS --- -# Список разрешенных источников (origins) -# Для разработки можно разрешить все или указать адрес вашего React-приложения origins = [ - "http://localhost", # Если React запускается на http://localhost:port - "http://localhost:3000", # Стандартный порт для create-react-app - "http://localhost:5173", # Стандартный порт для Vite/React - # "http://127.0.0.1:5173", # Можно добавить и IP-адрес - # "*" # Разрешить все источники (будьте осторожны в production) + "http://localhost", + "http://localhost:3000", + "http://localhost:5173", ] app.add_middleware( CORSMiddleware, - allow_origins=origins, # Указываем разрешенные источники - allow_credentials=True, # Разрешаем куки (если потребуются в будущем) - allow_methods=["*"], # Разрешаем все методы (GET, POST, PUT, etc.) - allow_headers=["*"], # Разрешаем все заголовки + allow_origins=origins, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], ) -# --- Конец настройки CORS --- - -# Подключаем роутер расчетов app.include_router(calculation.router) @app.get("/") async def read_root(): - return {"message": "Welcome to the Glass Cutting Optimization API"} - -# Убедимся, что здесь нет подключения auth роутера -# from .routers import calculation # Позже подключим calculation -# app.include_router(calculation.router) \ No newline at end of file + return {"message": "Welcome to the Glass Cutting Optimization API"} \ No newline at end of file diff --git a/backend/app/models/base.py b/backend/app/models/base.py index 23a74d1..b9a25f5 100644 --- a/backend/app/models/base.py +++ b/backend/app/models/base.py @@ -2,7 +2,5 @@ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column from sqlalchemy import Integer from typing import Annotated -# Define a base class using SQLAlchemy 2.0 style class Base(DeclarativeBase): - # Define a primary key type annotation for convenience pass \ No newline at end of file diff --git a/backend/app/models/calculation.py b/backend/app/models/calculation.py index b9611a4..4203b7e 100644 --- a/backend/app/models/calculation.py +++ b/backend/app/models/calculation.py @@ -8,7 +8,6 @@ from typing import Dict, Any class Calculation(Base): __tablename__ = "calculations" - # id: Mapped[int] = mapped_column(primary_key=True, index=True) input_params: Mapped[Dict[str, Any]] = mapped_column(JSON, nullable=False) output_results: Mapped[Dict[str, Any] | None] = mapped_column(JSON, nullable=True) diff --git a/backend/app/models1.py b/backend/app/models1.py deleted file mode 100644 index 4a40cfa..0000000 --- a/backend/app/models1.py +++ /dev/null @@ -1,20 +0,0 @@ -from sqlalchemy import Column, Integer, String, JSON, Float, DateTime -from sqlalchemy.sql import func -from sqlalchemy.ext.declarative import declarative_base - -Base = declarative_base() - -class Calculation(Base): - __tablename__ = "calculations" - - id = Column(Integer, primary_key=True, index=True) - input_params = Column(JSON) - output_results = Column(JSON) - timestamp = Column(DateTime(timezone=True), server_default=func.now()) - model_name = Column(String, nullable=True) - objective_score = Column(Float, nullable=True) - # Удалите эти строки, если они есть: - # user_id = Column(Integer, ForeignKey("users.id")) - # user = relationship("User", back_populates="calculations") - -# Можно вообще удалить класс User, если он больше не нужен \ No newline at end of file diff --git a/backend/app/routers/calculation.py b/backend/app/routers/calculation.py index eac7ee3..dbe1c19 100644 --- a/backend/app/routers/calculation.py +++ b/backend/app/routers/calculation.py @@ -7,8 +7,8 @@ from app.services import calculation_service as service from app.db.session import get_db router = APIRouter( - prefix="/calculation", # Общий префикс для эндпоинтов этого роутера - tags=["Calculations"], # Тег для группировки в документации Swagger + prefix="/calculation", + tags=["Calculations"], ) @router.post("/", response_model=CalculationRead, status_code=status.HTTP_201_CREATED) @@ -45,8 +45,6 @@ async def read_calculation( ) return db_calc -# Определяем эндпоинт для истории до эндпоинта с ID, -# чтобы FastAPI не принял "history" за ID @router.get("/history/", response_model=List[CalculationRead]) async def read_calculation_history( skip: int = 0, diff --git a/backend/app/schemas/calculation.py b/backend/app/schemas/calculation.py index 13c1ef7..7078bae 100644 --- a/backend/app/schemas/calculation.py +++ b/backend/app/schemas/calculation.py @@ -2,34 +2,22 @@ from pydantic import BaseModel, ConfigDict from datetime import datetime from typing import Optional, Dict, Any -# --- Базовая схема --- -# Общие поля для расчета class CalculationBase(BaseModel): input_params: Dict[str, Any] - model_name: Optional[str] = None # Модель может быть выбрана сервером + model_name: Optional[str] = None -# --- Схема для создания (запуска) расчета --- -# Данные, которые пользователь отправляет для запуска class CalculationCreate(CalculationBase): - pass # Пока совпадает с Base, но может быть расширена + pass -# --- Схема для чтения --- -# Полная информация о расчете, возвращаемая API class CalculationRead(CalculationBase): id: int output_results: Optional[Dict[str, Any]] = None timestamp: datetime objective_score: Optional[float] = None - # Включаем режим ORM model_config = ConfigDict(from_attributes=True) - # В Pydantic v1 было: - # class Config: - # orm_mode = True - -# --- Опционально: Схема для обновления (если результаты добавляются позже) --- class CalculationUpdate(BaseModel): output_results: Optional[Dict[str, Any]] = None objective_score: Optional[float] = None - model_name: Optional[str] = None # Если модель определяется по результату \ No newline at end of file + model_name: Optional[str] = None \ No newline at end of file diff --git a/backend/app/schemas/token.py b/backend/app/schemas/token.py index 5caaa71..9628562 100644 --- a/backend/app/schemas/token.py +++ b/backend/app/schemas/token.py @@ -6,6 +6,4 @@ class Token(BaseModel): token_type: str class TokenData(BaseModel): - username: Optional[str] = None - # Можно использовать id пользователя, если удобнее - # user_id: Optional[int] = None \ No newline at end of file + username: Optional[str] = None \ No newline at end of file diff --git a/backend/app/services/calculation_service.py b/backend/app/services/calculation_service.py index fa0ccf5..b369ff0 100644 --- a/backend/app/services/calculation_service.py +++ b/backend/app/services/calculation_service.py @@ -3,25 +3,19 @@ from typing import List, Optional, Dict, Any from app.models.calculation import Calculation from app.schemas.calculation import CalculationCreate, CalculationUpdate -from app.fuzzy_solver import solve_cutting_problem # Импортируем асинхронную заглушку +from app.fuzzy_solver import solve_cutting_problem -# Делаем функцию создания асинхронной async def create_calculation(db: Session, calc_in: CalculationCreate) -> Calculation: """ Асинхронно создает запись о расчете, вызывает заглушку и сохраняет результат. """ - # 1. Вызываем асинхронную заглушку расчета print("Calling async solver...") output_results = await solve_cutting_problem(calc_in.input_params) print("Async solver finished.") - # 2. Определяем другие параметры (логика остается) objective_score = output_results.get("waste_percentage") model_name = calc_in.model_name or "default_fuzzy_model_v1" - # 3. Создаем объект модели SQLAlchemy - # Операции с БД остаются синхронными в этом примере - # Для полной асинхронности потребовался бы async-драйвер и AsyncSession print("Creating DB object...") db_calc = Calculation( input_params=calc_in.input_params, @@ -30,7 +24,6 @@ async def create_calculation(db: Session, calc_in: CalculationCreate) -> Calcula model_name=model_name ) - # 4. Сохраняем в базу данных (синхронные операции) print("Adding to DB session...") db.add(db_calc) print("Committing DB session...") @@ -40,8 +33,6 @@ async def create_calculation(db: Session, calc_in: CalculationCreate) -> Calcula print("Calculation created and saved.") return db_calc -# Функции чтения можно оставить синхронными, т.к. они быстрые -# и используют синхронную сессию def get_calculation_by_id(db: Session, calc_id: int) -> Optional[Calculation]: """ Получает расчет по его ID. @@ -53,16 +44,3 @@ def get_calculations(db: Session, skip: int = 0, limit: int = 100) -> List[Calcu Получает список всех расчетов с пагинацией. """ return db.query(Calculation).offset(skip).limit(limit).all() - -# Опционально: Функция для обновления расчета (если нужно) -# def update_calculation(db: Session, calc_id: int, calc_update: CalculationUpdate) -> Optional[Calculation]: -# db_calc = get_calculation_by_id(db, calc_id) -# if not db_calc: -# return None -# update_data = calc_update.model_dump(exclude_unset=True) # Pydantic v2 -# # update_data = calc_update.dict(exclude_unset=True) # Pydantic v1 -# for key, value in update_data.items(): -# setattr(db_calc, key, value) -# db.commit() -# db.refresh(db_calc) -# return db_calc \ No newline at end of file