Mark-Lasfar commited on
Commit
8a9ebcc
·
1 Parent(s): 4e9a771

Update Model

Browse files
Files changed (8) hide show
  1. .env +1 -0
  2. Dockerfile +6 -1
  3. api/__pycache__/database.cpython-311.pyc +0 -0
  4. api/auth.py +16 -2
  5. api/endpoints.py +2 -1
  6. api/models.py +0 -63
  7. init_db.py +52 -0
  8. main.py +4 -3
.env ADDED
@@ -0,0 +1 @@
 
 
1
+ SQLALCHEMY_DATABASE_URL=sqlite:///data/mgzon_users.db
Dockerfile CHANGED
@@ -10,11 +10,13 @@ RUN apt-get update && apt-get install -y \
10
  gcc \
11
  libc-dev \
12
  ffmpeg \
 
13
  && apt-get clean && rm -rf /var/lib/apt/lists/*
14
 
15
  # Update pip
16
  RUN pip install --upgrade pip
17
 
 
18
  RUN pip install packaging torch==2.4.1
19
 
20
  # Copy requirements.txt and install dependencies
@@ -30,9 +32,12 @@ COPY . .
30
  # Verify files in /app
31
  RUN ls -R /app
32
 
 
 
 
 
33
  # Expose port 7860 for FastAPI
34
  EXPOSE 7860
35
 
36
  # Run the FastAPI app
37
  CMD ["python", "main.py"]
38
-
 
10
  gcc \
11
  libc-dev \
12
  ffmpeg \
13
+ sqlite3 \
14
  && apt-get clean && rm -rf /var/lib/apt/lists/*
15
 
16
  # Update pip
17
  RUN pip install --upgrade pip
18
 
19
+ # Install torch and dependencies
20
  RUN pip install packaging torch==2.4.1
21
 
22
  # Copy requirements.txt and install dependencies
 
32
  # Verify files in /app
33
  RUN ls -R /app
34
 
35
+ # Initialize the database
36
+ ENV SQLALCHEMY_DATABASE_URL=sqlite:////data/mgzon_users.db
37
+ RUN python init_db.py
38
+
39
  # Expose port 7860 for FastAPI
40
  EXPOSE 7860
41
 
42
  # Run the FastAPI app
43
  CMD ["python", "main.py"]
 
api/__pycache__/database.cpython-311.pyc ADDED
Binary file (5.63 kB). View file
 
api/auth.py CHANGED
@@ -11,6 +11,7 @@ from fastapi_users.models import UP
11
  from typing import Optional
12
  import os
13
  import logging
 
14
  from api.models import UserRead, UserCreate, UserUpdate
15
  from sqlalchemy import select
16
 
@@ -71,6 +72,8 @@ class UserManager(IntegerIDMixin, BaseUserManager[User, int]):
71
  associate_by_email: bool = False,
72
  is_verified_by_default: bool = False,
73
  ) -> UP:
 
 
74
  oauth_account_dict = {
75
  "oauth_name": oauth_name,
76
  "access_token": access_token,
@@ -89,20 +92,29 @@ class UserManager(IntegerIDMixin, BaseUserManager[User, int]):
89
  existing_oauth_account = result.scalars().first()
90
 
91
  if existing_oauth_account is not None:
92
- return await self.on_after_login(existing_oauth_account.user, request)
 
 
 
 
 
 
93
 
94
  if associate_by_email:
95
- # Synchronous query for get_by_email
96
  statement = select(User).where(User.email == account_email)
97
  result = self.user_db.session.execute(statement)
98
  user = result.scalars().first()
99
  if user is not None:
 
100
  oauth_account.user_id = user.id
101
  self.user_db.session.add(oauth_account)
102
  self.user_db.session.commit()
 
103
  return await self.on_after_login(user, request)
104
 
105
  # Create new user
 
106
  user_dict = {
107
  "email": account_email,
108
  "hashed_password": self.password_helper.hash("dummy_password"),
@@ -113,10 +125,12 @@ class UserManager(IntegerIDMixin, BaseUserManager[User, int]):
113
  self.user_db.session.add(user)
114
  self.user_db.session.commit()
115
  self.user_db.session.refresh(user)
 
116
 
117
  oauth_account.user_id = user.id
118
  self.user_db.session.add(oauth_account)
119
  self.user_db.session.commit()
 
120
  return await self.on_after_login(user, request)
121
 
122
  async def get_user_manager(user_db: SQLAlchemyUserDatabase = Depends(get_user_db)):
 
11
  from typing import Optional
12
  import os
13
  import logging
14
+ from api.database import User, OAuthAccount
15
  from api.models import UserRead, UserCreate, UserUpdate
16
  from sqlalchemy import select
17
 
 
72
  associate_by_email: bool = False,
73
  is_verified_by_default: bool = False,
74
  ) -> UP:
75
+ logger.info(f"Processing OAuth callback for {oauth_name} with account_id: {account_id}, email: {account_email}")
76
+
77
  oauth_account_dict = {
78
  "oauth_name": oauth_name,
79
  "access_token": access_token,
 
92
  existing_oauth_account = result.scalars().first()
93
 
94
  if existing_oauth_account is not None:
95
+ logger.info(f"Found existing OAuth account for {oauth_name}, account_id: {account_id}")
96
+ user = existing_oauth_account.user
97
+ if user is None:
98
+ logger.error(f"No user associated with OAuth account {account_id}")
99
+ raise ValueError("No user associated with this OAuth account")
100
+ logger.info(f"Returning existing user: {user.email}")
101
+ return await self.on_after_login(user, request)
102
 
103
  if associate_by_email:
104
+ logger.info(f"Associating by email: {account_email}")
105
  statement = select(User).where(User.email == account_email)
106
  result = self.user_db.session.execute(statement)
107
  user = result.scalars().first()
108
  if user is not None:
109
+ logger.info(f"Found existing user by email: {user.email}")
110
  oauth_account.user_id = user.id
111
  self.user_db.session.add(oauth_account)
112
  self.user_db.session.commit()
113
+ logger.info(f"Associated OAuth account with user: {user.email}")
114
  return await self.on_after_login(user, request)
115
 
116
  # Create new user
117
+ logger.info(f"Creating new user for email: {account_email}")
118
  user_dict = {
119
  "email": account_email,
120
  "hashed_password": self.password_helper.hash("dummy_password"),
 
125
  self.user_db.session.add(user)
126
  self.user_db.session.commit()
127
  self.user_db.session.refresh(user)
128
+ logger.info(f"Created new user: {user.email}")
129
 
130
  oauth_account.user_id = user.id
131
  self.user_db.session.add(oauth_account)
132
  self.user_db.session.commit()
133
+ logger.info(f"Linked OAuth account to new user: {user.email}")
134
  return await self.on_after_login(user, request)
135
 
136
  async def get_user_manager(user_db: SQLAlchemyUserDatabase = Depends(get_user_db)):
api/endpoints.py CHANGED
@@ -3,7 +3,8 @@ import os
3
  import uuid
4
  from fastapi import APIRouter, Depends, HTTPException, Request, status, UploadFile, File
5
  from fastapi.responses import StreamingResponse
6
- from api.models import QueryRequest, User, Conversation, Message, ConversationOut, ConversationCreate, UserUpdate
 
7
  from api.auth import current_active_user
8
  from api.database import get_db
9
  from sqlalchemy.orm import Session
 
3
  import uuid
4
  from fastapi import APIRouter, Depends, HTTPException, Request, status, UploadFile, File
5
  from fastapi.responses import StreamingResponse
6
+ from api.database import User, Conversation, Message
7
+ from api.models import QueryRequest, ConversationOut, ConversationCreate, UserUpdate
8
  from api.auth import current_active_user
9
  from api.database import get_db
10
  from sqlalchemy.orm import Session
api/models.py CHANGED
@@ -1,70 +1,7 @@
1
- # models.py
2
- from fastapi_users.db import SQLAlchemyBaseUserTable
3
- from sqlalchemy import Column, Integer, String, Boolean, Text, ForeignKey, DateTime
4
- from sqlalchemy.orm import relationship
5
- from sqlalchemy.ext.declarative import declarative_base
6
  from pydantic import BaseModel, Field
7
  from typing import List, Optional
8
  from fastapi_users import schemas
9
  from datetime import datetime
10
- import uuid
11
-
12
- Base = declarative_base()
13
-
14
- # جدول OAuth Accounts لتخزين بيانات تسجيل الدخول الخارجي
15
- class OAuthAccount(Base):
16
- __tablename__ = "oauth_accounts"
17
- id = Column(Integer, primary_key=True)
18
- user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
19
- oauth_name = Column(String, nullable=False)
20
- access_token = Column(String, nullable=False)
21
- expires_at = Column(Integer, nullable=True)
22
- refresh_token = Column(String, nullable=True)
23
- account_id = Column(String, index=True, nullable=False)
24
- account_email = Column(String, nullable=False)
25
- user = relationship("User", back_populates="oauth_accounts")
26
-
27
- # نموذج المستخدم
28
- class User(SQLAlchemyBaseUserTable, Base):
29
- __tablename__ = "users"
30
- id = Column(Integer, primary_key=True, index=True)
31
- email = Column(String, unique=True, index=True, nullable=False)
32
- hashed_password = Column(String, nullable=True)
33
- is_active = Column(Boolean, default=True)
34
- is_superuser = Column(Boolean, default=False)
35
- display_name = Column(String, nullable=True) # الاسم المستعار للمستخدم
36
- preferred_model = Column(String, nullable=True) # النموذج المفضل (اسم وهمي)
37
- job_title = Column(String, nullable=True) # الوظيفة
38
- education = Column(String, nullable=True) # التعليم
39
- interests = Column(Text, nullable=True) # الاهتمامات
40
- additional_info = Column(Text, nullable=True) # معلومات إضافية
41
- conversation_style = Column(String, nullable=True) # نمط المحادثة (مثل: موجز، تحليلي)
42
- oauth_accounts = relationship("OAuthAccount", back_populates="user", cascade="all, delete-orphan")
43
- conversations = relationship("Conversation", back_populates="user", cascade="all, delete-orphan")
44
-
45
- # نموذج المحادثة
46
- class Conversation(Base):
47
- __tablename__ = "conversations"
48
- id = Column(Integer, primary_key=True, index=True)
49
- conversation_id = Column(String, unique=True, index=True, default=lambda: str(uuid.uuid4()))
50
- title = Column(String, nullable=True)
51
- user_id = Column(Integer, ForeignKey("users.id"))
52
- created_at = Column(DateTime, default=datetime.utcnow)
53
- updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
54
-
55
- user = relationship("User", back_populates="conversations")
56
- messages = relationship("Message", back_populates="conversation", cascade="all, delete-orphan")
57
-
58
- # نموذج الرسالة
59
- class Message(Base):
60
- __tablename__ = "messages"
61
- id = Column(Integer, primary_key=True, index=True)
62
- role = Column(String)
63
- content = Column(Text)
64
- conversation_id = Column(Integer, ForeignKey("conversations.id"))
65
- created_at = Column(DateTime, default=datetime.utcnow)
66
-
67
- conversation = relationship("Conversation", back_populates="messages")
68
 
69
  # Pydantic schemas for fastapi-users
70
  class UserRead(schemas.BaseUser[int]):
 
 
 
 
 
 
1
  from pydantic import BaseModel, Field
2
  from typing import List, Optional
3
  from fastapi_users import schemas
4
  from datetime import datetime
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
  # Pydantic schemas for fastapi-users
7
  class UserRead(schemas.BaseUser[int]):
init_db.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from sqlalchemy import create_engine, select, delete
3
+ from sqlalchemy.orm import sessionmaker
4
+ from api.database import Base, User, OAuthAccount, Conversation, Message
5
+ import logging
6
+
7
+ # Setup logging
8
+ logging.basicConfig(level=logging.INFO)
9
+ logger = logging.getLogger(__name__)
10
+
11
+ # جلب URL قاعدة البيانات من المتغيرات البيئية
12
+ SQLALCHEMY_DATABASE_URL = os.getenv("SQLALCHEMY_DATABASE_URL")
13
+ if not SQLALCHEMY_DATABASE_URL:
14
+ logger.error("SQLALCHEMY_DATABASE_URL is not set in environment variables.")
15
+ raise ValueError("SQLALCHEMY_DATABASE_URL is required.")
16
+
17
+ # إنشاء المحرك
18
+ engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
19
+
20
+ # إعداد الجلسة
21
+ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
22
+
23
+ def init_db():
24
+ logger.info("Starting database initialization...")
25
+
26
+ # إنشاء الجداول
27
+ Base.metadata.create_all(bind=engine)
28
+ logger.info("Database tables created successfully.")
29
+
30
+ # تنظيف البيانات غير المتسقة
31
+ with SessionLocal() as session:
32
+ # حذف سجلات oauth_accounts اللي مش مرتبطة بمستخدم موجود
33
+ stmt = delete(OAuthAccount).where(
34
+ OAuthAccount.user_id.notin_(select(User.id))
35
+ )
36
+ result = session.execute(stmt)
37
+ deleted_count = result.rowcount
38
+ session.commit()
39
+ logger.info(f"Deleted {deleted_count} orphaned OAuth accounts.")
40
+
41
+ # التأكد من إن كل المستخدمين ليهم is_active=True
42
+ users = session.execute(select(User)).scalars().all()
43
+ for user in users:
44
+ if not user.is_active:
45
+ user.is_active = True
46
+ logger.info(f"Updated user {user.email} to is_active=True")
47
+ session.commit()
48
+
49
+ logger.info("Database initialization completed.")
50
+
51
+ if __name__ == "__main__":
52
+ init_db()
main.py CHANGED
@@ -14,8 +14,8 @@ from fastapi.openapi.docs import get_swagger_ui_html
14
  from fastapi.middleware.cors import CORSMiddleware
15
  from api.endpoints import router as api_router
16
  from api.auth import fastapi_users, auth_backend, current_active_user, get_auth_router
17
- from api.database import get_db, engine, Base
18
- from api.models import User, UserRead, UserCreate, Conversation, UserUpdate # استيراد User
19
  from motor.motor_asyncio import AsyncIOMotorClient
20
  from pydantic import BaseModel
21
  from typing import List
@@ -28,6 +28,7 @@ from hashlib import md5
28
  from datetime import datetime
29
  from httpx_oauth.exceptions import GetIdEmailError
30
  import re
 
31
 
32
  # Setup logging
33
  logging.basicConfig(level=logging.INFO)
@@ -79,8 +80,8 @@ CONCURRENCY_LIMIT = int(os.getenv("CONCURRENCY_LIMIT", 20))
79
  # Initialize FastAPI app
80
  @asynccontextmanager
81
  async def lifespan(app: FastAPI):
 
82
  await setup_mongo_index()
83
- Base.metadata.create_all(bind=engine) # Create tables on startup
84
  yield
85
 
86
  app = FastAPI(title="MGZon Chatbot API", lifespan=lifespan)
 
14
  from fastapi.middleware.cors import CORSMiddleware
15
  from api.endpoints import router as api_router
16
  from api.auth import fastapi_users, auth_backend, current_active_user, get_auth_router
17
+ from api.database import get_db
18
+ from api.models import UserRead, UserCreate, UserUpdate
19
  from motor.motor_asyncio import AsyncIOMotorClient
20
  from pydantic import BaseModel
21
  from typing import List
 
28
  from datetime import datetime
29
  from httpx_oauth.exceptions import GetIdEmailError
30
  import re
31
+ from init_db import init_db # استيراد دالة init_db
32
 
33
  # Setup logging
34
  logging.basicConfig(level=logging.INFO)
 
80
  # Initialize FastAPI app
81
  @asynccontextmanager
82
  async def lifespan(app: FastAPI):
83
+ init_db() # استدعاء دالة init_db لإنشاء الجداول وتنظيف البيانات
84
  await setup_mongo_index()
 
85
  yield
86
 
87
  app = FastAPI(title="MGZon Chatbot API", lifespan=lifespan)