gemini-cli refactor

This commit is contained in:
2025-07-18 01:59:30 +03:00
parent 0868dd21cc
commit 840e2c4d6a
10 changed files with 956 additions and 908 deletions

View File

@@ -1,139 +1,74 @@
# [FILE] src/core/models.py
# ANCHOR: Core_Models_Module
# Семантика: Определяет Pydantic-модели для структурированного представления данных
# в приложении (продукты, логи, сообщения RabbitMQ).
# [CONTRACT]: Все модели наследуются от `BaseModel` и обеспечивают типизацию и валидацию.
# [COHERENCE]: Согласованы со схемами данных, используемыми в БД и экспортах.
# <MODULE name="core.models" semantics="data_contracts" />
# <DESIGN_NOTE>
# Этот модуль определяет все Pydantic-модели, которые служат контрактами данных
# в приложении. Они обеспечивают валидацию, типизацию и четкую структуру
# для продуктов, логов и сообщений RabbitMQ.
# </DESIGN_NOTE>
from pydantic import BaseModel, Field, HttpUrl, ValidationError
# <IMPORTS>
from pydantic import BaseModel, Field, HttpUrl
from datetime import datetime
from typing import Optional, List
from typing import List
import uuid
# </IMPORTS>
# <MAIN_CONTRACT for="ProductVariant">
# description: "Модель данных для одного варианта продукта."
# invariant: "`name`, `price`, `url` являются обязательными. `price` всегда `int` > 0."
# </MAIN_CONTRACT>
class ProductVariant(BaseModel):
"""
[CONTRACT]
@description: Модель данных для варианта продукта.
@invariant: `name`, `price`, `url` являются обязательными. `price` всегда `int`.
"""
# <STATE name="product_variant_fields">
name: str = Field(..., description="Название продукта.")
volume: str = Field(..., description="Объем или вариант продукта (например, '50мл', '10 капсул').")
price: int = Field(..., description="Цена продукта в числовом формате.")
url: HttpUrl = Field(..., description="Полный URL страницы варианта продукта.", examples=["https://elixirpeptide.ru/product/?product=123"])
# [VALIDATOR] Пример пост-валидации, если нужно.
# @validator('price')
# def price_must_be_positive(cls, v):
# if v < 0:
# raise ValueError('Price must be a positive integer')
# return v
class Config:
json_schema_extra = {
"example": {
"name": "Peptide X",
"volume": "30ml",
"price": 1500,
"url": "https://elixirpeptide.ru/catalog/peptide-x/?product=variant1"
}
}
price: int = Field(..., gt=0, description="Цена продукта в числовом формате, должна быть положительной.")
url: HttpUrl = Field(..., description="Полный URL страницы варианта продукта.")
is_in_stock: bool = Field(..., description="Наличие товара.")
# </STATE>
# <MAIN_CONTRACT for="LogRecordModel">
# description: "Модель данных для записи лога, используемая при сохранении в БД или отправке в RabbitMQ."
# invariant: "Все поля являются обязательными."
# </MAIN_CONTRACT>
class LogRecordModel(BaseModel):
"""
[CONTRACT]
@description: Модель данных для записи лога, используемая при сохранении логов в БД.
@invariant: Все поля являются обязательными. `timestamp` хранится как ISO-строка.
"""
# <STATE name="log_record_fields">
run_id: str = Field(..., description="Уникальный идентификатор текущего запуска парсера.")
timestamp: datetime = Field(..., description="Время создания лог-записи.")
level: str = Field(..., description="Уровень логирования (e.g., INFO, ERROR, DEBUG).")
message: str = Field(..., description="Текст лог-сообщения.")
# </STATE>
# Pydantic автоматически обработает datetime в JSON и другие форматы.
# Для SQLite, timestamp будет храниться как TEXT в ISO-формате.
class Config:
json_schema_extra = {
"example": {
"run_id": "20231027-123456",
"timestamp": "2023-10-27T12:34:56.789Z",
"level": "INFO",
"message": "Парсинг начат."
}
}
# ANCHOR: RabbitMQ_Models
# Семантика: Модели для работы с сообщениями RabbitMQ
# <MODULE name="rabbitmq_models" semantics="message_contracts_for_rabbitmq" />
# <MAIN_CONTRACT for="RabbitMQMessage">
# description: "Базовая модель для всех сообщений, отправляемых в RabbitMQ."
# invariant: "Все сообщения имеют уникальный ID, timestamp и источник."
# </MAIN_CONTRACT>
class RabbitMQMessage(BaseModel):
"""
[CONTRACT]
@description: Базовая модель для сообщений RabbitMQ.
@invariant: Все сообщения имеют уникальный ID и timestamp.
"""
# <STATE name="rabbitmq_base_fields">
message_id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Уникальный идентификатор сообщения.")
timestamp: datetime = Field(default_factory=datetime.utcnow, description="Время создания сообщения.")
source: str = Field(..., description="Источник сообщения (например, 'price_parser').")
class Config:
json_encoders = {
datetime: lambda v: v.isoformat()
}
source: str = Field(default="price_parser", description="Источник сообщения.")
# </STATE>
# <MAIN_CONTRACT for="ProductDataMessage">
# description: "Модель сообщения с данными о продуктах для отправки в RabbitMQ."
# invariant: "Содержит список продуктов и метаданные о запуске."
# </MAIN_CONTRACT>
class ProductDataMessage(RabbitMQMessage):
"""
[CONTRACT]
@description: Модель сообщения с данными о продуктах для отправки в RabbitMQ.
@invariant: Содержит список продуктов и метаданные о парсинге.
"""
# <STATE name="product_data_message_fields">
products: List[ProductVariant] = Field(..., description="Список продуктов для обработки.")
run_id: str = Field(..., description="Идентификатор запуска парсера.")
total_count: int = Field(..., description="Общее количество продуктов в сообщении.")
class Config:
json_schema_extra = {
"example": {
"message_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2023-10-27T12:34:56.789Z",
"source": "price_parser",
"products": [
{
"name": "Peptide X",
"volume": "30ml",
"price": 1500,
"url": "https://elixirpeptide.ru/catalog/peptide-x/?product=variant1"
}
],
"run_id": "20231027-123456",
"total_count": 1
}
}
# </STATE>
# <MAIN_CONTRACT for="LogMessage">
# description: "Модель сообщения с логами для отправки в RabbitMQ."
# invariant: "Содержит список записей логов и метаданные о запуске."
# </MAIN_CONTRACT>
class LogMessage(RabbitMQMessage):
"""
[CONTRACT]
@description: Модель сообщения с логами для отправки в RabbitMQ.
@invariant: Содержит информацию о логах парсера.
"""
# <STATE name="log_message_fields">
log_records: List[LogRecordModel] = Field(..., description="Список записей логов.")
run_id: str = Field(..., description="Идентификатор запуска парсера.")
class Config:
json_schema_extra = {
"example": {
"message_id": "550e8400-e29b-41d4-a716-446655440001",
"timestamp": "2023-10-27T12:34:56.789Z",
"source": "price_parser",
"log_records": [
{
"run_id": "20231027-123456",
"timestamp": "2023-10-27T12:34:56.789Z",
"level": "INFO",
"message": "Парсинг начат."
}
],
"run_id": "20231027-123456"
}
}
# </STATE>
# [COHERENCE_CHECK_PASSED] Все основные модели данных определены и типизированы.
# <COHERENCE_CHECK status="PASSED" description="Все основные модели данных определены, типизированы и структурированы." />