humanvprojectceo commited on
Commit
559dc5a
·
verified ·
1 Parent(s): 2c12055

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +96 -74
app.py CHANGED
@@ -1,6 +1,7 @@
1
  import os
2
  import json
3
  import logging
 
4
  from datetime import datetime, timedelta, timezone
5
  from typing import Optional, List
6
  from contextlib import asynccontextmanager
@@ -18,7 +19,7 @@ from google import genai
18
  import bcrypt
19
 
20
  # ==========================================
21
- # بخش اول: سیستم ثبت لاگ خطاهای پشت‌صحنه سرور
22
  # ==========================================
23
  logging.basicConfig(level=logging.INFO)
24
  logger = logging.getLogger("production_backend")
@@ -27,7 +28,7 @@ logger = logging.getLogger("production_backend")
27
  AI_CORE_KEY = os.getenv("GEMINI_API_KEY") or os.getenv("SECRET")
28
 
29
  # ==========================================
30
- # بخش دوم: اتصال پایگاه داده Neon.tech و سوئیچ به SQLite
31
  # ==========================================
32
  DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./test.db")
33
  if DATABASE_URL.startswith("postgres://"):
@@ -51,15 +52,12 @@ except Exception as db_init_err:
51
  SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
52
  Base = declarative_base()
53
 
54
- # ==========================================
55
- # بخش سوم: توابع همزمانی پایه (حل قطعی NameError)
56
- # ==========================================
57
  def get_now():
58
- """تولید زمان بومی و دقیق کنونی جهت هماهنگی با فرآیندهای دیتابیس"""
59
  return datetime.now(timezone.utc).replace(tzinfo=None)
60
 
61
  # ==========================================
62
- # بخش چهارم: مدل‌های دیتابیس (SQLAlchemy Models)
63
  # ==========================================
64
 
65
  class User(Base):
@@ -214,7 +212,7 @@ class Review(Base):
214
  comment = Column(Text, nullable=True)
215
 
216
  # ==========================================
217
- # بخش پنجم: توابع کمکی امنیتی و رمزنگاری
218
  # ==========================================
219
 
220
  def get_password_hash(password: str) -> str:
