Mtextile_bot / database.py
Deploy Bot
Initial commit of V1.0 - Features Complete
c9f1a26
"""
MTXtyle - Database operatsiyalari (SQLite + aiosqlite)
"""
import aiosqlite
import time
from config import DATABASE_PATH, CATEGORIES
async def init_db():
"""Jadvallarni yaratish"""
async with aiosqlite.connect(DATABASE_PATH) as db:
# Kategoriyalar jadvali
await db.execute("""
CREATE TABLE IF NOT EXISTS categories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
slug TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
emoji TEXT DEFAULT ''
)
""")
# Mahsulotlar jadvali
await db.execute("""
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT DEFAULT '',
price INTEGER NOT NULL,
category_id INTEGER,
photo_id TEXT DEFAULT '',
in_stock INTEGER DEFAULT 1,
created_at REAL DEFAULT 0,
FOREIGN KEY (category_id) REFERENCES categories(id)
)
""")
# Savat jadvali
await db.execute("""
CREATE TABLE IF NOT EXISTS cart_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
quantity INTEGER DEFAULT 1,
FOREIGN KEY (product_id) REFERENCES products(id)
)
""")
# Buyurtmalar jadvali
await db.execute("""
CREATE TABLE IF NOT EXISTS orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
full_name TEXT NOT NULL,
phone TEXT NOT NULL,
address TEXT NOT NULL,
total_price INTEGER DEFAULT 0,
status TEXT DEFAULT 'yangi',
created_at REAL DEFAULT 0
)
""")
# Buyurtma elementlari
await db.execute("""
CREATE TABLE IF NOT EXISTS order_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
product_name TEXT NOT NULL,
quantity INTEGER DEFAULT 1,
price INTEGER DEFAULT 0,
FOREIGN KEY (order_id) REFERENCES orders(id),
FOREIGN KEY (product_id) REFERENCES products(id)
)
""")
# Users table for broadcast
await db.execute("""
CREATE TABLE IF NOT EXISTS users (
user_id INTEGER PRIMARY KEY,
username TEXT,
full_name TEXT,
joined_at REAL
)
""")
await db.commit()
# Default kategoriyalarni qo'shish
for slug, info in CATEGORIES.items():
try:
await db.execute(
"INSERT OR IGNORE INTO categories (slug, name, emoji) VALUES (?, ?, ?)",
(slug, info["name"], info["emoji"])
)
except Exception:
pass
await db.commit()
# ==================== KATEGORIYALAR ====================
async def get_categories():
"""Barcha kategoriyalarni olish"""
async with aiosqlite.connect(DATABASE_PATH) as db:
db.row_factory = aiosqlite.Row
cursor = await db.execute("SELECT * FROM categories ORDER BY id")
rows = await cursor.fetchall()
return [dict(row) for row in rows]
async def get_category_by_slug(slug):
"""Slug bo'yicha kategoriya olish"""
async with aiosqlite.connect(DATABASE_PATH) as db:
db.row_factory = aiosqlite.Row
cursor = await db.execute("SELECT * FROM categories WHERE slug = ?", (slug,))
row = await cursor.fetchone()
return dict(row) if row else None
async def get_category_by_id(category_id):
"""ID bo'yicha kategoriya olish"""
async with aiosqlite.connect(DATABASE_PATH) as db:
db.row_factory = aiosqlite.Row
cursor = await db.execute("SELECT * FROM categories WHERE id = ?", (category_id,))
row = await cursor.fetchone()
return dict(row) if row else None
# ==================== MAHSULOTLAR ====================
async def add_product(name, description, price, category_id, photo_id=""):
"""Yangi mahsulot qo'shish"""
async with aiosqlite.connect(DATABASE_PATH) as db:
cursor = await db.execute(
"""INSERT INTO products (name, description, price, category_id, photo_id, created_at)
VALUES (?, ?, ?, ?, ?, ?)""",
(name, description, price, category_id, photo_id, time.time())
)
await db.commit()
return cursor.lastrowid
async def get_products_by_category(category_id, page=0, per_page=5):
"""Kategoriya bo'yicha mahsulotlarni olish (pagination bilan)"""
async with aiosqlite.connect(DATABASE_PATH) as db:
db.row_factory = aiosqlite.Row
offset = page * per_page
# Umumiy sonni olish
cursor = await db.execute(
"SELECT COUNT(*) as cnt FROM products WHERE category_id = ? AND in_stock = 1",
(category_id,)
)
row = await cursor.fetchone()
total = row["cnt"]
# Mahsulotlarni olish
cursor = await db.execute(
"""SELECT * FROM products WHERE category_id = ? AND in_stock = 1
ORDER BY created_at DESC LIMIT ? OFFSET ?""",
(category_id, per_page, offset)
)
rows = await cursor.fetchall()
products = [dict(r) for r in rows]
return products, total
async def get_product_by_id(product_id):
"""ID bo'yicha mahsulot olish"""
async with aiosqlite.connect(DATABASE_PATH) as db:
db.row_factory = aiosqlite.Row
cursor = await db.execute("SELECT * FROM products WHERE id = ?", (product_id,))
row = await cursor.fetchone()
return dict(row) if row else None
async def get_all_products():
"""Barcha mahsulotlarni olish"""
async with aiosqlite.connect(DATABASE_PATH) as db:
db.row_factory = aiosqlite.Row
cursor = await db.execute(
"SELECT p.*, c.name as category_name FROM products p LEFT JOIN categories c ON p.category_id = c.id ORDER BY p.created_at DESC"
)
rows = await cursor.fetchall()
return [dict(r) for r in rows]
async def update_product(product_id, **kwargs):
"""Mahsulotni yangilash"""
allowed_fields = {"name", "description", "price", "category_id", "photo_id", "in_stock"}
fields = {k: v for k, v in kwargs.items() if k in allowed_fields}
if not fields:
return False
set_clause = ", ".join(f"{k} = ?" for k in fields)
values = list(fields.values()) + [product_id]
async with aiosqlite.connect(DATABASE_PATH) as db:
await db.execute(f"UPDATE products SET {set_clause} WHERE id = ?", values)
await db.commit()
return True
async def delete_product(product_id):
"""Mahsulotni o'chirish"""
async with aiosqlite.connect(DATABASE_PATH) as db:
await db.execute("DELETE FROM products WHERE id = ?", (product_id,))
await db.commit()
async def search_products(query):
"""Mahsulotlarni qidirish"""
async with aiosqlite.connect(DATABASE_PATH) as db:
db.row_factory = aiosqlite.Row
cursor = await db.execute(
"SELECT * FROM products WHERE in_stock = 1 AND (name LIKE ? OR description LIKE ?) ORDER BY created_at DESC",
(f"%{query}%", f"%{query}%")
)
rows = await cursor.fetchall()
return [dict(r) for r in rows]
# ==================== SAVAT (CART) ====================
async def add_to_cart(user_id, product_id, quantity=1):
"""Savatga mahsulot qo'shish"""
async with aiosqlite.connect(DATABASE_PATH) as db:
# Mavjudligini tekshirish
cursor = await db.execute(
"SELECT * FROM cart_items WHERE user_id = ? AND product_id = ?",
(user_id, product_id)
)
existing = await cursor.fetchone()
if existing:
new_qty = existing[3] + quantity
if new_qty <= 0:
await db.execute(
"DELETE FROM cart_items WHERE user_id = ? AND product_id = ?",
(user_id, product_id)
)
else:
await db.execute(
"UPDATE cart_items SET quantity = ? WHERE user_id = ? AND product_id = ?",
(new_qty, user_id, product_id)
)
else:
if quantity > 0:
await db.execute(
"INSERT INTO cart_items (user_id, product_id, quantity) VALUES (?, ?, ?)",
(user_id, product_id, quantity)
)
await db.commit()
async def get_cart(user_id):
"""Foydalanuvchi savatini olish"""
async with aiosqlite.connect(DATABASE_PATH) as db:
db.row_factory = aiosqlite.Row
cursor = await db.execute(
"""SELECT ci.*, p.name, p.price, p.photo_id
FROM cart_items ci
JOIN products p ON ci.product_id = p.id
WHERE ci.user_id = ?""",
(user_id,)
)
rows = await cursor.fetchall()
return [dict(r) for r in rows]
async def get_cart_total(user_id):
"""Savat umumiy summasini olish"""
async with aiosqlite.connect(DATABASE_PATH) as db:
cursor = await db.execute(
"""SELECT COALESCE(SUM(ci.quantity * p.price), 0) as total
FROM cart_items ci
JOIN products p ON ci.product_id = p.id
WHERE ci.user_id = ?""",
(user_id,)
)
row = await cursor.fetchone()
return row[0]
async def update_cart_quantity(user_id, product_id, quantity):
"""Savatdagi mahsulot miqdorini o'zgartirish"""
async with aiosqlite.connect(DATABASE_PATH) as db:
if quantity <= 0:
await db.execute(
"DELETE FROM cart_items WHERE user_id = ? AND product_id = ?",
(user_id, product_id)
)
else:
await db.execute(
"UPDATE cart_items SET quantity = ? WHERE user_id = ? AND product_id = ?",
(quantity, user_id, product_id)
)
await db.commit()
async def remove_from_cart(user_id, product_id):
"""Savatdan mahsulotni o'chirish"""
async with aiosqlite.connect(DATABASE_PATH) as db:
await db.execute(
"DELETE FROM cart_items WHERE user_id = ? AND product_id = ?",
(user_id, product_id)
)
await db.commit()
async def clear_cart(user_id):
"""Savatni tozalash"""
async with aiosqlite.connect(DATABASE_PATH) as db:
await db.execute("DELETE FROM cart_items WHERE user_id = ?", (user_id,))
await db.commit()
# ==================== USERS & BROADCAST ====================
async def add_user(user_id, username, full_name):
"""Yangi foydalanuvchini qo'shish"""
async with aiosqlite.connect(DATABASE_PATH) as db:
await db.execute(
"INSERT OR IGNORE INTO users (user_id, username, full_name, joined_at) VALUES (?, ?, ?, ?)",
(user_id, username, full_name, time.time())
)
await db.commit()
async def get_all_users():
"""Barcha foydalanuvchilarni olish"""
async with aiosqlite.connect(DATABASE_PATH) as db:
db.row_factory = aiosqlite.Row
cursor = await db.execute("SELECT user_id FROM users")
rows = await cursor.fetchall()
return [row["user_id"] for row in rows]
async def get_all_orders_with_items():
"""Excel export uchun barcha buyurtmalar va mahsulotlar"""
async with aiosqlite.connect(DATABASE_PATH) as db:
db.row_factory = aiosqlite.Row
cursor = await db.execute("""
SELECT o.id, o.user_id, o.full_name, o.phone, o.address, o.total_price, o.status, o.created_at,
oi.product_name, oi.quantity, oi.price
FROM orders o
JOIN order_items oi ON o.id = oi.order_id
ORDER BY o.created_at DESC
""")
rows = await cursor.fetchall()
return [dict(r) for r in rows]
# ==================== BUYURTMALAR ====================
async def create_order(user_id, full_name, phone, address):
"""Yangi buyurtma yaratish"""
cart = await get_cart(user_id)
if not cart:
return None
total = await get_cart_total(user_id)
async with aiosqlite.connect(DATABASE_PATH) as db:
cursor = await db.execute(
"""INSERT INTO orders (user_id, full_name, phone, address, total_price, status, created_at)
VALUES (?, ?, ?, ?, ?, 'yangi', ?)""",
(user_id, full_name, phone, address, total, time.time())
)
order_id = cursor.lastrowid
# Savat elementlarini buyurtmaga ko'chirish
for item in cart:
await db.execute(
"""INSERT INTO order_items (order_id, product_id, product_name, quantity, price)
VALUES (?, ?, ?, ?, ?)""",
(order_id, item["product_id"], item["name"], item["quantity"], item["price"])
)
await db.commit()
# Savatni tozalash
await clear_cart(user_id)
return order_id
async def get_order(order_id):
"""Buyurtmani olish"""
async with aiosqlite.connect(DATABASE_PATH) as db:
db.row_factory = aiosqlite.Row
cursor = await db.execute("SELECT * FROM orders WHERE id = ?", (order_id,))
row = await cursor.fetchone()
return dict(row) if row else None
async def get_order_items(order_id):
"""Buyurtma elementlarini olish"""
async with aiosqlite.connect(DATABASE_PATH) as db:
db.row_factory = aiosqlite.Row
cursor = await db.execute(
"SELECT * FROM order_items WHERE order_id = ?", (order_id,)
)
rows = await cursor.fetchall()
return [dict(r) for r in rows]
async def get_user_orders(user_id):
"""Foydalanuvchi buyurtmalarini olish"""
async with aiosqlite.connect(DATABASE_PATH) as db:
db.row_factory = aiosqlite.Row
cursor = await db.execute(
"SELECT * FROM orders WHERE user_id = ? ORDER BY created_at DESC",
(user_id,)
)
rows = await cursor.fetchall()
return [dict(r) for r in rows]
async def update_order_status(order_id, status):
"""Buyurtma statusini o'zgartirish"""
async with aiosqlite.connect(DATABASE_PATH) as db:
await db.execute(
"UPDATE orders SET status = ? WHERE id = ?",
(status, order_id)
)
await db.commit()
async def get_all_orders(status=None):
"""Barcha buyurtmalarni olish (admin uchun)"""
async with aiosqlite.connect(DATABASE_PATH) as db:
db.row_factory = aiosqlite.Row
if status:
cursor = await db.execute(
"SELECT * FROM orders WHERE status = ? ORDER BY created_at DESC",
(status,)
)
else:
cursor = await db.execute(
"SELECT * FROM orders ORDER BY created_at DESC LIMIT 50"
)
rows = await cursor.fetchall()
return [dict(r) for r in rows]
# ==================== STATISTIKA ====================
async def get_stats():
"""Umumiy statistika (admin uchun)"""
async with aiosqlite.connect(DATABASE_PATH) as db:
stats = {}
cursor = await db.execute("SELECT COUNT(*) FROM products WHERE in_stock = 1")
row = await cursor.fetchone()
stats["products_count"] = row[0]
cursor = await db.execute("SELECT COUNT(*) FROM orders")
row = await cursor.fetchone()
stats["orders_count"] = row[0]
cursor = await db.execute("SELECT COUNT(*) FROM orders WHERE status = 'yangi'")
row = await cursor.fetchone()
stats["new_orders"] = row[0]
cursor = await db.execute(
"SELECT COALESCE(SUM(total_price), 0) FROM orders WHERE status != 'bekor_qilindi'"
)
row = await cursor.fetchone()
stats["total_revenue"] = row[0]
cursor = await db.execute("SELECT COUNT(DISTINCT user_id) FROM orders")
row = await cursor.fetchone()
stats["unique_customers"] = row[0]
return stats