Mark-Lasfar commited on
Commit
1a47e08
·
1 Parent(s): fb47c5d

fix import

Browse files
Files changed (2) hide show
  1. api/database.py +126 -74
  2. create_dummy_user.py +7 -29
api/database.py CHANGED
@@ -1,94 +1,146 @@
1
- # init_db.py
2
  # SPDX-FileCopyrightText: Hadad <hadad@linuxmail.org>
3
  # SPDX-License-License: Apache-2.0
4
 
5
  import os
6
  import logging
7
- import asyncio
8
- from sqlalchemy.ext.asyncio import AsyncSession
9
- from sqlalchemy import select, delete
10
- from api.database import async_engine, Base, User, OAuthAccount, Conversation, Message, AsyncSessionLocal
11
- from passlib.context import CryptContext
 
 
 
 
 
 
12
 
13
  # إعداد اللوج
14
- logging.basicConfig(level=logging.INFO)
15
  logger = logging.getLogger(__name__)
16
 
17
- # إعداد تشفير كلمة المرور
18
- pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
 
 
19
 
20
- async def init_db():
21
- logger.info("Starting database initialization...")
 
22
 
23
- # إنشاء الجداول
24
- try:
25
- async with async_engine.begin() as conn:
26
- await conn.run_sync(Base.metadata.create_all)
27
- logger.info("Database tables created successfully.")
28
- except Exception as e:
29
- logger.error(f"Error creating database tables: {e}")
30
- raise
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
- # تنظيف البيانات غير المتسقة
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  async with AsyncSessionLocal() as session:
34
  try:
35
- # حذف سجلات oauth_accounts اللي مش مرتبطة بمستخدم موجود
36
- stmt = delete(OAuthAccount).where(
37
- OAuthAccount.user_id.notin_(select(User.id))
38
- )
39
- result = await session.execute(stmt)
40
- deleted_count = result.rowcount
41
- await session.commit()
42
- logger.info(f"Deleted {deleted_count} orphaned OAuth accounts.")
43
-
44
- # التأكد من إن كل المستخدمين ليهم is_active=True
45
- users = (await session.execute(select(User))).scalars().all()
46
- for user in users:
47
- if not user.is_active:
48
- user.is_active = True
49
- logger.info(f"Updated user {user.email} to is_active=True")
50
- await session.commit()
51
-
52
- # اختبار إنشاء مستخدم ومحادثة (اختياري)
53
- test_user = (await session.execute(
54
- select(User).filter_by(email="test@example.com")
55
- )).scalar_one_or_none()
56
- if not test_user:
57
- test_user = User(
58
- email="test@example.com",
59
- hashed_password=pwd_context.hash("testpassword123"),
60
- is_active=True,
61
- display_name="Test User"
62
- )
63
- session.add(test_user)
64
- await session.commit()
65
- logger.info("Test user created successfully.")
66
-
67
- test_conversation = (await session.execute(
68
- select(Conversation).filter_by(user_id=test_user.id)
69
- )).scalar_one_or_none()
70
- if not test_conversation:
71
- test_conversation = Conversation(
72
- conversation_id="test-conversation-1",
73
- user_id=test_user.id,
74
- title="Test Conversation"
75
- )
76
- session.add(test_conversation)
77
- await session.commit()
78
- logger.info("Test conversation created successfully.")
79
-
80
- except Exception as e:
81
- await session.rollback()
82
- logger.error(f"Error during initialization: {e}")
83
- raise
84
  finally:
85
  await session.close()
86
 
87
- logger.info("Database initialization completed.")
 
 
88
 
89
- if __name__ == "__main__":
 
90
  try:
91
- asyncio.run(init_db())
 
 
92
  except Exception as e:
93
- logger.error(f"Failed to initialize database: {e}")
94
- raise
 
1
+ # api/database.py
2
  # SPDX-FileCopyrightText: Hadad <hadad@linuxmail.org>
3
  # SPDX-License-License: Apache-2.0
4
 
5
  import os
6
  import logging