@@ -232,14 +230,8 @@ SECRET_KEY = os.getenv("JWT_SECRET", "8c12fa4870a23049102c89eeffb8a719280abdc029
232
  ALGORITHM = "HS256"
233
  security = HTTPBearer()
234
 
235
- def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
236
- to_encode = data.copy()
237
- expire = get_now() + (expires_delta or timedelta(minutes=1440))
238
- to_encode.update({"exp": expire})
239
- return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
240
-
241
  # ==========================================
242
- # بخش ششم: متدهای پاک‌سازی دوره‌ای و تزریق وابستگی دیتابیس
243
  # ==========================================
244
 
245
  def clean_expired_bookings(db: Session):
@@ -264,7 +256,7 @@ def clean_expired_bookings(db: Session):
264
 
265
  def get_db():
266
  db = SessionLocal()
267
- clean_expired_bookings(db) # اجرای آزادسازی خودکار نوبت‌ها
268
  try:
269
  yield db
270
  finally:
@@ -292,7 +284,7 @@ def get_current_user(db: Session = Depends(get_db), credentials: HTTPAuthorizati
292
  return user
293
 
294
  # ==========================================
295
- # بخش هفتم: توابع پردازش ابری هوشمند بومی
296
  # ==========================================
297
 
298
  def _execute_native_matrix_processing(prompt: str, model_type: str = "fast") -> str:
@@ -376,7 +368,7 @@ def core_bio_extraction(resume_text: str) -> str:
376
  return result
377
 
378
  # ==========================================
379
- # بخش هشتم: کلاس‌های ورودی Pydantic
380
  # ==========================================
381
 
382
  class RegisterRequest(BaseModel):
@@ -476,68 +468,15 @@ class UpdateProfileRequest(BaseModel):
476
  city: Optional[str] = None
477
 
478
  # ==========================================
479
- # بخش نهم: ساختار فرآیند بارگذاری Lifespan و Seeding
480
  # ==========================================
481
 
482
- def seed_initial_data(db: Session):
483
- try:
484
- test_pwd = get_password_hash("demo1234")
485
-
486
- # دانشجویان پیش‌فرض
487
- student = User(email="student@demo.com", hashed_password=test_pwd, role="student", is_verified=True, balance=500000.0, phone_number="09121111111", city="تهران")
488
- db.add(student)
489
-
490
- # مشاوران پیش‌فرض
491
- counselor1 = User(email="counselor1@demo.com", hashed_password=test_pwd, role="counselor", is_verified=True, rating=4.8, city="تهران")
492
- counselor2 = User(email="counselor2@demo.com", hashed_password=test_pwd, role="counselor", is_verified=True, rating=4.5, city="شیراز")
493
- db.add(counselor1)
494
- db.add(counselor2)
495
- db.flush()
496
-
497
- p1 = CounselorProfile(user_id=counselor1.id, specialty="برنامه‌ریزی کنکور تجربی", resume="رتبه دو رقمی سراسری با سابقه تدریس تخصصی", bio="مشاور ارشد با تخصص رتبه‌پروری تجربی", hourly_rate=120000.0)
498
- p2 = CounselorProfile(user_id=counselor2.id, specialty="هدایت تحصیلی ریاضی و فیزیک", resume="عضو بنیاد نخبگان ریاضی", bio="متخصص بهینه‌سازی مسیر قبولی رشته ریاضی", hourly_rate=100000.0)
499
- db.add(p1)
500
- db.add(p2)
501
-
502
- # آموزشگاه‌های پیش‌فرض
503
- school_user = User(email="school@demo.com", hashed_password=test_pwd, role="school_representative", is_verified=True, city="اصفهان")
504
- db.add(school_user)
505
- db.flush()
506
-
507
- s_profile = SchoolProfile(
508
- user_id=school_user.id,
509
- name="موسسه کنکور اندیشه",
510
- subscription_active_until=get_now() + timedelta(days=60),
511
- is_sponsored=True,
512
- bio="برترین آکادمی تستی کنکور اصفهان",
513
- address="اصفهان، خیابان دانشگاه",
514
- phone_number="03133333333",
515
- founding_year=1395
516
- )
517
- db.add(s_profile)
518
- db.flush()
519
-
520
- course = SchoolCourse(school_id=s_profile.id, course_name="ریاضی تخصصی کنکور", teacher_name="استاد علوی", teaching_style="روش‌های سرعتی و محاسباتی")
521
- db.add(course)
522
-
523
- # اسلات آزاد مشاور
524
- now = get_now()
525
- slot1 = TimeSlot(counselor_id=counselor1.id, start_time=now + timedelta(days=1, hours=10), end_time=now + timedelta(days=1, hours=11), is_booked=False)
526
- db.add(slot1)
527
-
528
- db.commit()
529
- except Exception as e:
530
- logger.error(f"Seeding process crashed: {e}", exc_info=True)
531
- db.rollback()
532
-
533
-
534
  @asynccontextmanager
535
  async def lifespan(app: FastAPI):
536
  Base.metadata.create_all(bind=engine)
537
  db = SessionLocal()
538
  try:
539
- if db.query(User).count() == 0:
540
- seed_initial_data(db)
541
  except Exception as seed_err:
542
  logger.error(f"Error seeding database during startup: {seed_err}", exc_info=True)
543
  finally:
@@ -559,6 +498,89 @@ app.add_middleware(
559
  allow_headers=["*"],
560
  )
561
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
562
 
563
  @app.exception_handler(Exception)
564
  async def global_exception_handler(request: Request, exc: Exception):
@@ -569,7 +591,7 @@ async def global_exception_handler(request: Request, exc: Exception):
569
  )
570
 
571
  # ==========================================
572
- # بخش دهم: آدرس‌های API
573
  # ==========================================
574
 
575
  @app.post("/api/auth/register")
 
1
  import os
2
  import json
3
  import logging
4
+ import datetime
5
  from datetime import datetime, timedelta, timezone
6
  from typing import Optional, List
7
  from contextlib import asynccontextmanager
 
19
  import bcrypt
20
 
21
  # ==========================================
22
+ # بخش اول: سیستم ثبت لاگ خطارهای سرور
23
  # ==========================================
24
  logging.basicConfig(level=logging.INFO)
25
  logger = logging.getLogger("production_backend")
 
28
  AI_CORE_KEY = os.getenv("GEMINI_API_KEY") or os.getenv("SECRET")
29
 
30
  # ==========================================
31
+ # بخش دوم: اتصال پایگاه داده Neon.tech و ساختار پایه
32
  # ==========================================
33
  DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./test.db")
34
  if DATABASE_URL.startswith("postgres://"):
 
52
  SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
53
  Base = declarative_base()
54
 
55
+ # تعریف تابع زمان بومی سرور
 
 
56
  def get_now():
 
57
  return datetime.now(timezone.utc).replace(tzinfo=None)
58
 
59
  # ==========================================
60
+ # بخش سوم: مدل‌های دیتابیس (SQLAlchemy Models)
61
  # ==========================================
62
 
63
  class User(Base):
 
212
  comment = Column(Text, nullable=True)
213
 
214
  # ==========================================
215
+ # بخش چهارم: توابع کمکی امنیتی و رمزنگاری
216
  # ==========================================
217
 
218
  def get_password_hash(password: str) -> str:
 
230
  ALGORITHM = "HS256"
231
  security = HTTPBearer()
232
 
 
 
 
 
 
 
233
  # ==========================================
234
+ # بخش پنجم: توابع وابستگی تزریق پایگاه داده و احراز هویت
235
  # ==========================================
236
 
237
  def clean_expired_bookings(db: Session):
 
256
 
257
  def get_db():
258
  db = SessionLocal()
259
+ clean_expired_bookings(db) # اجرای مکانیزم آزادسازی نوبت‌ها در لحظه لود دیتابیس
260
  try:
261
  yield db
262
  finally:
 
284
  return user
285
 
286
  # ==========================================
287
+ # بخش ششم: توابع پردازش ابری هوشمند بومی تاپ دانش
288
  # ==========================================
289
 
290
  def _execute_native_matrix_processing(prompt: str, model_type: str = "fast") -> str:
 
368
  return result
369
 
370
  # ==========================================
371
+ # بخش هفتم: کلاس‌های ورودی Pydantic (صحت‌سنجی)
372
  # ==========================================
373
 
374
  class RegisterRequest(BaseModel):
 
468
  city: Optional[str] = None
469
 
470
  # ==========================================
471
+ # بخش هشتم: راه‌اندازی و اجرای وب‌سرور FastAPI
472
  # ==========================================
473
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
474
  @asynccontextmanager
475
  async def lifespan(app: FastAPI):
476
  Base.metadata.create_all(bind=engine)
477
  db = SessionLocal()
478
  try:
479
+ seed_initial_data(db)
 
480
  except Exception as seed_err:
481
  logger.error(f"Error seeding database during startup: {seed_err}", exc_info=True)
482
  finally:
 
498
  allow_headers=["*"],
499
  )
