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
10 changed files with 248 additions and 116 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 -22
View File
@@ -1,21 +1,29 @@
import psycopg2 import psycopg2
import os import os
import logging
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()
logger = logging.getLogger(__name__)
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
@@ -24,38 +32,34 @@ def get_connection() -> Generator[psycopg2.extensions.connection, None, None]:
conn = None conn = None
try: try:
conn = PSQLConnect() conn = PSQLConnect()
logger.info("Подключение к БД установлено") logger.debug("Контекст подключения к БД открыт")
yield conn yield conn
except psycopg2.OperationalError as e:
logger.error(f"Ошибка подключения к БД: {e}")
raise
except psycopg2.Error as e:
logger.error(f"Ошибка PostgreSQL: {e}")
raise
except Exception as e: except Exception as e:
logger.error(f"Неожиданная ошибка при работе с БД: {e}") logger.error(f"Ошибка в контексте подключения: {e}")
if conn:
conn.rollback()
logger.debug("Откат транзакции выполнен")
raise raise
finally: finally:
if conn: if conn:
try:
conn.close() conn.close()
logger.info("Соединение с БД закрыто") logger.debug("Подключение к БД закрыто")
except Exception as e:
logger.warning(f"Ошибка при закрытии соединения: {e}")
def test_connection() -> bool: def test_connection() -> bool:
try: try:
with get_connection() as conn: with get_connection() as conn:
cur = PSQLCursor(conn) cur = PSQLCursor(conn)
try:
cur.execute("SELECT version();") cur.execute("SELECT version();")
version = cur.fetchone() version = cur.fetchone()
logger.info(f"Версия PostgreSQL: {version[0]}") logger.info(f"Подключение к БД успешно: {version[0]}")
return True
finally:
cur.close() cur.close()
logger.debug("Курсор закрыт") logger.debug("Курсор БД закрыт")
return True
except Exception as e: except Exception as e:
logger.error(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()
+7 -9
View File
@@ -1,11 +1,9 @@
from typing import List, Optional from typing import List, Optional
from datetime import datetime, date from datetime import datetime, date
import logging 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
logger = logging.getLogger(__name__)
class AIPredictionsRepository: class AIPredictionsRepository:
def get_all(self) -> List[AIPrediction]: def get_all(self) -> List[AIPrediction]:
try: try:
@@ -23,7 +21,7 @@ class AIPredictionsRepository:
created_at=row[6] created_at=row[6]
) for row in cur.fetchall() ) for row in cur.fetchall()
] ]
logger.info(f"Получено {len(predictions)} прогнозов") logger.debug(f"Получено {len(predictions)} прогнозов")
return predictions return predictions
except Exception as e: except Exception as e:
logger.error(f"Ошибка получения прогнозов: {e}") logger.error(f"Ошибка получения прогнозов: {e}")
@@ -36,7 +34,7 @@ class AIPredictionsRepository:
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()
if row: if row:
logger.info(f"Прогноз {prediction_id} найден") logger.debug(f"Прогноз {prediction_id} найден")
return AIPrediction(*row) return AIPrediction(*row)
logger.warning(f"Прогноз {prediction_id} не найден") logger.warning(f"Прогноз {prediction_id} не найден")
return None return None
@@ -55,7 +53,7 @@ class AIPredictionsRepository:
LIMIT %s LIMIT %s
""", (product_id, limit)) """, (product_id, limit))
predictions = [AIPrediction(*row) for row in cur.fetchall()] predictions = [AIPrediction(*row) for row in cur.fetchall()]
logger.info(f"Получено {len(predictions)} прогнозов для товара {product_id}") logger.debug(f"Получено {len(predictions)} прогнозов для товара {product_id}")
return predictions return predictions
except Exception as e: except Exception as e:
logger.error(f"Ошибка получения прогноза по товару {product_id}: {e}") logger.error(f"Ошибка получения прогноза по товару {product_id}: {e}")
@@ -71,7 +69,7 @@ class AIPredictionsRepository:
ORDER BY product_id, prediction_date DESC ORDER BY product_id, prediction_date DESC
""") """)
predictions = [AIPrediction(*row) for row in cur.fetchall()] predictions = [AIPrediction(*row) for row in cur.fetchall()]
logger.info(f"Получено {len(predictions)} последних прогнозов по товарам") logger.debug(f"Получено {len(predictions)} последних прогнозов по товарам")
return predictions return predictions
except Exception as e: except Exception as e:
logger.error(f"Ошибка получения последних прогнозов по товарам: {e}") logger.error(f"Ошибка получения последних прогнозов по товарам: {e}")
@@ -97,7 +95,7 @@ class AIPredictionsRepository:
prediction_id = cur.fetchone()[0] prediction_id = cur.fetchone()[0]
conn.commit() conn.commit()
logger.info(f"Создан новый прогноз ID: {prediction_id} для товара {product_id}") logger.debug(f"Создан новый прогноз ID: {prediction_id} для товара {product_id}")
return prediction_id return prediction_id
except Exception as e: except Exception as e:
logger.error(f"Ошибка создания прогноза: {e}") logger.error(f"Ошибка создания прогноза: {e}")
@@ -113,7 +111,7 @@ class AIPredictionsRepository:
""", (older_than_days,)) """, (older_than_days,))
deleted_count = cur.rowcount deleted_count = cur.rowcount
conn.commit() conn.commit()
logger.info(f"Удалено {deleted_count} старых прогнозов старше {older_than_days} дней") logger.debug(f"Удалено {deleted_count} старых прогнозов старше {older_than_days} дней")
return deleted_count return deleted_count
except Exception as e: except Exception as e:
logger.error(f"Ошибка удаления старых прогнозов: {e}") logger.error(f"Ошибка удаления старых прогнозов: {e}")
+9 -11
View File
@@ -1,12 +1,10 @@
# 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
import logging 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
logger = logging.getLogger(__name__)
class InventoryRepository: class InventoryRepository:
def get_all(self) -> List[InventoryRecord]: def get_all(self) -> List[InventoryRecord]:
try: try:
@@ -27,7 +25,7 @@ class InventoryRepository:
created_at=row[9] created_at=row[9]
) for row in cur.fetchall() ) for row in cur.fetchall()
] ]
logger.info(f"Получено {len(records)} записей инвентаризации") logger.debug(f"Получено {len(records)} записей инвентаризации")
return records return records
except Exception as e: except Exception as e:
logger.error(f"Ошибка получения истории инвентаризации: {e}") logger.error(f"Ошибка получения истории инвентаризации: {e}")
@@ -40,7 +38,7 @@ class InventoryRepository:
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()
if row: if row:
logger.info(f"Запись инвентаризации {record_id} найдена") logger.debug(f"Запись инвентаризации {record_id} найдена")
return InventoryRecord(*row) return InventoryRecord(*row)
logger.warning(f"Запись инвентаризации {record_id} не найдена") logger.warning(f"Запись инвентаризации {record_id} не найдена")
return None return None
@@ -71,7 +69,7 @@ class InventoryRepository:
record_id = cur.fetchone()[0] record_id = cur.fetchone()[0]
conn.commit() conn.commit()
logger.info(f"Создана новая запись инвентаризации ID: {record_id}") logger.debug(f"Создана новая запись инвентаризации ID: {record_id}")
return record_id return record_id
except Exception as e: except Exception as e:
logger.error(f"Ошибка создания записи инвентаризации: {e}") logger.error(f"Ошибка создания записи инвентаризации: {e}")
@@ -87,7 +85,7 @@ class InventoryRepository:
ORDER BY product_id, scanned_at DESC ORDER BY product_id, scanned_at DESC
""") """)
records = [InventoryRecord(*row) for row in cur.fetchall()] records = [InventoryRecord(*row) for row in cur.fetchall()]
logger.info(f"Получено {len(records)} последних записей инвентаризации по товарам") logger.debug(f"Получено {len(records)} последних записей инвентаризации по товарам")
return records return records
except Exception as e: except Exception as e:
logger.error(f"Ошибка получения последней записи инвентаризации по каждому товару: {e}") logger.error(f"Ошибка получения последней записи инвентаризации по каждому товару: {e}")
@@ -104,7 +102,7 @@ class InventoryRepository:
LIMIT %s LIMIT %s
""", (product_id, limit)) """, (product_id, limit))
records = [InventoryRecord(*row) for row in cur.fetchall()] records = [InventoryRecord(*row) for row in cur.fetchall()]
logger.info(f"Получено {len(records)} записей инвентаризации для товара {product_id}") logger.debug(f"Получено {len(records)} записей инвентаризации для товара {product_id}")
return records return records
except Exception as e: except Exception as e:
logger.error(f"Ошибка получения записи инвентаризации по продукту {product_id}: {e}") logger.error(f"Ошибка получения записи инвентаризации по продукту {product_id}: {e}")
@@ -121,7 +119,7 @@ class InventoryRepository:
LIMIT %s LIMIT %s
""", (robot_id, limit)) """, (robot_id, limit))
records = [InventoryRecord(*row) for row in cur.fetchall()] records = [InventoryRecord(*row) for row in cur.fetchall()]
logger.info(f"Получено {len(records)} записей инвентаризации для робота {robot_id}") logger.debug(f"Получено {len(records)} записей инвентаризации для робота {robot_id}")
return records return records
except Exception as e: except Exception as e:
logger.error(f"Ошибка получения записи инвентаризации по роботу {robot_id}: {e}") logger.error(f"Ошибка получения записи инвентаризации по роботу {robot_id}: {e}")
@@ -138,7 +136,7 @@ class InventoryRepository:
LIMIT %s LIMIT %s
""", (zone, limit)) """, (zone, limit))
records = [InventoryRecord(*row) for row in cur.fetchall()] records = [InventoryRecord(*row) for row in cur.fetchall()]
logger.info(f"Получено {len(records)} записей инвентаризации для зоны {zone}") logger.debug(f"Получено {len(records)} записей инвентаризации для зоны {zone}")
return records return records
except Exception as e: except Exception as e:
logger.error(f"Ошибка получения записи инвентаризации по зоне {zone}: {e}") logger.error(f"Ошибка получения записи инвентаризации по зоне {zone}: {e}")
@@ -155,7 +153,7 @@ class InventoryRepository:
ORDER BY scanned_at DESC ORDER BY scanned_at DESC
""", (hours,)) """, (hours,))
records = [InventoryRecord(*row) for row in cur.fetchall()] records = [InventoryRecord(*row) for row in cur.fetchall()]
logger.info(f"Получено {len(records)} критических товаров за последние {hours} часов") logger.debug(f"Получено {len(records)} критических товаров за последние {hours} часов")
return records return records
except Exception as e: except Exception as e:
logger.error(f"Ошибка получения товаров с критическим статусом: {e}") logger.error(f"Ошибка получения товаров с критическим статусом: {e}")
+9 -11
View File
@@ -1,10 +1,8 @@
from typing import List, Optional from typing import List, Optional
import logging 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
logger = logging.getLogger(__name__)
class ProductRepository: class ProductRepository:
def get_all(self) -> List[Product]: def get_all(self) -> List[Product]:
try: try:
@@ -20,7 +18,7 @@ class ProductRepository:
optimal_stock=row[4] optimal_stock=row[4]
) for row in cur.fetchall() ) for row in cur.fetchall()
] ]
logger.info(f"Получено {len(products)} товаров") logger.debug(f"Получено {len(products)} товаров")
return products return products
except Exception as e: except Exception as e:
logger.error(f"Ошибка получения продуктов: {e}") logger.error(f"Ошибка получения продуктов: {e}")
@@ -33,7 +31,7 @@ class ProductRepository:
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()
if row: if row:
logger.info(f"Товар {product_id} найден") logger.debug(f"Товар {product_id} найден")
return Product(*row) return Product(*row)
logger.warning(f"Товар {product_id} не найден") logger.warning(f"Товар {product_id} не найден")
return None return None
@@ -47,7 +45,7 @@ class ProductRepository:
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,))
products = [Product(*row) for row in cur.fetchall()] products = [Product(*row) for row in cur.fetchall()]
logger.info(f"Получено {len(products)} товаров категории {category}") logger.debug(f"Получено {len(products)} товаров категории {category}")
return products return products
except Exception as e: except Exception as e:
logger.error(f"Ошибка получения товаров по категории {category}: {e}") logger.error(f"Ошибка получения товаров по категории {category}: {e}")
@@ -65,7 +63,7 @@ class ProductRepository:
conn.commit() conn.commit()
success = cur.rowcount > 0 success = cur.rowcount > 0
if success: if success:
logger.info(f"Создан новый товар {product_id}: {name}") logger.debug(f"Создан новый товар {product_id}: {name}")
else: else:
logger.warning(f"Не удалось создать товар {product_id}") logger.warning(f"Не удалось создать товар {product_id}")
return success return success
@@ -108,7 +106,7 @@ class ProductRepository:
conn.commit() conn.commit()
success = cur.rowcount > 0 success = cur.rowcount > 0
if success: if success:
logger.info(f"Товар {product_id} успешно обновлен") logger.debug(f"Товар {product_id} успешно обновлен")
else: else:
logger.warning(f"Товар {product_id} не найден для обновления") logger.warning(f"Товар {product_id} не найден для обновления")
return success return success
@@ -124,7 +122,7 @@ class ProductRepository:
conn.commit() conn.commit()
success = cur.rowcount > 0 success = cur.rowcount > 0
if success: if success:
logger.info(f"Товар {product_id} удален") logger.debug(f"Товар {product_id} удален")
else: else:
logger.warning(f"Товар {product_id} не найден для удаления") logger.warning(f"Товар {product_id} не найден для удаления")
return success return success
@@ -142,7 +140,7 @@ class ProductRepository:
ORDER BY name ORDER BY name
""", (f'%{search_term}%', f'%{search_term}%')) """, (f'%{search_term}%', f'%{search_term}%'))
products = [Product(*row) for row in cur.fetchall()] products = [Product(*row) for row in cur.fetchall()]
logger.info(f"Найдено {len(products)} товаров по запросу '{search_term}'") logger.debug(f"Найдено {len(products)} товаров по запросу '{search_term}'")
return products return products
except Exception as e: except Exception as e:
logger.error(f"Ошибка поиска товаров по названию '{search_term}': {e}") logger.error(f"Ошибка поиска товаров по названию '{search_term}': {e}")
@@ -161,7 +159,7 @@ class ProductRepository:
ORDER BY p.name ORDER BY p.name
""") """)
products = [Product(*row) for row in cur.fetchall()] products = [Product(*row) for row in cur.fetchall()]
logger.info(f"Получено {len(products)} товаров с низким запасом") logger.debug(f"Получено {len(products)} товаров с низким запасом")
return products return products
except Exception as e: except Exception as e:
logger.error(f"Ошибка получения товаров с низким запасом: {e}") logger.error(f"Ошибка получения товаров с низким запасом: {e}")
+9 -11
View File
@@ -1,10 +1,8 @@
from typing import List, Optional from typing import List, Optional
import logging 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
logger = logging.getLogger(__name__)
class RobotRepository: class RobotRepository:
def get_all(self) -> List[Robot]: def get_all(self) -> List[Robot]:
try: try:
@@ -22,7 +20,7 @@ class RobotRepository:
current_shelf=row[6] current_shelf=row[6]
) for row in cur.fetchall() ) for row in cur.fetchall()
] ]
logger.info(f"Получено {len(robots)} роботов") logger.debug(f"Получено {len(robots)} роботов")
return robots return robots
except Exception as e: except Exception as e:
logger.error(f"Ошибка получения всех роботов: {e}") logger.error(f"Ошибка получения всех роботов: {e}")
@@ -35,7 +33,7 @@ class RobotRepository:
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()
if row: if row:
logger.info(f"Робот {robot_id} найден") logger.debug(f"Робот {robot_id} найден")
return Robot(*row) return Robot(*row)
logger.warning(f"Робот {robot_id} не найден") logger.warning(f"Робот {robot_id} не найден")
return None return None
@@ -85,7 +83,7 @@ class RobotRepository:
conn.commit() conn.commit()
success = cur.rowcount > 0 success = cur.rowcount > 0
if success: if success:
logger.info(f"Робот {robot_id} успешно обновлен") logger.debug(f"Робот {robot_id} успешно обновлен")
else: else:
logger.warning(f"Робот {robot_id} не найден для обновления") logger.warning(f"Робот {robot_id} не найден для обновления")
return success return success
@@ -99,7 +97,7 @@ class RobotRepository:
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,))
robots = [Robot(*row) for row in cur.fetchall()] robots = [Robot(*row) for row in cur.fetchall()]
logger.info(f"Получено {len(robots)} роботов со статусом {status}") logger.debug(f"Получено {len(robots)} роботов со статусом {status}")
return robots return robots
except Exception as e: except Exception as e:
logger.error(f"Ошибка получения роботов по статусу {status}: {e}") logger.error(f"Ошибка получения роботов по статусу {status}: {e}")
@@ -115,7 +113,7 @@ class RobotRepository:
ORDER BY battery_level ASC ORDER BY battery_level ASC
""", (threshold,)) """, (threshold,))
robots = [Robot(*row) for row in cur.fetchall()] robots = [Robot(*row) for row in cur.fetchall()]
logger.info(f"Получено {len(robots)} роботов с низким зарядом (<{threshold}%)") logger.debug(f"Получено {len(robots)} роботов с низким зарядом (<{threshold}%)")
return robots return robots
except Exception as e: except Exception as e:
logger.error(f"Ошибка получения роботов с низким зарядом: {e}") logger.error(f"Ошибка получения роботов с низким зарядом: {e}")
@@ -127,7 +125,7 @@ class RobotRepository:
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,))
robots = [Robot(*row) for row in cur.fetchall()] robots = [Robot(*row) for row in cur.fetchall()]
logger.info(f"Получено {len(robots)} роботов в зоне {zone}") logger.debug(f"Получено {len(robots)} роботов в зоне {zone}")
return robots return robots
except Exception as e: except Exception as e:
logger.error(f"Ошибка получения роботов в зоне {zone}: {e}") logger.error(f"Ошибка получения роботов в зоне {zone}: {e}")
@@ -149,7 +147,7 @@ class RobotRepository:
conn.commit() conn.commit()
success = cur.rowcount > 0 success = cur.rowcount > 0
if success: if success:
logger.info(f"Создан новый робот {robot_id}") logger.debug(f"Создан новый робот {robot_id}")
else: else:
logger.warning(f"Не удалось создать робота {robot_id}") logger.warning(f"Не удалось создать робота {robot_id}")
return success return success
@@ -165,7 +163,7 @@ class RobotRepository:
conn.commit() conn.commit()
success = cur.rowcount > 0 success = cur.rowcount > 0
if success: if success:
logger.info(f"Робот {robot_id} удален") logger.debug(f"Робот {robot_id} удален")
else: else:
logger.warning(f"Робот {robot_id} не найден для удаления") logger.warning(f"Робот {robot_id} не найден для удаления")
return success return success
+15 -32
View File
@@ -1,10 +1,8 @@
from typing import List, Optional from typing import List, Optional
import logging 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
logger = logging.getLogger(__name__)
class UserRepository: class UserRepository:
def get_all(self) -> List[User]: def get_all(self) -> List[User]:
try: try:
@@ -21,7 +19,7 @@ class UserRepository:
created_at=row[5] created_at=row[5]
) for row in cur.fetchall() ) for row in cur.fetchall()
] ]
logger.info(f"Получено {len(users)} пользователей") logger.debug(f"Получено {len(users)} пользователей")
return users return users
except Exception as e: except Exception as e:
logger.error(f"Ошибка получения пользователей: {e}") logger.error(f"Ошибка получения пользователей: {e}")
@@ -34,7 +32,7 @@ class UserRepository:
cur.execute("SELECT * FROM users WHERE id = %s", (user_id,)) cur.execute("SELECT * FROM users WHERE id = %s", (user_id,))
row = cur.fetchone() row = cur.fetchone()
if row: if row:
logger.info(f"Пользователь {user_id} найден") logger.debug(f"Пользователь {user_id} найден")
return User(*row) return User(*row)
logger.warning(f"Пользователь {user_id} не найден") logger.warning(f"Пользователь {user_id} не найден")
return None return None
@@ -49,7 +47,7 @@ class UserRepository:
cur.execute("SELECT * FROM users WHERE email = %s", (email,)) cur.execute("SELECT * FROM users WHERE email = %s", (email,))
row = cur.fetchone() row = cur.fetchone()
if row: if row:
logger.info(f"Пользователь с email {email} найден") logger.debug(f"Пользователь с email {email} найден")
return User(*row) return User(*row)
logger.warning(f"Пользователь с email {email} не найден") logger.warning(f"Пользователь с email {email} не найден")
return None return None
@@ -77,7 +75,7 @@ class UserRepository:
conn.commit() conn.commit()
if row: if row:
logger.info(f"Создан новый пользователь {email} с ID {row[0]}") logger.debug(f"Создан новый пользователь {email} с ID {row[0]}")
return User(*row) return User(*row)
logger.warning(f"Не удалось создать пользователя {email}") logger.warning(f"Не удалось создать пользователя {email}")
return None return None
@@ -111,7 +109,7 @@ class UserRepository:
conn.commit() conn.commit()
success = cur.rowcount > 0 success = cur.rowcount > 0
if success: if success:
logger.info(f"Пользователь {user_id} успешно обновлен") logger.debug(f"Пользователь {user_id} успешно обновлен")
else: else:
logger.warning(f"Пользователь {user_id} не найден для обновления") logger.warning(f"Пользователь {user_id} не найден для обновления")
return success return success
@@ -127,7 +125,7 @@ class UserRepository:
conn.commit() conn.commit()
success = cur.rowcount > 0 success = cur.rowcount > 0
if success: if success:
logger.info(f"Пользователь {user_id} удален") logger.debug(f"Пользователь {user_id} удален")
else: else:
logger.warning(f"Пользователь {user_id} не найден для удаления") logger.warning(f"Пользователь {user_id} не найден для удаления")
return success return success
@@ -141,7 +139,7 @@ class UserRepository:
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute("SELECT * FROM users WHERE role = %s ORDER BY name", (role,)) cur.execute("SELECT * FROM users WHERE role = %s ORDER BY name", (role,))
users = [User(*row) for row in cur.fetchall()] users = [User(*row) for row in cur.fetchall()]
logger.info(f"Получено {len(users)} пользователей с ролью {role}") logger.debug(f"Получено {len(users)} пользователей с ролью {role}")
return users return users
except Exception as e: except Exception as e:
logger.error(f"Ошибка получения пользователей по роли {role}: {e}") logger.error(f"Ошибка получения пользователей по роли {role}: {e}")
@@ -159,7 +157,7 @@ class UserRepository:
conn.commit() conn.commit()
success = cur.rowcount > 0 success = cur.rowcount > 0
if success: if success:
logger.info(f"Пароль пользователя {user_id} изменен") logger.debug(f"Пароль пользователя {user_id} изменен")
else: else:
logger.warning(f"Пользователь {user_id} не найден для смены пароля") logger.warning(f"Пользователь {user_id} не найден для смены пароля")
return success return success
@@ -168,6 +166,9 @@ class UserRepository:
return False return False
def authenticate_user(self, email: str, password_hash: str) -> Optional[User]: def authenticate_user(self, email: str, password_hash: str) -> Optional[User]:
if not self.user_exists(email):
return
try: try:
with get_connection() as conn: with get_connection() as conn:
with conn.cursor() as cur: with conn.cursor() as cur:
@@ -177,7 +178,7 @@ class UserRepository:
""", (email, password_hash)) """, (email, password_hash))
row = cur.fetchone() row = cur.fetchone()
if row: if row:
logger.info(f"Успешная аутентификация пользователя {email}") logger.debug(f"Успешная аутентификация пользователя {email}")
return User(*row) return User(*row)
logger.warning(f"Неудачная аутентификация пользователя {email}") logger.warning(f"Неудачная аутентификация пользователя {email}")
return None return None
@@ -185,24 +186,6 @@ class UserRepository:
logger.error(f"Ошибка аутентификации пользователя {email}: {e}") logger.error(f"Ошибка аутентификации пользователя {email}: {e}")
return None return None
def is_valid_authenticate(self, email: str, password_hash: str) -> bool:
try:
with get_connection() as conn:
with conn.cursor() as cur:
cur.execute("""
SELECT 1 FROM users
WHERE email = %s AND password_hash = %s
""", (email, password_hash))
is_valid = cur.fetchone() is not None
if is_valid:
logger.info(f"Валидные учетные данные для пользователя {email}")
else:
logger.warning(f"Невалидные учетные данные для пользователя {email}")
return is_valid
except Exception as e:
logger.error(f"Ошибка проверки учетных данных пользователя {email}: {e}")
return False
def user_exists(self, email: str) -> bool: def user_exists(self, email: str) -> bool:
try: try:
with get_connection() as conn: with get_connection() as conn:
@@ -210,9 +193,9 @@ class UserRepository:
cur.execute("SELECT 1 FROM users WHERE email = %s", (email,)) cur.execute("SELECT 1 FROM users WHERE email = %s", (email,))
exists = cur.fetchone() is not None exists = cur.fetchone() is not None
if exists: if exists:
logger.info(f"Пользователь с email {email} существует") logger.debug(f"Пользователь с email {email} существует")
else: else:
logger.info(f"Пользователь с email {email} не существует") logger.debug(f"Пользователь с email {email} не существует")
return exists return exists
except Exception as e: except Exception as e:
logger.error(f"Ошибка проверки существования пользователя {email}: {e}") logger.error(f"Ошибка проверки существования пользователя {email}: {e}")
-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