Compare commits

21 Commits

Author SHA1 Message Date
Kita Trofimov 7fdd3c56cb Merge remote-tracking branch 'origin/feat/db-init' into feat/db-init
# Conflicts:
#	api/loginapi.py
#	db/connection.py
#	db/repositories/ai_prediction_repository.py
#	db/repositories/inventory_repository.py
#	db/repositories/product_repository.py
#	db/repositories/robot_repository.py
#	db/repositories/user_repository.py
2025-10-27 23:04:10 +03:00
Kita Trofimov 052c93928c feat(db): Database initialization has been added 2025-10-27 23:02:19 +03:00
Sweetbread e44696ce04 wip 2025-10-26 23:19:51 +03:00
Sweetbread 72f766ab46 fix(log): replace logger with loguru (again!) 2025-10-26 23:09:42 +03:00
Kita Trofimov 9ecb6a83ba style(database): Fixed the issue with long function declarations 2025-10-26 21:25:59 +03:00
Kita Trofimov 951e4c7e45 style(database): Changed logging 2025-10-26 21:25:59 +03:00
Kita Trofimov c33e8798ed style(database): Fixed type of logger on the db sucsess operations 2025-10-26 20:31:51 +03:00
Kita Trofimov 401205aea2 style(database): Fixed the issue with long function declarations 2025-10-26 19:41:11 +03:00
Kita Trofimov 8c07b1febc style(database): Changed logging 2025-10-26 19:32:34 +03:00
Kita Trofimov 3e4755cca1 fix(database): Fixed the database connection 2025-10-26 19:14:51 +03:00
Kita Trofimov c117ceb85e style(database): Refactor repository name 2025-10-26 18:52:54 +03:00
Kita Trofimov 4e3b21f31e fix(model/user.py): Removed unnecesary methods 2025-10-26 18:48:54 +03:00
Kita Trofimov 6483afa0d4 feat(database/repositories/user_repository.py): A new login verification method has been added 2025-10-26 16:32:02 +03:00
Kita Trofimov 251e1b6e57 feat(database/repositories/ai_prediction_repository.py): Added new repository AIPredictionsRepository 2025-10-26 16:09:33 +03:00
Kita Trofimov 64e81aec69 feat(model/ai_prediction.py): Added new model AIPrediction 2025-10-26 15:52:48 +03:00
Kita Trofimov 6d5abeb88d feat(database/repositories/inventory_repository.py): Added inventory repository 2025-10-26 15:42:20 +03:00
Kita Trofimov fa00e27015 feat(database/repositories/product_repository.py): Added product repository 2025-10-26 15:04:47 +03:00
Kita Trofimov 2f6782ab9d feat(database/repositories/robot_repository.py): Added robot_repository 2025-10-26 14:09:21 +03:00
Kita Trofimov c1bab1b304 feat(database/repositories/user_repository.py): Added user repository 2025-10-26 13:14:51 +03:00
Kita Trofimov 506f2f3f39 feat(database): Implemented database connection 2025-10-26 00:43:32 +03:00
Kita Trofimov 79cac3d8cb feat(model): Added correct data models 2025-10-26 00:11:20 +03:00
14 changed files with 549 additions and 203 deletions
+9 -2
View File
@@ -1,5 +1,7 @@
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from model.user import User from model.user import User
from db.repositories.user_repository import UserRepository # FIXME: authenticate_user as get_user
from utils.token import generateKey as getToken
auth = Blueprint("auth", __name__) auth = Blueprint("auth", __name__)
@@ -21,8 +23,13 @@ def login():
if len(password.strip()) < 8: if len(password.strip()) < 8:
return "Password is too short", 400 return "Password is too short", 400
user = User(email, password) user = UserRepository().authenticate_user(email, password)
return jsonify(user.toJson()) if not user:
return "Wrong credentials", 400
token = getToken(user)
return jsonify({'token': token, 'user': {'id': user.id, 'name': user.name, 'role': user.role}})
else: else:
return "Request is not a json", 400 return "Request is not a json", 400
+26 -12
View File
@@ -1,19 +1,29 @@
# db/connection.py
import psycopg2 import psycopg2
import os import os
from contextlib import contextmanager from contextlib import contextmanager
from typing import Generator from typing import Generator
from loguru import logger
from utils.loadDotEnv import initializeENV from utils.loadDotEnv import initializeENV
initializeENV() initializeENV()
def PSQLConnect(): def PSQLConnect():
conn = psycopg2.connect(os.getenv('POSTDRESS_CONNECTION')) conn_str = os.getenv('POSTGRES_CONNECTION')
if not conn_str:
logger.error("POSTGRES_CONNECTION не найден в .env файле")
raise ValueError("POSTGRES_CONNECTION не найден в .env файле")
conn = psycopg2.connect(conn_str)
logger.debug("Подключение к БД установлено")
return conn return conn
def PSQLCursor(conn): def PSQLCursor(conn):
cur = conn.cursor() cur = conn.cursor()
logger.debug("Курсор БД создан")
return cur return cur
@@ -22,18 +32,19 @@ def get_connection() -> Generator[psycopg2.extensions.connection, None, None]:
conn = None conn = None
try: try:
conn = PSQLConnect() conn = PSQLConnect()
print("Подключение к БД установлено") logger.debug("Контекст подключения к БД открыт")
yield conn yield conn
except psycopg2.OperationalError as e:
print(f"Ошибка подключения к БД: {e}")
raise
except Exception as e: except Exception as e:
print(f"Неожиданная ошибка: {e}") logger.error(f"Ошибка в контексте подключения: {e}")
if conn:
conn.rollback()
logger.debug("Откат транзакции выполнен")
raise raise
finally: finally:
if conn: if conn:
conn.close() conn.close()
print("Соединение с БД закрыто") logger.debug("Подключение к БД закрыто")
def test_connection() -> bool: def test_connection() -> bool:
try: try:
@@ -41,11 +52,14 @@ def test_connection() -> bool:
cur = PSQLCursor(conn) cur = PSQLCursor(conn)
cur.execute("SELECT version();") cur.execute("SELECT version();")
version = cur.fetchone() version = cur.fetchone()
print(f"Версия PostgreSQL: {version[0]}") logger.info(f"Подключение к БД успешно: {version[0]}")
cur.close() cur.close()
logger.debug("Курсор БД закрыт")
return True return True
except Exception as e: except Exception as e:
print(f"Тест подключения к БД провален: {e}") logger.error(f"Ошибка подключения к БД: {e}")
return False return False
print(test_connection())
if __name__ == "__main__":
test_connection()
+147
View File
@@ -0,0 +1,147 @@
from db.connection import get_connection
from loguru import logger
def create_tables():
try:
with get_connection() as conn:
with conn.cursor() as cur:
# Пользователи
cur.execute("""
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
role VARCHAR(50) NOT NULL DEFAULT 'viewer',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
# Роботы
cur.execute("""
CREATE TABLE IF NOT EXISTS robots (
id VARCHAR(50) PRIMARY KEY,
status VARCHAR(50) DEFAULT 'active',
battery_level INTEGER DEFAULT 100,
last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
current_zone VARCHAR(10),
current_row INTEGER,
current_shelf INTEGER
)
""")
# Товары
cur.execute("""
CREATE TABLE IF NOT EXISTS products (
id VARCHAR(50) PRIMARY KEY,
name VARCHAR(255) NOT NULL,
category VARCHAR(100),
min_stock INTEGER DEFAULT 10,
optimal_stock INTEGER DEFAULT 100
)
""")
# История инвентаризации
cur.execute("""
CREATE TABLE IF NOT EXISTS inventory_history (
id SERIAL PRIMARY KEY,
robot_id VARCHAR(50) REFERENCES robots(id),
product_id VARCHAR(50) REFERENCES products(id),
quantity INTEGER NOT NULL,
zone VARCHAR(10) NOT NULL,
row_number INTEGER,
shelf_number INTEGER,
status VARCHAR(50),
scanned_at TIMESTAMP NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
# Прогнозы ИИ
cur.execute("""
CREATE TABLE IF NOT EXISTS ai_predictions (
id SERIAL PRIMARY KEY,
product_id VARCHAR(50) REFERENCES products(id),
prediction_date DATE NOT NULL,
days_until_stockout INTEGER,
recommended_order INTEGER,
confidence_score DECIMAL(3,2),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
conn.commit()
logger.debug("Все таблицы успешно созданы")
except Exception as e:
logger.error(f"Ошибка создания таблиц: {e}")
raise
def create_indexes():
try:
with get_connection() as conn:
with conn.cursor() as cur:
cur.execute("CREATE INDEX IF NOT EXISTS idx_inventory_scanned ON inventory_history(scanned_at DESC)")
cur.execute("CREATE INDEX IF NOT EXISTS idx_inventory_product ON inventory_history(product_id)")
cur.execute("CREATE INDEX IF NOT EXISTS idx_inventory_zone ON inventory_history(zone)")
conn.commit()
logger.debug("Индексы созданы")
except Exception as e:
logger.error(f"Ошибка создания индексов: {e}")
raise
def insert_sample_data():
try:
with get_connection() as conn:
with conn.cursor() as cur:
cur.execute("""
INSERT INTO users (email, password_hash, name, role)
VALUES
('admin@warehouse.com', 'hash1', 'Администратор', 'admin'),
('operator@warehouse.com', 'hash2', 'Оператор Иванов', 'operator'),
('viewer@warehouse.com', 'hash3', 'Наблюдатель Петров', 'viewer')
ON CONFLICT (email) DO NOTHING
""")
cur.execute("""
INSERT INTO robots (id, status, battery_level, current_zone)
VALUES
('RB-001', 'active', 85, 'A'),
('RB-002', 'active', 45, 'B'),
('RB-003', 'maintenance', 100, NULL)
ON CONFLICT (id) DO NOTHING
""")
cur.execute("""
INSERT INTO products (id, name, category, min_stock, optimal_stock)
VALUES
('TEL-1234', 'Смартфон X', 'Электроника', 5, 50),
('NOTE-567', 'Ноутбук Pro', 'Электроника', 3, 20),
('ACC-999', 'Чехол для телефона', 'Аксессуары', 10, 100)
ON CONFLICT (id) DO NOTHING
""")
conn.commit()
logger.debug("Тестовые данные добавлены")
except Exception as e:
logger.error(f"Ошибка добавления тестовых данных: {e}")
raise
def initialize_database():
logger.info("Начинаем инициализацию базы данных...")
create_tables()
create_indexes()
insert_sample_data()
logger.debug("База данных успешно инициализирована!")
if __name__ == "__main__":
initialize_database()
+32 -16
View File
@@ -1,16 +1,16 @@
from typing import List, Optional from typing import List, Optional
from datetime import datetime, date from datetime import datetime, date
from loguru import logger
from db.connection import get_connection from db.connection import get_connection
from model.ai_prediction import AIPrediction from model.ai_prediction import AIPrediction
class AIPredictionsRepository: class AIPredictionsRepository:
def get_all(self) -> List[AIPrediction]: def get_all(self) -> List[AIPrediction]:
try: try:
with get_connection() as conn: with get_connection() as conn:
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute("SELECT * FROM ai_predictions ORDER BY prediction_date DESC, product_id") cur.execute("SELECT * FROM ai_predictions ORDER BY prediction_date DESC, product_id")
return [ predictions = [
AIPrediction( AIPrediction(
id=row[0], id=row[0],
product_id=row[1], product_id=row[1],
@@ -21,8 +21,10 @@ class AIPredictionsRepository:
created_at=row[6] created_at=row[6]
) for row in cur.fetchall() ) for row in cur.fetchall()
] ]
logger.debug(f"Получено {len(predictions)} прогнозов")
return predictions
except Exception as e: except Exception as e:
print(f"Ошибка получения прогнозов: {e}") logger.error(f"Ошибка получения прогнозов: {e}")
return [] return []
def get_by_id(self, prediction_id: int) -> Optional[AIPrediction]: def get_by_id(self, prediction_id: int) -> Optional[AIPrediction]:
@@ -31,9 +33,13 @@ class AIPredictionsRepository:
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute("SELECT * FROM ai_predictions WHERE id = %s", (prediction_id,)) cur.execute("SELECT * FROM ai_predictions WHERE id = %s", (prediction_id,))
row = cur.fetchone() row = cur.fetchone()
return AIPrediction(*row) if row else None if row:
logger.debug(f"Прогноз {prediction_id} найден")
return AIPrediction(*row)
logger.warning(f"Прогноз {prediction_id} не найден")
return None
except Exception as e: except Exception as e:
print(f"Ошибка получения прогноза {prediction_id}: {e}") logger.error(f"Ошибка получения прогноза {prediction_id}: {e}")
return None return None
def get_by_product(self, product_id: str, limit: int = 10) -> List[AIPrediction]: def get_by_product(self, product_id: str, limit: int = 10) -> List[AIPrediction]:
@@ -46,9 +52,11 @@ class AIPredictionsRepository:
ORDER BY prediction_date DESC ORDER BY prediction_date DESC
LIMIT %s LIMIT %s
""", (product_id, limit)) """, (product_id, limit))
return [AIPrediction(*row) for row in cur.fetchall()] predictions = [AIPrediction(*row) for row in cur.fetchall()]
logger.debug(f"Получено {len(predictions)} прогнозов для товара {product_id}")
return predictions
except Exception as e: except Exception as e:
print(f"Ошибка получения прогноза по товару {product_id}: {e}") logger.error(f"Ошибка получения прогноза по товару {product_id}: {e}")
return [] return []
def get_latest_predictions(self) -> List[AIPrediction]: def get_latest_predictions(self) -> List[AIPrediction]:
@@ -60,14 +68,21 @@ class AIPredictionsRepository:
FROM ai_predictions FROM ai_predictions
ORDER BY product_id, prediction_date DESC ORDER BY product_id, prediction_date DESC
""") """)
return [AIPrediction(*row) for row in cur.fetchall()] predictions = [AIPrediction(*row) for row in cur.fetchall()]
logger.debug(f"Получено {len(predictions)} последних прогнозов по товарам")
return predictions
except Exception as e: except Exception as e:
print(f"Ошибка получения последних прогнозов по товарам: {e}") logger.error(f"Ошибка получения последних прогнозов по товарам: {e}")
return [] return []
def create_prediction(self, product_id: str, prediction_date: date, def create_prediction(
days_until_stockout: int, recommended_order: int, self,
confidence_score: float) -> Optional[int]: product_id: str,
prediction_date: date,
days_until_stockout: int,
recommended_order: int,
confidence_score: float
) -> Optional[int]:
try: try:
with get_connection() as conn: with get_connection() as conn:
with conn.cursor() as cur: with conn.cursor() as cur:
@@ -80,12 +95,12 @@ class AIPredictionsRepository:
prediction_id = cur.fetchone()[0] prediction_id = cur.fetchone()[0]
conn.commit() conn.commit()
logger.debug(f"Создан новый прогноз ID: {prediction_id} для товара {product_id}")
return prediction_id return prediction_id
except Exception as e: except Exception as e:
print(f"Ошибка создания прогноза: {e}") logger.error(f"Ошибка создания прогноза: {e}")
return None return None
def delete_old_predictions(self, older_than_days: int = 90) -> int: def delete_old_predictions(self, older_than_days: int = 90) -> int:
try: try:
with get_connection() as conn: with get_connection() as conn:
@@ -96,7 +111,8 @@ class AIPredictionsRepository:
""", (older_than_days,)) """, (older_than_days,))
deleted_count = cur.rowcount deleted_count = cur.rowcount
conn.commit() conn.commit()
logger.debug(f"Удалено {deleted_count} старых прогнозов старше {older_than_days} дней")
return deleted_count return deleted_count
except Exception as e: except Exception as e:
print(f"Ошибка удаления старых прогнозов: {e}") logger.error(f"Ошибка удаления старых прогнозов: {e}")
return 0 return 0
+44 -19
View File
@@ -1,18 +1,17 @@
# db/repositories/inventory_repository.py # db/repositories/inventory_repository.py
from typing import List, Optional, Tuple from typing import List, Optional, Tuple
from datetime import datetime from datetime import datetime
from loguru import logger
from db.connection import get_connection from db.connection import get_connection
from model.inventory import InventoryRecord from model.inventory import InventoryRecord
class InventoryRepository: class InventoryRepository:
def get_all(self) -> List[InventoryRecord]: def get_all(self) -> List[InventoryRecord]:
try: try:
with get_connection() as conn: with get_connection() as conn:
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute("SELECT * FROM inventory_history ORDER BY scanned_at DESC") cur.execute("SELECT * FROM inventory_history ORDER BY scanned_at DESC")
return [ records = [
InventoryRecord( InventoryRecord(
id=row[0], id=row[0],
robot_id=row[1], robot_id=row[1],
@@ -26,8 +25,10 @@ class InventoryRepository:
created_at=row[9] created_at=row[9]
) for row in cur.fetchall() ) for row in cur.fetchall()
] ]
logger.debug(f"Получено {len(records)} записей инвентаризации")
return records
except Exception as e: except Exception as e:
print(f"Ошибка получения истории инвентаризации: {e}") logger.error(f"Ошибка получения истории инвентаризации: {e}")
return [] return []
def get_by_id(self, record_id: int) -> Optional[InventoryRecord]: def get_by_id(self, record_id: int) -> Optional[InventoryRecord]:
@@ -36,13 +37,26 @@ class InventoryRepository:
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute("SELECT * FROM inventory_history WHERE id = %s", (record_id,)) cur.execute("SELECT * FROM inventory_history WHERE id = %s", (record_id,))
row = cur.fetchone() row = cur.fetchone()
return InventoryRecord(*row) if row else None if row:
logger.debug(f"Запись инвентаризации {record_id} найдена")
return InventoryRecord(*row)
logger.warning(f"Запись инвентаризации {record_id} не найдена")
return None
except Exception as e: except Exception as e:
print(f"Ошибка получения записи инвентаризации {record_id}: {e}") logger.error(f"Ошибка получения записи инвентаризации {record_id}: {e}")
return None return None
def create_record(self, robot_id: str, product_id: str, quantity: int, zone: str, def create_record(
row_number: int, shelf_number: int, status: str, scanned_at: datetime) -> Optional[int]: self,
robot_id: str,
product_id: str,
quantity: int,
zone: str,
row_number: int,
shelf_number: int,
status: str,
scanned_at: datetime
) -> Optional[int]:
try: try:
with get_connection() as conn: with get_connection() as conn:
with conn.cursor() as cur: with conn.cursor() as cur:
@@ -55,9 +69,10 @@ class InventoryRepository:
record_id = cur.fetchone()[0] record_id = cur.fetchone()[0]
conn.commit() conn.commit()
logger.debug(f"Создана новая запись инвентаризации ID: {record_id}")
return record_id return record_id
except Exception as e: except Exception as e:
print(f"Ошибка создания записи инвентаризации: {e}") logger.error(f"Ошибка создания записи инвентаризации: {e}")
return None return None
def get_latest_inventory(self) -> List[InventoryRecord]: def get_latest_inventory(self) -> List[InventoryRecord]:
@@ -69,9 +84,11 @@ class InventoryRepository:
FROM inventory_history FROM inventory_history
ORDER BY product_id, scanned_at DESC ORDER BY product_id, scanned_at DESC
""") """)
return [InventoryRecord(*row) for row in cur.fetchall()] records = [InventoryRecord(*row) for row in cur.fetchall()]
logger.debug(f"Получено {len(records)} последних записей инвентаризации по товарам")
return records
except Exception as e: except Exception as e:
print(f"Ошибка получения последней записи инвентаризации по каждому товару: {e}") logger.error(f"Ошибка получения последней записи инвентаризации по каждому товару: {e}")
return [] return []
def get_records_by_product(self, product_id: str, limit: int = 100) -> List[InventoryRecord]: def get_records_by_product(self, product_id: str, limit: int = 100) -> List[InventoryRecord]:
@@ -84,9 +101,11 @@ class InventoryRepository:
ORDER BY scanned_at DESC ORDER BY scanned_at DESC
LIMIT %s LIMIT %s
""", (product_id, limit)) """, (product_id, limit))
return [InventoryRecord(*row) for row in cur.fetchall()] records = [InventoryRecord(*row) for row in cur.fetchall()]
logger.debug(f"Получено {len(records)} записей инвентаризации для товара {product_id}")
return records
except Exception as e: except Exception as e:
print(f"Ошибка получения записи инвентаризации по продукту {product_id}: {e}") logger.error(f"Ошибка получения записи инвентаризации по продукту {product_id}: {e}")
return [] return []
def get_records_by_robot(self, robot_id: str, limit: int = 100) -> List[InventoryRecord]: def get_records_by_robot(self, robot_id: str, limit: int = 100) -> List[InventoryRecord]:
@@ -99,9 +118,11 @@ class InventoryRepository:
ORDER BY scanned_at DESC ORDER BY scanned_at DESC
LIMIT %s LIMIT %s
""", (robot_id, limit)) """, (robot_id, limit))
return [InventoryRecord(*row) for row in cur.fetchall()] records = [InventoryRecord(*row) for row in cur.fetchall()]
logger.debug(f"Получено {len(records)} записей инвентаризации для робота {robot_id}")
return records
except Exception as e: except Exception as e:
print(f"Ошибка получения записи инвентаризации по роботу {robot_id}: {e}") logger.error(f"Ошибка получения записи инвентаризации по роботу {robot_id}: {e}")
return [] return []
def get_records_by_zone(self, zone: str, limit: int = 100) -> List[InventoryRecord]: def get_records_by_zone(self, zone: str, limit: int = 100) -> List[InventoryRecord]:
@@ -114,9 +135,11 @@ class InventoryRepository:
ORDER BY scanned_at DESC ORDER BY scanned_at DESC
LIMIT %s LIMIT %s
""", (zone, limit)) """, (zone, limit))
return [InventoryRecord(*row) for row in cur.fetchall()] records = [InventoryRecord(*row) for row in cur.fetchall()]
logger.debug(f"Получено {len(records)} записей инвентаризации для зоны {zone}")
return records
except Exception as e: except Exception as e:
print(f"Ошибка получения записи инвентаризации по зоне {zone}: {e}") logger.error(f"Ошибка получения записи инвентаризации по зоне {zone}: {e}")
return [] return []
def get_critical_items(self, hours: int = 24) -> List[InventoryRecord]: def get_critical_items(self, hours: int = 24) -> List[InventoryRecord]:
@@ -129,7 +152,9 @@ class InventoryRepository:
AND scanned_at >= NOW() - INTERVAL '%s hours' AND scanned_at >= NOW() - INTERVAL '%s hours'
ORDER BY scanned_at DESC ORDER BY scanned_at DESC
""", (hours,)) """, (hours,))
return [InventoryRecord(*row) for row in cur.fetchall()] records = [InventoryRecord(*row) for row in cur.fetchall()]
logger.debug(f"Получено {len(records)} критических товаров за последние {hours} часов")
return records
except Exception as e: except Exception as e:
print(f"Ошибка получения товаров с критическим статусом: {e}") logger.error(f"Ошибка получения товаров с критическим статусом: {e}")
return [] return []
+45 -18
View File
@@ -1,16 +1,15 @@
# db/repositories/product_repository.py
from typing import List, Optional from typing import List, Optional
from loguru import logger
from db.connection import get_connection from db.connection import get_connection
from model.product import Product from model.product import Product
class ProductRepository: class ProductRepository:
def get_all(self) -> List[Product]: def get_all(self) -> List[Product]:
try: try:
with get_connection() as conn: with get_connection() as conn:
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute("SELECT * FROM products ORDER BY id") cur.execute("SELECT * FROM products ORDER BY id")
return [ products = [
Product( Product(
id=row[0], id=row[0],
name=row[1], name=row[1],
@@ -19,8 +18,10 @@ class ProductRepository:
optimal_stock=row[4] optimal_stock=row[4]
) for row in cur.fetchall() ) for row in cur.fetchall()
] ]
logger.debug(f"Получено {len(products)} товаров")
return products
except Exception as e: except Exception as e:
print(f"Ошибка получения продуктов: {e}") logger.error(f"Ошибка получения продуктов: {e}")
return [] return []
def get_by_id(self, product_id: str) -> Optional[Product]: def get_by_id(self, product_id: str) -> Optional[Product]:
@@ -29,9 +30,13 @@ class ProductRepository:
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute("SELECT * FROM products WHERE id = %s", (product_id,)) cur.execute("SELECT * FROM products WHERE id = %s", (product_id,))
row = cur.fetchone() row = cur.fetchone()
return Product(*row) if row else None if row:
logger.debug(f"Товар {product_id} найден")
return Product(*row)
logger.warning(f"Товар {product_id} не найден")
return None
except Exception as e: except Exception as e:
print(f"Ошибка получения товара {product_id}: {e}") logger.error(f"Ошибка получения товара {product_id}: {e}")
return None return None
def get_by_category(self, category: str) -> List[Product]: def get_by_category(self, category: str) -> List[Product]:
@@ -39,9 +44,11 @@ class ProductRepository:
with get_connection() as conn: with get_connection() as conn:
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute("SELECT * FROM products WHERE category = %s ORDER BY name", (category,)) cur.execute("SELECT * FROM products WHERE category = %s ORDER BY name", (category,))
return [Product(*row) for row in cur.fetchall()] products = [Product(*row) for row in cur.fetchall()]
logger.debug(f"Получено {len(products)} товаров категории {category}")
return products
except Exception as e: except Exception as e:
print(f"Ошибка получения товаров по категории {category}: {e}") logger.error(f"Ошибка получения товаров по категории {category}: {e}")
return [] return []
def create_product(self, product_id: str, name: str, category: str, def create_product(self, product_id: str, name: str, category: str,
@@ -54,9 +61,14 @@ class ProductRepository:
VALUES (%s, %s, %s, %s, %s) VALUES (%s, %s, %s, %s, %s)
""", (product_id, name, category, min_stock, optimal_stock)) """, (product_id, name, category, min_stock, optimal_stock))
conn.commit() conn.commit()
return cur.rowcount > 0 success = cur.rowcount > 0
if success:
logger.debug(f"Создан новый товар {product_id}: {name}")
else:
logger.warning(f"Не удалось создать товар {product_id}")
return success
except Exception as e: except Exception as e:
print(f"Ошибка создания товара {product_id}: {e}") logger.error(f"Ошибка создания товара {product_id}: {e}")
return False return False
def update_product(self, product_id: str, name: str = None, category: str = None, def update_product(self, product_id: str, name: str = None, category: str = None,
@@ -84,6 +96,7 @@ class ProductRepository:
params.append(optimal_stock) params.append(optimal_stock)
if not updates: if not updates:
logger.warning(f"Нет данных для обновления товара {product_id}")
return False return False
params.append(product_id) params.append(product_id)
@@ -91,9 +104,14 @@ class ProductRepository:
cur.execute(query, params) cur.execute(query, params)
conn.commit() conn.commit()
return cur.rowcount > 0 success = cur.rowcount > 0
if success:
logger.debug(f"Товар {product_id} успешно обновлен")
else:
logger.warning(f"Товар {product_id} не найден для обновления")
return success
except Exception as e: except Exception as e:
print(f"Ошибка обновления товара {product_id}: {e}") logger.error(f"Ошибка обновления товара {product_id}: {e}")
return False return False
def delete_product(self, product_id: str) -> bool: def delete_product(self, product_id: str) -> bool:
@@ -102,9 +120,14 @@ class ProductRepository:
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute("DELETE FROM products WHERE id = %s", (product_id,)) cur.execute("DELETE FROM products WHERE id = %s", (product_id,))
conn.commit() conn.commit()
return cur.rowcount > 0 success = cur.rowcount > 0
if success:
logger.debug(f"Товар {product_id} удален")
else:
logger.warning(f"Товар {product_id} не найден для удаления")
return success
except Exception as e: except Exception as e:
print(f"Ошибка удаления товара {product_id}: {e}") logger.error(f"Ошибка удаления товара {product_id}: {e}")
return False return False
def search_products(self, search_term: str) -> List[Product]: def search_products(self, search_term: str) -> List[Product]:
@@ -116,9 +139,11 @@ class ProductRepository:
WHERE name ILIKE %s OR id ILIKE %s WHERE name ILIKE %s OR id ILIKE %s
ORDER BY name ORDER BY name
""", (f'%{search_term}%', f'%{search_term}%')) """, (f'%{search_term}%', f'%{search_term}%'))
return [Product(*row) for row in cur.fetchall()] products = [Product(*row) for row in cur.fetchall()]
logger.debug(f"Найдено {len(products)} товаров по запросу '{search_term}'")
return products
except Exception as e: except Exception as e:
print(f"Ошибка поиска товров по названию '{search_term}': {e}") logger.error(f"Ошибка поиска товаров по названию '{search_term}': {e}")
return [] return []
def get_low_stock_products(self) -> List[Product]: def get_low_stock_products(self) -> List[Product]:
@@ -133,7 +158,9 @@ class ProductRepository:
AND ih.scanned_at >= NOW() - INTERVAL '1 day' AND ih.scanned_at >= NOW() - INTERVAL '1 day'
ORDER BY p.name ORDER BY p.name
""") """)
return [Product(*row) for row in cur.fetchall()] products = [Product(*row) for row in cur.fetchall()]
logger.debug(f"Получено {len(products)} товаров с низким запасом")
return products
except Exception as e: except Exception as e:
print(f"Ошибка получения товаров с низким запасом: {e}") logger.error(f"Ошибка получения товаров с низким запасом: {e}")
return [] return []
+59 -20
View File
@@ -1,5 +1,5 @@
# db/repositories/robot_repository.py
from typing import List, Optional from typing import List, Optional
from loguru import logger
from db.connection import get_connection from db.connection import get_connection
from model.robot import Robot from model.robot import Robot
@@ -9,7 +9,7 @@ class RobotRepository:
with get_connection() as conn: with get_connection() as conn:
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute("SELECT * FROM robots ORDER BY id") cur.execute("SELECT * FROM robots ORDER BY id")
return [ robots = [
Robot( Robot(
id=row[0], id=row[0],
status=row[1], status=row[1],
@@ -20,8 +20,10 @@ class RobotRepository:
current_shelf=row[6] current_shelf=row[6]
) for row in cur.fetchall() ) for row in cur.fetchall()
] ]
logger.debug(f"Получено {len(robots)} роботов")
return robots
except Exception as e: except Exception as e:
print(f"Ошика получения всех роботов: {e}") logger.error(f"Ошибка получения всех роботов: {e}")
return [] return []
def get_by_id(self, robot_id: str) -> Optional[Robot]: def get_by_id(self, robot_id: str) -> Optional[Robot]:
@@ -30,13 +32,24 @@ class RobotRepository:
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute("SELECT * FROM robots WHERE id = %s", (robot_id,)) cur.execute("SELECT * FROM robots WHERE id = %s", (robot_id,))
row = cur.fetchone() row = cur.fetchone()
return Robot(*row) if row else None if row:
logger.debug(f"Робот {robot_id} найден")
return Robot(*row)
logger.warning(f"Робот {robot_id} не найден")
return None
except Exception as e: except Exception as e:
print(f"Ошибка получения роботов {robot_id}: {e}") logger.error(f"Ошибка получения робота {robot_id}: {e}")
return None return None
def update_robot(self, robot_id: str, status: str = None, battery_level: int = None, def update_robot(
current_zone: str = None, current_row: int = None, current_shelf: int = None) -> bool: self,
robot_id: str,
status: str = None,
battery_level: int = None,
current_zone: str = None,
current_row: int = None,
current_shelf: int = None
) -> bool:
try: try:
with get_connection() as conn: with get_connection() as conn:
with conn.cursor() as cur: with conn.cursor() as cur:
@@ -68,9 +81,14 @@ class RobotRepository:
cur.execute(query, params) cur.execute(query, params)
conn.commit() conn.commit()
return cur.rowcount > 0 success = cur.rowcount > 0
if success:
logger.debug(f"Робот {robot_id} успешно обновлен")
else:
logger.warning(f"Робот {robot_id} не найден для обновления")
return success
except Exception as e: except Exception as e:
print(f"Ошибка обновления робота {robot_id}: {e}") logger.error(f"Ошибка обновления робота {robot_id}: {e}")
return False return False
def get_robots_by_status(self, status: str) -> List[Robot]: def get_robots_by_status(self, status: str) -> List[Robot]:
@@ -78,9 +96,11 @@ class RobotRepository:
with get_connection() as conn: with get_connection() as conn:
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute("SELECT * FROM robots WHERE status = %s ORDER BY id", (status,)) cur.execute("SELECT * FROM robots WHERE status = %s ORDER BY id", (status,))
return [Robot(*row) for row in cur.fetchall()] robots = [Robot(*row) for row in cur.fetchall()]
logger.debug(f"Получено {len(robots)} роботов со статусом {status}")
return robots
except Exception as e: except Exception as e:
print(f"Ошибка получения роботов по статусу {status}: {e}") logger.error(f"Ошибка получения роботов по статусу {status}: {e}")
return [] return []
def get_low_battery_robots(self, threshold: int = 20) -> List[Robot]: def get_low_battery_robots(self, threshold: int = 20) -> List[Robot]:
@@ -92,9 +112,11 @@ class RobotRepository:
WHERE battery_level < %s AND status = 'active' WHERE battery_level < %s AND status = 'active'
ORDER BY battery_level ASC ORDER BY battery_level ASC
""", (threshold,)) """, (threshold,))
return [Robot(*row) for row in cur.fetchall()] robots = [Robot(*row) for row in cur.fetchall()]
logger.debug(f"Получено {len(robots)} роботов с низким зарядом (<{threshold}%)")
return robots
except Exception as e: except Exception as e:
print(f"Ошибка получения роботов с низким зарядом: {e}") logger.error(f"Ошибка получения роботов с низким зарядом: {e}")
return [] return []
def get_robots_in_zone(self, zone: str) -> List[Robot]: def get_robots_in_zone(self, zone: str) -> List[Robot]:
@@ -102,12 +124,19 @@ class RobotRepository:
with get_connection() as conn: with get_connection() as conn:
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute("SELECT * FROM robots WHERE current_zone = %s ORDER BY id", (zone,)) cur.execute("SELECT * FROM robots WHERE current_zone = %s ORDER BY id", (zone,))
return [Robot(*row) for row in cur.fetchall()] robots = [Robot(*row) for row in cur.fetchall()]
logger.debug(f"Получено {len(robots)} роботов в зоне {zone}")
return robots
except Exception as e: except Exception as e:
print(f"Ошибка получения роботов в зоне {zone}: {e}") logger.error(f"Ошибка получения роботов в зоне {zone}: {e}")
return [] return []
def create_robot(self, robot_id: str, status: str = 'active', battery_level: int = 100) -> bool: def create_robot(
self,
robot_id: str,
status: str = 'active',
battery_level: int = 100
) -> bool:
try: try:
with get_connection() as conn: with get_connection() as conn:
with conn.cursor() as cur: with conn.cursor() as cur:
@@ -116,9 +145,14 @@ class RobotRepository:
VALUES (%s, %s, %s, CURRENT_TIMESTAMP) VALUES (%s, %s, %s, CURRENT_TIMESTAMP)
""", (robot_id, status, battery_level)) """, (robot_id, status, battery_level))
conn.commit() conn.commit()
return cur.rowcount > 0 success = cur.rowcount > 0
if success:
logger.debug(f"Создан новый робот {robot_id}")
else:
logger.warning(f"Не удалось создать робота {robot_id}")
return success
except Exception as e: except Exception as e:
print(f"Ошибка создания робота {robot_id}: {e}") logger.error(f"Ошибка создания робота {robot_id}: {e}")
return False return False
def delete_robot(self, robot_id: str) -> bool: def delete_robot(self, robot_id: str) -> bool:
@@ -127,7 +161,12 @@ class RobotRepository:
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute("DELETE FROM robots WHERE id = %s", (robot_id,)) cur.execute("DELETE FROM robots WHERE id = %s", (robot_id,))
conn.commit() conn.commit()
return cur.rowcount > 0 success = cur.rowcount > 0
if success:
logger.debug(f"Робот {robot_id} удален")
else:
logger.warning(f"Робот {robot_id} не найден для удаления")
return success
except Exception as e: except Exception as e:
print(f"Ошибка удаления робота {robot_id}: {e}") logger.error(f"Ошибка удаления робота {robot_id}: {e}")
return False return False
+171 -99
View File
@@ -1,130 +1,202 @@
from typing import List, Optional from typing import List, Optional
from loguru import logger
from model.user import User from model.user import User
from db.connection import get_connection from db.connection import get_connection
class UserRepository: class UserRepository:
def get_all(self) -> List[User]: def get_all(self) -> List[User]:
with get_connection() as conn: try:
with conn.cursor() as cur: with get_connection() as conn:
cur.execute("SELECT * FROM users ORDER BY id") with conn.cursor() as cur:
return [ cur.execute("SELECT * FROM users ORDER BY id")
User( users = [
id=row[0], User(
email=row[1], id=row[0],
password_hash=row[2], email=row[1],
name=row[3], password_hash=row[2],
role=row[4], name=row[3],
created_at=row[5] role=row[4],
) for row in cur.fetchall() created_at=row[5]
] ) for row in cur.fetchall()
]
logger.debug(f"Получено {len(users)} пользователей")
return users
except Exception as e:
logger.error(f"Ошибка получения пользователей: {e}")
return []
def get_by_id(self, user_id: int) -> Optional[User]: def get_by_id(self, user_id: int) -> Optional[User]:
with get_connection() as conn: try:
with conn.cursor() as cur: with get_connection() as conn:
cur.execute("SELECT * FROM users WHERE id = %s", (user_id,)) with conn.cursor() as cur:
row = cur.fetchone() cur.execute("SELECT * FROM users WHERE id = %s", (user_id,))
if row: row = cur.fetchone()
return User(*row) if row:
return None logger.debug(f"Пользователь {user_id} найден")
return User(*row)
logger.warning(f"Пользователь {user_id} не найден")
return None
except Exception as e:
logger.error(f"Ошибка получения пользователя {user_id}: {e}")
return None
def get_by_email(self, email: str) -> Optional[User]: def get_by_email(self, email: str) -> Optional[User]:
with get_connection() as conn: try:
with conn.cursor() as cur: with get_connection() as conn:
cur.execute("SELECT * FROM users WHERE email = %s", (email,)) with conn.cursor() as cur:
row = cur.fetchone() cur.execute("SELECT * FROM users WHERE email = %s", (email,))
if row: row = cur.fetchone()
return User(*row) if row:
return None logger.debug(f"Пользователь с email {email} найден")
return User(*row)
logger.warning(f"Пользователь с email {email} не найден")
return None
except Exception as e:
logger.error(f"Ошибка получения пользователя по email {email}: {e}")
return None
def create_user(self, email: str, password_hash: str, name: str, role: str) -> Optional[User]: def create_user(
with get_connection() as conn: self,
with conn.cursor() as cur: email: str,
cur.execute(""" password_hash: str,
INSERT INTO users (email, password_hash, name, role) name: str,
VALUES (%s, %s, %s, %s) role: str
RETURNING id, email, password_hash, name, role, created_at ) -> Optional[User]:
""", (email, password_hash, name, role)) try:
with get_connection() as conn:
with conn.cursor() as cur:
cur.execute("""
INSERT INTO users (email, password_hash, name, role)
VALUES (%s, %s, %s, %s)
RETURNING id, email, password_hash, name, role, created_at
""", (email, password_hash, name, role))
row = cur.fetchone() row = cur.fetchone()
conn.commit() conn.commit()
if row: if row:
return User(*row) logger.debug(f"Создан новый пользователь {email} с ID {row[0]}")
return None return User(*row)
logger.warning(f"Не удалось создать пользователя {email}")
return None
except Exception as e:
logger.error(f"Ошибка создания пользователя {email}: {e}")
return None
def update_user(self, user_id: int, name: str = None, role: str = None) -> bool: def update_user(self, user_id: int, name: str = None, role: str = None) -> bool:
with get_connection() as conn: try:
with conn.cursor() as cur: with get_connection() as conn:
updates = [] with conn.cursor() as cur:
params = [] updates = []
params = []
if name is not None: if name is not None:
updates.append("name = %s") updates.append("name = %s")
params.append(name) params.append(name)
if role is not None: if role is not None:
updates.append("role = %s") updates.append("role = %s")
params.append(role) params.append(role)
if not updates: if not updates:
return False logger.warning(f"Нет данных для обновления пользователя {user_id}")
return False
params.append(user_id) params.append(user_id)
query = f"UPDATE users SET {', '.join(updates)} WHERE id = %s" query = f"UPDATE users SET {', '.join(updates)} WHERE id = %s"
cur.execute(query, params) cur.execute(query, params)
conn.commit() conn.commit()
return cur.rowcount > 0 success = cur.rowcount > 0
if success:
logger.debug(f"Пользователь {user_id} успешно обновлен")
else:
logger.warning(f"Пользователь {user_id} не найден для обновления")
return success
except Exception as e:
logger.error(f"Ошибка обновления пользователя {user_id}: {e}")
return False
def delete_user(self, user_id: int) -> bool: def delete_user(self, user_id: int) -> bool:
with get_connection() as conn: try:
with conn.cursor() as cur: with get_connection() as conn:
cur.execute("DELETE FROM users WHERE id = %s", (user_id,)) with conn.cursor() as cur:
conn.commit() cur.execute("DELETE FROM users WHERE id = %s", (user_id,))
return cur.rowcount > 0 conn.commit()
success = cur.rowcount > 0
if success:
logger.debug(f"Пользователь {user_id} удален")
else:
logger.warning(f"Пользователь {user_id} не найден для удаления")
return success
except Exception as e:
logger.error(f"Ошибка удаления пользователя {user_id}: {e}")
return False
def get_users_by_role(self, role: str) -> List[User]: def get_users_by_role(self, role: str) -> List[User]:
with get_connection() as conn: try:
with conn.cursor() as cur: with get_connection() as conn:
cur.execute("SELECT * FROM users WHERE role = %s ORDER BY name", (role,)) with conn.cursor() as cur:
return [ cur.execute("SELECT * FROM users WHERE role = %s ORDER BY name", (role,))
User(*row) for row in cur.fetchall() users = [User(*row) for row in cur.fetchall()]
] logger.debug(f"Получено {len(users)} пользователей с ролью {role}")
return users
except Exception as e:
logger.error(f"Ошибка получения пользователей по роли {role}: {e}")
return []
def change_password(self, user_id: int, new_password_hash: str) -> bool: def change_password(self, user_id: int, new_password_hash: str) -> bool:
with get_connection() as conn: try:
with conn.cursor() as cur: with get_connection() as conn:
cur.execute(""" with conn.cursor() as cur:
UPDATE users cur.execute("""
SET password_hash = %s UPDATE users
WHERE id = %s SET password_hash = %s
""", (new_password_hash, user_id)) WHERE id = %s
conn.commit() """, (new_password_hash, user_id))
return cur.rowcount > 0 conn.commit()
success = cur.rowcount > 0
if success:
logger.debug(f"Пароль пользователя {user_id} изменен")
else:
logger.warning(f"Пользователь {user_id} не найден для смены пароля")
return success
except Exception as e:
logger.error(f"Ошибка смены пароля пользователя {user_id}: {e}")
return False
def authenticate_user(self, email: str, password_hash: str) -> Optional[User]: def authenticate_user(self, email: str, password_hash: str) -> Optional[User]:
with get_connection() as conn: if not self.user_exists(email):
with conn.cursor() as cur: return
cur.execute("""
SELECT * FROM users
WHERE email = %s AND password_hash = %s
""", (email, password_hash))
row = cur.fetchone()
if row:
return User(*row)
return None
def is_valid_authenticate(self, email: str, password_hash: str) -> bool: try:
with get_connection() as conn: with get_connection() as conn:
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute(""" cur.execute("""
SELECT 1 FROM users SELECT * FROM users
WHERE email = %s AND password_hash = %s WHERE email = %s AND password_hash = %s
""", (email, password_hash)) """, (email, password_hash))
return cur.fetchone() is not None row = cur.fetchone()
if row:
logger.debug(f"Успешная аутентификация пользователя {email}")
return User(*row)
logger.warning(f"Неудачная аутентификация пользователя {email}")
return None
except Exception as e:
logger.error(f"Ошибка аутентификации пользователя {email}: {e}")
return None
def user_exists(self, email: str) -> bool: def user_exists(self, email: str) -> bool:
with get_connection() as conn: try:
with conn.cursor() as cur: with get_connection() as conn:
cur.execute("SELECT 1 FROM users WHERE email = %s", (email,)) with conn.cursor() as cur:
return cur.fetchone() is not None cur.execute("SELECT 1 FROM users WHERE email = %s", (email,))
exists = cur.fetchone() is not None
if exists:
logger.debug(f"Пользователь с email {email} существует")
else:
logger.debug(f"Пользователь с email {email} не существует")
return exists
except Exception as e:
logger.error(f"Ошибка проверки существования пользователя {email}: {e}")
return False
+1 -1
View File
@@ -9,4 +9,4 @@ class AIPrediction:
days_until_stockout: int days_until_stockout: int
recommended_order: int recommended_order: int
confidence_score: float confidence_score: float
created_at: datetime created_at: datetime
+1 -1
View File
@@ -11,4 +11,4 @@ class InventoryRecord:
shelf_number: int shelf_number: int
status: str status: str
scanned_at: datetime scanned_at: datetime
created_at: datetime created_at: datetime
+1 -1
View File
@@ -6,4 +6,4 @@ class Product:
name: str name: str
category: str category: str
min_stock: int min_stock: int
optimal_stock: int optimal_stock: int
+1 -1
View File
@@ -10,4 +10,4 @@ class Robot:
last_update: datetime last_update: datetime
current_zone: Optional[str] = None current_zone: Optional[str] = None
current_row: Optional[int] = None current_row: Optional[int] = None
current_shelf: Optional[int] = None current_shelf: Optional[int] = None
-11
View File
@@ -1,6 +1,5 @@
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from utils.token import generateKey
@dataclass @dataclass
@@ -11,13 +10,3 @@ class User:
name: str name: str
role: str role: str
created_at: datetime created_at: datetime
def __init__(self, email: str, passwd: str):
#us = getUsModel() #возвращает словарь
self.id = 1#us['id']
self.name = 'Bob'#us['name']
self.role = 'Backend'#us['role']
self.token = generateKey(email, passwd)
def toJson(self):
return {"user": {"id": self.id, "name": self.name, "role": self.role}, "token": self.token}
+12 -2
View File
@@ -1,8 +1,18 @@
import jwt import jwt
import os import os
from time import time from time import time
from model.user import User
def generateKey(email, passwd): def generateKey(user: User) -> dict:
key = os.getenv('KEY') key = os.getenv('KEY')
encoded = jwt.encode({email: passwd, 'iat': time()}, key, algorithm="HS256") encoded = jwt.encode(
{
'id': user.id,
'name': user.name,
'role': user.role,
'iat': time()
},
key,
algorithm="HS256"
)
return encoded return encoded