7
+ from datetime import datetime
8
+ from typing import AsyncGenerator, Optional, Dict, Any
9
+
10
+ from sqlalchemy import Column, String, Integer, ForeignKey, DateTime, Boolean, Text, select
11
+ from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
12
+ from sqlalchemy.ext.declarative import declarative_base
13
+ from sqlalchemy.orm import relationship
14
+ from fastapi import Depends
15
+ from fastapi_users.db import SQLAlchemyUserDatabase
16
+ from fastapi_users_db_sqlalchemy import SQLAlchemyBaseUserTable # استيراد الصحيح
17
+ import aiosqlite
18
 
19
  # إعداد اللوج
 
20
  logger = logging.getLogger(__name__)
21
 
22
+ # استخدم القيمة مباشرة إذا لم يكن هناك متغير بيئة
23
+ SQLALCHEMY_DATABASE_URL = os.environ.get(
24
+ "SQLALCHEMY_DATABASE_URL"
25
+ ) or "sqlite+aiosqlite:///./data/mgzon_users.db"
26
 
27
+ # تأكد أن الدرايفر async
28
+ if "aiosqlite" not in SQLALCHEMY_DATABASE_URL:
29
+ raise ValueError("Database URL must use 'sqlite+aiosqlite' for async support")
30
 
31
+ # إنشاء محرك async
32
+ async_engine = create_async_engine(
33
+ SQLALCHEMY_DATABASE_URL,
34
+ echo=True,
35
+ connect_args={"check_same_thread": False}
36
+ )
37
+
38
+ # إعداد الجلسة async
39
+ AsyncSessionLocal = async_sessionmaker(
40
+ async_engine,
41
+ expire_on_commit=False,
42
+ class_=AsyncSession
43
+ )
44
+
45
+ # القاعدة الأساسية للنماذج
46
+ Base = declarative_base()
47
+
48
+ # النماذج (Models)
49
+ class OAuthAccount(Base):
50
+ __tablename__ = "oauth_account"
51
+ id = Column(Integer, primary_key=True, index=True)
52
+ user_id = Column(Integer, ForeignKey("user.id"), nullable=False)
53
+ oauth_name = Column(String, nullable=False)
54
+ access_token = Column(String, nullable=False)
55
+ expires_at = Column(Integer, nullable=True)
56
+ refresh_token = Column(String, nullable=True)
57
+ account_id = Column(String, index=True, nullable=False)
58
+ account_email = Column(String, nullable=False)
59
+
60
+ user = relationship("User", back_populates="oauth_accounts", lazy="selectin")
61
+
62
+ class User(SQLAlchemyBaseUserTable, Base): # بدون [int] في الإصدار 10.4.2
63
+ __tablename__ = "user"
64
+ id = Column(Integer, primary_key=True, index=True)
65
+ email = Column(String, unique=True, index=True, nullable=False)
66
+ hashed_password = Column(String, nullable=False)
67
+ is_active = Column(Boolean, default=True)
68
+ is_superuser = Column(Boolean, default=False)
69
+ is_verified = Column(Boolean, default=False)
70
+ display_name = Column(String, nullable=True)
71
+ preferred_model = Column(String, nullable=True)
72
+ job_title = Column(String, nullable=True)
73
+ education = Column(String, nullable=True)
74
+ interests = Column(String, nullable=True)
75
+ additional_info = Column(Text, nullable=True)
76
+ conversation_style = Column(String, nullable=True)
77
+
78
+ oauth_accounts = relationship("OAuthAccount", back_populates="user", cascade="all, delete-orphan")
79
+ conversations = relationship("Conversation", back_populates="user", cascade="all, delete-orphan")
80
 
