2025-09-20 01:17:27 +03:30
# Removed __future__ annotations to fix OpenAPI schema generation
2025-09-15 21:50:09 +03:30
2025-09-19 04:35:13 +03:30
import datetime
2025-09-20 01:17:27 +03:30
from fastapi import APIRouter , Depends , Request , Query
2025-09-19 04:35:13 +03:30
from fastapi . responses import Response
2025-09-15 21:50:09 +03:30
from sqlalchemy . orm import Session
from adapters . db . session import get_db
2025-09-18 10:44:23 +03:30
from app . core . responses import success_response , format_datetime_fields
2025-09-15 21:50:09 +03:30
from app . services . captcha_service import create_captcha
2025-09-19 04:35:13 +03:30
from app . services . auth_service import register_user , login_user , create_password_reset , reset_password , change_password , referral_stats
from app . services . pdf import PDFService
2025-09-20 01:17:27 +03:30
from . schemas import (
RegisterRequest , LoginRequest , ForgotPasswordRequest , ResetPasswordRequest ,
ChangePasswordRequest , CreateApiKeyRequest , QueryInfo , FilterItem ,
SuccessResponse , CaptchaResponse , LoginResponse , ApiKeyResponse ,
ReferralStatsResponse , UserResponse
)
2025-09-15 21:50:09 +03:30
from app . core . auth_dependency import get_current_user , AuthContext
from app . services . api_key_service import list_personal_keys , create_personal_key , revoke_key
router = APIRouter ( prefix = " /auth " , tags = [ " auth " ] )
2025-09-20 01:17:27 +03:30
@router.post ( " /captcha " ,
summary = " تولید کپچای عددی " ,
description = " تولید کپچای عددی برای تأیید هویت در عملیات حساس " ,
response_model = SuccessResponse ,
responses = {
200 : {
" description " : " کپچا با موفقیت تولید شد " ,
" content " : {
" application/json " : {
" example " : {
" success " : True ,
" message " : " کپچا تولید شد " ,
" data " : {
" captcha_id " : " abc123def456 " ,
" image_base64 " : " iVBORw0KGgoAAAANSUhEUgAA... " ,
" ttl_seconds " : 180
}
}
}
}
}
}
)
2025-09-15 21:50:09 +03:30
def generate_captcha ( db : Session = Depends ( get_db ) ) - > dict :
captcha_id , image_base64 , ttl = create_captcha ( db )
return success_response ( {
" captcha_id " : captcha_id ,
" image_base64 " : image_base64 ,
" ttl_seconds " : ttl ,
} )
2025-09-20 01:17:27 +03:30
@router.get ( " /me " ,
summary = " دریافت اطلاعات کاربر کنونی " ,
description = " دریافت اطلاعات کامل کاربری که در حال حاضر وارد سیستم شده است " ,
response_model = SuccessResponse ,
responses = {
200 : {
" description " : " اطلاعات کاربر با موفقیت دریافت شد " ,
" content " : {
" application/json " : {
" example " : {
" success " : True ,
" message " : " اطلاعات کاربر دریافت شد " ,
" data " : {
" id " : 1 ,
" email " : " user@example.com " ,
" mobile " : " 09123456789 " ,
" first_name " : " احمد " ,
" last_name " : " احمدی " ,
" is_active " : True ,
" referral_code " : " ABC123 " ,
" referred_by_user_id " : None ,
" app_permissions " : { " admin " : True } ,
" created_at " : " 2024-01-01T00:00:00Z " ,
" updated_at " : " 2024-01-01T00:00:00Z "
}
}
}
}
} ,
401 : {
" description " : " کاربر احراز هویت نشده است " ,
" content " : {
" application/json " : {
" example " : {
" success " : False ,
" message " : " احراز هویت مورد نیاز است " ,
" error_code " : " UNAUTHORIZED "
}
}
}
}
}
)
2025-09-19 16:40:05 +03:30
def get_current_user_info (
request : Request ,
ctx : AuthContext = Depends ( get_current_user )
) - > dict :
""" دریافت اطلاعات کاربر کنونی """
return success_response ( ctx . to_dict ( ) , request )
2025-09-20 01:17:27 +03:30
@router.post ( " /register " ,
summary = " ثبتنام کاربر جدید " ,
description = " ثبتنام کاربر جدید در سیستم با تأیید کپچا " ,
response_model = SuccessResponse ,
responses = {
200 : {
" description " : " کاربر با موفقیت ثبتنام شد " ,
" content " : {
" application/json " : {
" example " : {
" success " : True ,
" message " : " ثبتنام با موفقیت انجام شد " ,
" data " : {
" api_key " : " sk_1234567890abcdef " ,
" expires_at " : None ,
" user " : {
" id " : 1 ,
" first_name " : " احمد " ,
" last_name " : " احمدی " ,
" email " : " ahmad@example.com " ,
" mobile " : " 09123456789 " ,
" referral_code " : " ABC123 " ,
" app_permissions " : None
}
}
}
}
}
} ,
400 : {
" description " : " خطا در اعتبارسنجی دادهها " ,
" content " : {
" application/json " : {
" example " : {
" success " : False ,
" message " : " کپچا نامعتبر است " ,
" error_code " : " INVALID_CAPTCHA "
}
}
}
} ,
409 : {
" description " : " کاربر با این ایمیل یا موبایل قبلاً ثبتنام کرده است " ,
" content " : {
" application/json " : {
" example " : {
" success " : False ,
" message " : " کاربر با این ایمیل قبلاً ثبتنام کرده است " ,
" error_code " : " USER_EXISTS "
}
}
}
}
}
)
2025-09-16 00:10:20 +03:30
def register ( request : Request , payload : RegisterRequest , db : Session = Depends ( get_db ) ) - > dict :
2025-09-15 21:50:09 +03:30
user_id = register_user (
db = db ,
first_name = payload . first_name ,
last_name = payload . last_name ,
email = payload . email ,
mobile = payload . mobile ,
password = payload . password ,
captcha_id = payload . captcha_id ,
captcha_code = payload . captcha_code ,
2025-09-18 10:44:23 +03:30
referrer_code = payload . referrer_code ,
2025-09-15 21:50:09 +03:30
)
2025-09-16 00:10:20 +03:30
# Create a session api key similar to login
user_agent = request . headers . get ( " User-Agent " )
ip = request . client . host if request . client else None
from app . core . security import generate_api_key
from adapters . db . repositories . api_key_repo import ApiKeyRepository
api_key , key_hash = generate_api_key ( )
api_repo = ApiKeyRepository ( db )
api_repo . create_session_key ( user_id = user_id , key_hash = key_hash , device_id = payload . device_id , user_agent = user_agent , ip = ip , expires_at = None )
2025-09-18 10:44:23 +03:30
from adapters . db . models . user import User
user_obj = db . get ( User , user_id )
2025-09-19 14:34:43 +03:30
user = { " id " : user_id , " first_name " : payload . first_name , " last_name " : payload . last_name , " email " : payload . email , " mobile " : payload . mobile , " referral_code " : getattr ( user_obj , " referral_code " , None ) , " app_permissions " : getattr ( user_obj , " app_permissions " , None ) }
2025-09-18 10:44:23 +03:30
response_data = { " api_key " : api_key , " expires_at " : None , " user " : user }
formatted_data = format_datetime_fields ( response_data , request )
return success_response ( formatted_data , request )
2025-09-15 21:50:09 +03:30
2025-09-20 01:17:27 +03:30
@router.post ( " /login " ,
summary = " ورود با ایمیل یا موبایل " ,
description = " ورود کاربر به سیستم با استفاده از ایمیل یا شماره موبایل و رمز عبور " ,
response_model = SuccessResponse ,
responses = {
200 : {
" description " : " ورود با موفقیت انجام شد " ,
" content " : {
" application/json " : {
" example " : {
" success " : True ,
" message " : " ورود با موفقیت انجام شد " ,
" data " : {
" api_key " : " sk_1234567890abcdef " ,
" expires_at " : " 2024-01-02T00:00:00Z " ,
" user " : {
" id " : 1 ,
" first_name " : " احمد " ,
" last_name " : " احمدی " ,
" email " : " ahmad@example.com " ,
" mobile " : " 09123456789 " ,
" referral_code " : " ABC123 " ,
" app_permissions " : { " admin " : True }
}
}
}
}
}
} ,
400 : {
" description " : " خطا در اعتبارسنجی دادهها " ,
" content " : {
" application/json " : {
" example " : {
" success " : False ,
" message " : " کپچا نامعتبر است " ,
" error_code " : " INVALID_CAPTCHA "
}
}
}
} ,
401 : {
" description " : " اطلاعات ورود نامعتبر است " ,
" content " : {
" application/json " : {
" example " : {
" success " : False ,
" message " : " ایمیل یا رمز عبور اشتباه است " ,
" error_code " : " INVALID_CREDENTIALS "
}
}
}
}
}
)
2025-09-15 21:50:09 +03:30
def login ( request : Request , payload : LoginRequest , db : Session = Depends ( get_db ) ) - > dict :
user_agent = request . headers . get ( " User-Agent " )
ip = request . client . host if request . client else None
api_key , expires_at , user = login_user (
db = db ,
identifier = payload . identifier ,
password = payload . password ,
captcha_id = payload . captcha_id ,
captcha_code = payload . captcha_code ,
device_id = payload . device_id ,
user_agent = user_agent ,
ip = ip ,
)
2025-09-18 10:44:23 +03:30
# Ensure referral_code is included
from adapters . db . repositories . user_repo import UserRepository
repo = UserRepository ( db )
from adapters . db . models . user import User
user_obj = None
if ' id ' in user and user [ ' id ' ] :
user_obj = repo . db . get ( User , user [ ' id ' ] )
if user_obj is not None :
user [ " referral_code " ] = getattr ( user_obj , " referral_code " , None )
response_data = { " api_key " : api_key , " expires_at " : expires_at , " user " : user }
formatted_data = format_datetime_fields ( response_data , request )
return success_response ( formatted_data , request )
2025-09-15 21:50:09 +03:30
2025-09-20 01:17:27 +03:30
@router.post ( " /forgot-password " ,
summary = " ایجاد توکن بازنشانی رمز عبور " ,
description = " ایجاد توکن برای بازنشانی رمز عبور کاربر " ,
response_model = SuccessResponse ,
responses = {
200 : {
" description " : " توکن بازنشانی با موفقیت ایجاد شد " ,
" content " : {
" application/json " : {
" example " : {
" success " : True ,
" message " : " توکن بازنشانی ارسال شد " ,
" data " : {
" ok " : True ,
" token " : " reset_token_1234567890abcdef "
}
}
}
}
} ,
400 : {
" description " : " خطا در اعتبارسنجی دادهها " ,
" content " : {
" application/json " : {
" example " : {
" success " : False ,
" message " : " کپچا نامعتبر است " ,
" error_code " : " INVALID_CAPTCHA "
}
}
}
} ,
404 : {
" description " : " کاربر یافت نشد " ,
" content " : {
" application/json " : {
" example " : {
" success " : False ,
" message " : " کاربر با این ایمیل یا موبایل یافت نشد " ,
" error_code " : " USER_NOT_FOUND "
}
}
}
}
}
)
2025-09-15 21:50:09 +03:30
def forgot_password ( payload : ForgotPasswordRequest , db : Session = Depends ( get_db ) ) - > dict :
# In production do not return token; send via email/SMS. Here we return for dev/testing.
token = create_password_reset ( db = db , identifier = payload . identifier , captcha_id = payload . captcha_id , captcha_code = payload . captcha_code )
return success_response ( { " ok " : True , " token " : token if token else None } )
2025-09-20 01:17:27 +03:30
@router.post ( " /reset-password " ,
summary = " بازنشانی رمز عبور با توکن " ,
description = " بازنشانی رمز عبور کاربر با استفاده از توکن دریافتی " ,
response_model = SuccessResponse ,
responses = {
200 : {
" description " : " رمز عبور با موفقیت بازنشانی شد " ,
" content " : {
" application/json " : {
" example " : {
" success " : True ,
" message " : " رمز عبور با موفقیت تغییر کرد " ,
" data " : {
" ok " : True
}
}
}
}
} ,
400 : {
" description " : " خطا در اعتبارسنجی دادهها " ,
" content " : {
" application/json " : {
" example " : {
" success " : False ,
" message " : " کپچا نامعتبر است " ,
" error_code " : " INVALID_CAPTCHA "
}
}
}
} ,
404 : {
" description " : " توکن نامعتبر یا منقضی شده است " ,
" content " : {
" application/json " : {
" example " : {
" success " : False ,
" message " : " توکن نامعتبر یا منقضی شده است " ,
" error_code " : " INVALID_TOKEN "
}
}
}
}
}
)
2025-09-15 21:50:09 +03:30
def reset_password_endpoint ( payload : ResetPasswordRequest , db : Session = Depends ( get_db ) ) - > dict :
reset_password ( db = db , token = payload . token , new_password = payload . new_password , captcha_id = payload . captcha_id , captcha_code = payload . captcha_code )
return success_response ( { " ok " : True } )
2025-09-20 01:17:27 +03:30
@router.get ( " /api-keys " ,
summary = " لیست کلیدهای API شخصی " ,
description = " دریافت لیست کلیدهای API شخصی کاربر " ,
response_model = SuccessResponse ,
responses = {
200 : {
" description " : " لیست کلیدهای API با موفقیت دریافت شد " ,
" content " : {
" application/json " : {
" example " : {
" success " : True ,
" message " : " لیست کلیدهای API دریافت شد " ,
" data " : [
{
" id " : 1 ,
" name " : " کلید اصلی " ,
" scopes " : " read,write " ,
" device_id " : " device123 " ,
" user_agent " : " Mozilla/5.0... " ,
" ip " : " 192.168.1.1 " ,
" expires_at " : None ,
" last_used_at " : " 2024-01-01T12:00:00Z " ,
" created_at " : " 2024-01-01T00:00:00Z "
}
]
}
}
}
} ,
401 : {
" description " : " کاربر احراز هویت نشده است "
}
}
)
2025-09-19 04:35:13 +03:30
def list_keys ( request : Request , ctx : AuthContext = Depends ( get_current_user ) , db : Session = Depends ( get_db ) ) - > dict :
2025-09-15 21:50:09 +03:30
items = list_personal_keys ( db , ctx . user . id )
return success_response ( items )
2025-09-20 01:17:27 +03:30
@router.post ( " /api-keys " ,
summary = " ایجاد کلید API شخصی " ,
description = " ایجاد کلید API جدید برای کاربر " ,
response_model = SuccessResponse ,
responses = {
200 : {
" description " : " کلید API با موفقیت ایجاد شد " ,
" content " : {
" application/json " : {
" example " : {
" success " : True ,
" message " : " کلید API ایجاد شد " ,
" data " : {
" id " : 1 ,
" api_key " : " sk_1234567890abcdef "
}
}
}
}
} ,
401 : {
" description " : " کاربر احراز هویت نشده است "
}
}
)
2025-09-19 04:35:13 +03:30
def create_key ( request : Request , payload : CreateApiKeyRequest , ctx : AuthContext = Depends ( get_current_user ) , db : Session = Depends ( get_db ) ) - > dict :
2025-09-15 21:50:09 +03:30
id_ , api_key = create_personal_key ( db , ctx . user . id , payload . name , payload . scopes , None )
return success_response ( { " id " : id_ , " api_key " : api_key } )
2025-09-20 01:17:27 +03:30
@router.post ( " /change-password " ,
summary = " تغییر رمز عبور " ,
description = " تغییر رمز عبور کاربر با تأیید رمز عبور فعلی " ,
response_model = SuccessResponse ,
responses = {
200 : {
" description " : " رمز عبور با موفقیت تغییر کرد " ,
" content " : {
" application/json " : {
" example " : {
" success " : True ,
" message " : " رمز عبور با موفقیت تغییر کرد " ,
" data " : {
" ok " : True
}
}
}
}
} ,
400 : {
" description " : " خطا در اعتبارسنجی دادهها " ,
" content " : {
" application/json " : {
" example " : {
" success " : False ,
" message " : " رمز عبور فعلی اشتباه است " ,
" error_code " : " INVALID_CURRENT_PASSWORD "
}
}
}
} ,
401 : {
" description " : " کاربر احراز هویت نشده است "
}
}
)
2025-09-18 10:44:23 +03:30
def change_password_endpoint ( request : Request , payload : ChangePasswordRequest , ctx : AuthContext = Depends ( get_current_user ) , db : Session = Depends ( get_db ) ) - > dict :
# دریافت translator از request state
translator = getattr ( request . state , " translator " , None )
change_password (
db = db ,
user_id = ctx . user . id ,
current_password = payload . current_password ,
new_password = payload . new_password ,
confirm_password = payload . confirm_password ,
translator = translator
)
return success_response ( { " ok " : True } )
2025-09-20 01:17:27 +03:30
@router.delete ( " /api-keys/ {key_id} " ,
summary = " حذف کلید API " ,
description = " حذف کلید API مشخص شده " ,
response_model = SuccessResponse ,
responses = {
200 : {
" description " : " کلید API با موفقیت حذف شد " ,
" content " : {
" application/json " : {
" example " : {
" success " : True ,
" message " : " کلید API حذف شد " ,
" data " : {
" ok " : True
}
}
}
}
} ,
401 : {
" description " : " کاربر احراز هویت نشده است "
} ,
404 : {
" description " : " کلید API یافت نشد " ,
" content " : {
" application/json " : {
" example " : {
" success " : False ,
" message " : " کلید API یافت نشد " ,
" error_code " : " API_KEY_NOT_FOUND "
}
}
}
}
}
)
2025-09-19 04:35:13 +03:30
def delete_key ( request : Request , key_id : int , ctx : AuthContext = Depends ( get_current_user ) , db : Session = Depends ( get_db ) ) - > dict :
2025-09-15 21:50:09 +03:30
revoke_key ( db , ctx . user . id , key_id )
return success_response ( { " ok " : True } )
2025-09-20 01:17:27 +03:30
@router.get ( " /referrals/stats " ,
summary = " آمار معرفیها " ,
description = " دریافت آمار معرفیهای کاربر فعلی " ,
response_model = SuccessResponse ,
responses = {
200 : {
" description " : " آمار معرفیها با موفقیت دریافت شد " ,
" content " : {
" application/json " : {
" example " : {
" success " : True ,
" message " : " آمار معرفیها دریافت شد " ,
" data " : {
" total_referrals " : 25 ,
" active_referrals " : 20 ,
" recent_referrals " : 5 ,
" referral_rate " : 0.8
}
}
}
}
} ,
401 : {
" description " : " کاربر احراز هویت نشده است "
}
}
)
def get_referral_stats ( request : Request , ctx : AuthContext = Depends ( get_current_user ) , db : Session = Depends ( get_db ) , start : str = Query ( None , description = " تاریخ شروع (ISO format) " ) , end : str = Query ( None , description = " تاریخ پایان (ISO format) " ) ) :
2025-09-18 10:44:23 +03:30
from datetime import datetime
start_dt = datetime . fromisoformat ( start ) if start else None
end_dt = datetime . fromisoformat ( end ) if end else None
stats = referral_stats ( db = db , user_id = ctx . user . id , start = start_dt , end = end_dt )
return success_response ( stats )
2025-09-20 01:17:27 +03:30
@router.post ( " /referrals/list " ,
summary = " لیست معرفیها با فیلتر پیشرفته " ,
description = " دریافت لیست معرفیها با قابلیت فیلتر، جستجو، مرتبسازی و صفحهبندی " ,
response_model = SuccessResponse ,
responses = {
200 : {
" description " : " لیست معرفیها با موفقیت دریافت شد " ,
" content " : {
" application/json " : {
" example " : {
" success " : True ,
" message " : " لیست معرفیها دریافت شد " ,
" data " : {
" items " : [
{
" id " : 1 ,
" first_name " : " علی " ,
" last_name " : " احمدی " ,
" email " : " ali@example.com " ,
" mobile " : " 09123456789 " ,
" created_at " : " 2024-01-01T00:00:00Z "
}
] ,
" total " : 1 ,
" page " : 1 ,
" limit " : 10 ,
" total_pages " : 1 ,
" has_next " : False ,
" has_prev " : False
}
}
}
}
} ,
401 : {
" description " : " کاربر احراز هویت نشده است "
}
}
)
2025-09-19 04:35:13 +03:30
def get_referral_list_advanced (
request : Request ,
query_info : QueryInfo ,
2025-09-18 10:44:23 +03:30
ctx : AuthContext = Depends ( get_current_user ) ,
2025-09-19 04:35:13 +03:30
db : Session = Depends ( get_db )
2025-09-18 10:44:23 +03:30
) - > dict :
2025-09-19 04:35:13 +03:30
"""
دریافت لیست معرفی ه ا با قابلیت فیلتر پیشرفته
پارامترهای QueryInfo :
- sort_by : فیلد مرتب سازی ( مثال : created_at , first_name , last_name , email )
- sort_desc : ترتیب نزولی ( true / false )
- take : تعداد رکورد در هر صفحه ( پیش فرض : 10 )
- skip : تعداد رکورد صرف نظر شده ( پیش فرض : 0 )
- search : عبارت جستجو
- search_fields : فیلدهای جستجو ( مثال : [ " first_name " , " last_name " , " email " ] )
- filters : آرایه فیلترها با ساختار :
[
{
" property " : " created_at " ,
" operator " : " >= " ,
" value " : " 2024-01-01T00:00:00 "
} ,
{
" property " : " first_name " ,
" operator " : " * " ,
" value " : " احمد "
}
]
"""
from adapters . db . repositories . user_repo import UserRepository
from adapters . db . models . user import User
2025-09-18 10:44:23 +03:30
from datetime import datetime
2025-09-19 04:35:13 +03:30
# Create a custom query for referrals
repo = UserRepository ( db )
# Add filter for referrals only (users with referred_by_user_id = current user)
referral_filter = FilterItem (
property = " referred_by_user_id " ,
operator = " = " ,
value = ctx . user . id
)
# Add referral filter to existing filters
if query_info . filters is None :
query_info . filters = [ referral_filter ]
else :
query_info . filters . append ( referral_filter )
# Set default search fields for referrals
if query_info . search_fields is None :
query_info . search_fields = [ " first_name " , " last_name " , " email " ]
# Execute query with filters
referrals , total = repo . query_with_filters ( query_info )
# Convert to dictionary format
referral_dicts = [ repo . to_dict ( referral ) for referral in referrals ]
# Format datetime fields
formatted_referrals = format_datetime_fields ( referral_dicts , request )
# Calculate pagination info
page = ( query_info . skip / / query_info . take ) + 1
total_pages = ( total + query_info . take - 1 ) / / query_info . take
return success_response ( {
" items " : formatted_referrals ,
" total " : total ,
" page " : page ,
" limit " : query_info . take ,
" total_pages " : total_pages ,
" has_next " : page < total_pages ,
" has_prev " : page > 1
} , request )
2025-09-20 01:17:27 +03:30
@router.post ( " /referrals/export/pdf " ,
summary = " خروجی PDF لیست معرفیها " ,
description = " خروجی PDF لیست معرفیها با قابلیت فیلتر و انتخاب سطرهای خاص " ,
responses = {
200 : {
" description " : " فایل PDF با موفقیت تولید شد " ,
" content " : {
" application/pdf " : {
" schema " : {
" type " : " string " ,
" format " : " binary "
}
}
}
} ,
401 : {
" description " : " کاربر احراز هویت نشده است "
}
}
)
2025-09-19 04:35:13 +03:30
def export_referrals_pdf (
request : Request ,
query_info : QueryInfo ,
selected_only : bool = False ,
selected_indices : str | None = None ,
ctx : AuthContext = Depends ( get_current_user ) ,
db : Session = Depends ( get_db )
) - > Response :
"""
خروجی PDF لیست معرفی ه ا
پارامترها :
- selected_only : آیا فقط سطرهای انتخاب شده export شوند
- selected_indices : لیست ایندکس های انتخاب شده ( JSON string )
- سایر پارامترهای QueryInfo برای فیلتر
"""
from app . services . pdf import PDFService
from app . services . auth_service import referral_stats
import json
# Parse selected indices if provided
indices = None
if selected_only and selected_indices :
try :
indices = json . loads ( selected_indices )
except ( json . JSONDecodeError , TypeError ) :
indices = None
# Get stats for the report
stats = None
try :
# Extract date range from filters if available
start_date = None
end_date = None
if query_info . filters :
for filter_item in query_info . filters :
if filter_item . property == ' created_at ' :
if filter_item . operator == ' >= ' :
start_date = filter_item . value
elif filter_item . operator == ' < ' :
end_date = filter_item . value
stats = referral_stats (
db = db ,
user_id = ctx . user . id ,
start = start_date ,
end = end_date
)
except Exception :
pass # Continue without stats
# Get calendar type from request headers
calendar_header = request . headers . get ( " X-Calendar-Type " , " jalali " )
calendar_type = " jalali " if calendar_header . lower ( ) in [ " jalali " , " persian " , " shamsi " ] else " gregorian "
# Generate PDF using new modular service
pdf_service = PDFService ( )
# Get locale from request headers
locale_header = request . headers . get ( " Accept-Language " , " fa " )
locale = " fa " if locale_header . startswith ( " fa " ) else " en "
pdf_bytes = pdf_service . generate_pdf (
module_name = ' marketing ' ,
data = { } , # Empty data - module will fetch its own data
calendar_type = calendar_type ,
locale = locale ,
db = db ,
user_id = ctx . user . id ,
query_info = query_info ,
selected_indices = indices ,
stats = stats
)
# Return PDF response
from fastapi . responses import Response
import datetime
filename = f " referrals_export_ { datetime . datetime . now ( ) . strftime ( ' % Y % m %d _ % H % M % S ' ) } .pdf "
return Response (
content = pdf_bytes ,
media_type = " application/pdf " ,
headers = {
" Content-Disposition " : f " attachment; filename= { filename } " ,
" Content-Length " : str ( len ( pdf_bytes ) )
}
)
2025-09-20 01:17:27 +03:30
@router.post ( " /referrals/export/excel " ,
summary = " خروجی Excel لیست معرفیها " ,
description = " خروجی Excel لیست معرفیها با قابلیت فیلتر و انتخاب سطرهای خاص " ,
responses = {
200 : {
" description " : " فایل Excel با موفقیت تولید شد " ,
" content " : {
" application/vnd.openxmlformats-officedocument.spreadsheetml.sheet " : {
" schema " : {
" type " : " string " ,
" format " : " binary "
}
}
}
} ,
401 : {
" description " : " کاربر احراز هویت نشده است "
}
}
)
2025-09-19 04:35:13 +03:30
def export_referrals_excel (
request : Request ,
query_info : QueryInfo ,
selected_only : bool = False ,
selected_indices : str | None = None ,
ctx : AuthContext = Depends ( get_current_user ) ,
db : Session = Depends ( get_db )
) - > Response :
"""
خروجی Excel لیست معرفی ه ا ( فایل Excel واقعی برای دانلود )
پارامترها :
- selected_only : آیا فقط سطرهای انتخاب شده export شوند
- selected_indices : لیست ایندکس های انتخاب شده ( JSON string )
- سایر پارامترهای QueryInfo برای فیلتر
"""
from app . services . pdf import PDFService
import json
import io
from openpyxl import Workbook
from openpyxl . styles import Font , Alignment , PatternFill , Border , Side
# Parse selected indices if provided
indices = None
if selected_only and selected_indices :
try :
indices = json . loads ( selected_indices )
except ( json . JSONDecodeError , TypeError ) :
indices = None
# Get calendar type from request headers
calendar_header = request . headers . get ( " X-Calendar-Type " , " jalali " )
calendar_type = " jalali " if calendar_header . lower ( ) in [ " jalali " , " persian " , " shamsi " ] else " gregorian "
# Generate Excel data using new modular service
pdf_service = PDFService ( )
# Get locale from request headers
locale_header = request . headers . get ( " Accept-Language " , " fa " )
locale = " fa " if locale_header . startswith ( " fa " ) else " en "
excel_data = pdf_service . generate_excel_data (
module_name = ' marketing ' ,
data = { } , # Empty data - module will fetch its own data
calendar_type = calendar_type ,
locale = locale ,
db = db ,
user_id = ctx . user . id ,
query_info = query_info ,
selected_indices = indices
)
# Create Excel workbook
wb = Workbook ( )
ws = wb . active
ws . title = " Referrals "
# Define styles
header_font = Font ( bold = True , color = " FFFFFF " )
header_fill = PatternFill ( start_color = " 366092 " , end_color = " 366092 " , fill_type = " solid " )
header_alignment = Alignment ( horizontal = " center " , vertical = " center " )
border = Border (
left = Side ( style = ' thin ' ) ,
right = Side ( style = ' thin ' ) ,
top = Side ( style = ' thin ' ) ,
bottom = Side ( style = ' thin ' )
)
# Add headers
if excel_data :
headers = list ( excel_data [ 0 ] . keys ( ) )
for col , header in enumerate ( headers , 1 ) :
cell = ws . cell ( row = 1 , column = col , value = header )
cell . font = header_font
cell . fill = header_fill
cell . alignment = header_alignment
cell . border = border
# Add data rows
for row , data in enumerate ( excel_data , 2 ) :
for col , header in enumerate ( headers , 1 ) :
cell = ws . cell ( row = row , column = col , value = data . get ( header , " " ) )
cell . border = border
# Center align for numbers and dates
if header in [ " ردیف " , " Row " , " تاریخ ثبت " , " Registration Date " ] :
cell . alignment = Alignment ( horizontal = " center " )
# Auto-adjust column widths
for column in ws . columns :
max_length = 0
column_letter = column [ 0 ] . column_letter
for cell in column :
try :
if len ( str ( cell . value ) ) > max_length :
max_length = len ( str ( cell . value ) )
except :
pass
adjusted_width = min ( max_length + 2 , 50 )
ws . column_dimensions [ column_letter ] . width = adjusted_width
# Save to BytesIO
excel_buffer = io . BytesIO ( )
wb . save ( excel_buffer )
excel_buffer . seek ( 0 )
# Generate filename
filename = f " referrals_export_ { datetime . datetime . now ( ) . strftime ( ' % Y % m %d _ % H % M % S ' ) } .xlsx "
# Return Excel file as response
return Response (
content = excel_buffer . getvalue ( ) ,
media_type = " application/vnd.openxmlformats-officedocument.spreadsheetml.sheet " ,
headers = {
" Content-Disposition " : f " attachment; filename= { filename } " ,
" Content-Type " : " application/vnd.openxmlformats-officedocument.spreadsheetml.sheet "
}
)
2025-09-18 10:44:23 +03:30