500
 
501
+ # ==========================================
502
+ # بخش نهم: فرآیند ثبت و همگام‌سازی داده‌های تستی و دمو (Check-Seeding)
503
+ # ==========================================
504
+
505
+ def seed_initial_data(db: Session):
506
+ try:
507
+ # لیست جامع اکانت‌های پیش‌فرض قدیمی و جدید با کلمات عبور هماهنگ با کلاینت اندروید
508
+ seed_users = [
509
+ {"email": "student@demo.com", "password": "demo1234", "role": "student", "balance": 500000.0, "phone": "09121111111", "city": "تهران"},
510
+ {"email": "student@test.com", "password": "student123", "role": "student", "balance": 300000.0, "phone": "09122222222", "city": "تهران"},
511
+ {"email": "counselor1@demo.com", "password": "demo1234", "role": "counselor", "rating": 4.8, "city": "تهران"},
512
+ {"email": "counselor2@demo.com", "password": "demo1234", "role": "counselor", "rating": 4.5, "city": "شیراز"},
513
+ {"email": "counselor@test.com", "password": "counselor123", "role": "counselor", "rating": 4.7, "city": "تهران"},
514
+ {"email": "school@demo.com", "password": "demo1234", "role": "school_representative", "city": "اصفهان"},
515
+ {"email": "school@test.com", "password": "school123", "role": "school_representative", "city": "تهران"}
516
+ ]
517
+
518
+ for u_data in seed_users:
519
+ # بررسی تک تک کاندیداها به صورت پویا (سازگار با داده‌های قبلی Neon)
520
+ existing = db.query(User).filter(User.email == u_data["email"]).first()
521
+ if not existing:
522
+ new_user = User(
523
+ email=u_data["email"],
524
+ hashed_password=get_password_hash(u_data["password"]),
525
+ role=u_data["role"],
526
+ is_verified=True,
527
+ balance=u_data.get("balance", 0.0),
528
+ rating=u_data.get("rating", 5.0),
529
+ phone_number=u_data.get("phone"),
530
+ city=u_data.get("city")
531
+ )
532
+ db.add(new_user)
533
+ db.flush()
534
+
535
+ # ثبت دقیق پروفایل‌های همبسته
536
+ if u_data["role"] == "counselor":
537
+ p = CounselorProfile(
538
+ user_id=new_user.id,
539
+ specialty="برنامه‌ریزی کنکور و هدایت تحصیلی",
540
+ resume="رتبه برتر کنکور و کارشناس باسابقه پلتفرم تراز تحصیلی بومی تاپ دانش",
541
+ bio="متخصص افزایش راندمان مطالعه داوطلبان با رویکردهای نوین مشاوره کلاسی",
542
+ hourly_rate=120000.0
543
+ )
544
+ db.add(p)
545
+
546
+ # ایجاد اسلات زمانی آزاد عمومی پیش‌فرض
547
+ now = get_now()
548
+ slot = TimeSlot(
549
+ counselor_id=new_user.id,
550
+ start_time=now + timedelta(days=1, hours=10),
551
+ end_time=now + timedelta(days=1, hours=11),
552
+ is_booked=False
553
+ )
554
+ db.add(slot)
555
+
556
+ elif u_data["role"] == "school_representative":
557
+ s = SchoolProfile(
558
+ user_id=new_user.id,
559
+ name="موسسه اندیشه نوین",
560
+ subscription_active_until=get_now() + timedelta(days=60),
561
+ is_sponsored=True,
562
+ bio="برترین آکادمی تستی کنکور با کادر اساتید برجسته بومی",
563
+ address="خیابان ولیعصر، پلاک ۱۲",
564
+ phone_number="02122222222",
565
+ founding_year=1395
566
+ )
567
+ db.add(s)
568
+ db.flush()
569
+
570
+ course = SchoolCourse(
571
+ school_id=s.id,
572
+ course_name="شیمی تخصصی جامع",
573
+ teacher_name="استاد احمدی",
574
+ teaching_style="روش‌های تستی سرعتی"
575
+ )
576
+ db.add(course)
577
+
578
+ db.commit()
579
+ logger.info("Check-Seeding process successfully verified and updated!")
580
+ except Exception as e:
581
+ logger.error(f"Seeding process crashed: {e}", exc_info=True)
582
+ db.rollback()
583
+
584
 
585
  @app.exception_handler(Exception)
586
  async def global_exception_handler(request: Request, exc: Exception):
 
591
  )
592
 
593
  # ==========================================
594
+ # بخش دهم: کلیه آدرس‌های APIها (مراحل ۱ تا ۴)
595
  # ==========================================
596
 
597
  @app.post("/api/auth/register")