81
+ class Conversation(Base):
82
+ __tablename__ = "conversation"
83
+ id = Column(Integer, primary_key=True, index=True)
84
+ conversation_id = Column(String, unique=True, index=True, nullable=False)
85
+ user_id = Column(Integer, ForeignKey("user.id"), nullable=False)
86
+ title = Column(String, nullable=False)
87
+ created_at = Column(DateTime, default=datetime.utcnow)
88
+ updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
89
+
90
+ user = relationship("User", back_populates="conversations")
91
+ messages = relationship("Message", back_populates="conversation", cascade="all, delete-orphan")
92
+
93
+ class Message(Base):
94
+ __tablename__ = "message"
95
+ id = Column(Integer, primary_key=True, index=True)
96
+ conversation_id = Column(Integer, ForeignKey("conversation.id"), nullable=False)
97
+ role = Column(String, nullable=False)
98
+ content = Column(Text, nullable=False)
99
+ created_at = Column(DateTime, default=datetime.utcnow)
100
+
101
+ conversation = relationship("Conversation", back_populates="messages")
102
+
103
+ # قاعدة بيانات المستخدم المخصصة
104
+ class CustomSQLAlchemyUserDatabase(SQLAlchemyUserDatabase):
105
+ def __init__(self, session: AsyncSession, user_table, oauth_account_table=None):
106
+ super().__init__(session, user_table, oauth_account_table)
107
+
108
+ def parse_id(self, value: Any) -> int:
109
+ logger.debug(f"Parsing user id: {value} (type={type(value)})")
110
+ return int(value) if isinstance(value, str) else value
111
+
112
+ async def get_by_email(self, email: str) -> Optional[User]:
113
+ logger.info(f"Looking for user with email: {email}")
114
+ stmt = select(self.user_table).where(self.user_table.email == email)
115
+ result = await self.session.execute(stmt)
116
+ return result.scalar_one_or_none()
117
+
118
+ async def create(self, create_dict: Dict[str, Any]) -> User:
119
+ logger.info(f"Creating new user: {create_dict.get('email')}")
120
+ user = self.user_table(**create_dict)
121
+ self.session.add(user)
122
+ await self.session.commit()
123
+ await self.session.refresh(user)
124
+ return user
125
+
126
+ # دالة لجلب الجلسة async
127
+ async def get_db() -> AsyncGenerator[AsyncSession, None]:
128
  async with AsyncSessionLocal() as session:
129
  try:
130
+ yield session
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  finally:
132
  await session.close()
133
 
134
+ # دالة لجلب قاعدة بيانات المستخدمين لـ fastapi-users
135
+ async def get_user_db(session: AsyncSession = Depends(get_db)) -> AsyncGenerator[CustomSQLAlchemyUserDatabase, None]:
136
+ yield CustomSQLAlchemyUserDatabase(session, User, OAuthAccount)
137
 
138
+ # دالة لإنشاء الجداول
139
+ async def init_db():
140
  try:
141
+ async with async_engine.begin() as conn:
142
+ await conn.run_sync(Base.metadata.create_all)
143
+ logger.info("Database tables created successfully")
144
  except Exception as e:
145
+ logger.error(f"Error creating database tables: {e}")
146
+ raise
create_dummy_user.py CHANGED
@@ -1,30 +1,8 @@
1
- # create_dummy_user.py
2
 
3
- import asyncio
4
- from api.database import get_db, User
5
- from passlib.hash import bcrypt
6
-
7
- async def create_dummy_user():
8
- async for session in get_db():
9
- existing = await session.execute(
10
- User.__table__.select().where(User.email == "admin@example.com")
11
- )
12
- if existing.scalar():
13
- print("⚠️ User already exists.")
14
- return
15
-
16
- user = User(
17
- email="admin@example.com",
18
- hashed_password=bcrypt.hash("00000000"),
19
- is_active=True,
20
- is_superuser=True,
21
- is_verified=True,
22
- display_name="Admin"
23
- )
24
- session.add(user)
25
- await session.commit()
26
- await session.refresh(user)
27
- print(f"✅ User created: {user.email}")
28
-
29
- if __name__ == "__main__":
30
- asyncio.run(create_dummy_user())
 
 
1
 
2
+ # email="admin@example.com",
3
+ # hashed_password=bcrypt.hash("admin123"),
4
+ # is_active=True,
5
+ # is_superuser=True,
6
+ # is_verified=True,
7
+ # display_name="Admin"
8
+