jhjv / scripts /daily_gains.py
Docfile's picture
Upload 67 files
73d203f verified
#!/usr/bin/env python3
"""
Daily Gains Calculator Script
This script should be run daily (via cron job or scheduler) to:
1. Calculate and distribute daily gains for active investments
2. Process referral commissions (configurable % of daily gains to referrers)
3. Mark expired investments as inactive
4. Auto-process withdrawals that have passed their configurable delay
Configuration values are read from config.py:
- REFERRAL_PURCHASE_COMMISSION: Commission rate on plan purchases (default: 0.15 = 15%)
- REFERRAL_DAILY_GAIN_COMMISSION: Commission rate on daily gains (default: 0.03 = 3%)
- WITHDRAWAL_FEE_PERCENTAGE: Fee on withdrawals (default: 0.15 = 15%)
- WITHDRAWAL_DELAY_HOURS: Hours before auto-processing withdrawals (default: 24)
Usage:
python scripts/daily_gains.py
Cron example (run daily at 00:05):
5 0 * * * cd /path/to/project && python scripts/daily_gains.py
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from datetime import date, datetime, timezone
from app import create_app, db
from app.models import (
Notification,
ReferralCommission,
Transaction,
User,
UserMetal,
)
def calculate_daily_gains():
"""
Calculate and distribute daily gains for all active investments.
Also processes referral commissions on these gains using config rate.
"""
app = create_app()
with app.app_context():
# Get commission rate from config
daily_gain_commission_rate = app.config.get(
"REFERRAL_DAILY_GAIN_COMMISSION", 0.03
)
print(f"[{datetime.now()}] Starting daily gains calculation for {date.today()}")
print(
f" Config: Daily gain commission rate = {daily_gain_commission_rate * 100}%"
)
active_metals = UserMetal.query.filter_by(is_active=True).all()
processed_count = 0
expired_count = 0
commission_count = 0
total_gains_distributed = 0
total_commissions_paid = 0
for user_metal in active_metals:
# Skip if already processed today
if user_metal.last_gain_date >= date.today():
continue
# Check if investment has expired
if datetime.now(timezone.utc) >= user_metal.expiry_date:
user_metal.is_active = False
expired_count += 1
notification = Notification(
user_id=user_metal.user_id,
title="Adoption Expirée",
message=f"Votre adoption en {user_metal.metal.name} a expiré. Vous avez reçu un total de {user_metal.metal.total_return:.0f} FCFA de gains.",
type="expiry",
)
db.session.add(notification)
print(
f" - Expired investment for user {user_metal.user_id} ({user_metal.metal.name})"
)
continue
metal = user_metal.metal
if not metal:
continue
user = user_metal.user
if not user:
continue
daily_gain = metal.daily_gain
# Add daily gain to user balance
user.balance += daily_gain
user.total_gains += daily_gain
user_metal.last_gain_date = date.today()
total_gains_distributed += daily_gain
# Create notification for user
notification = Notification(
user_id=user.id,
title="Gain Quotidien 💰",
message=f"Vous avez gagné {daily_gain:.0f} FCFA aujourd'hui de votre adoption en {metal.name}.",
type="gain",
)
db.session.add(notification)
# Create transaction record
transaction = Transaction(
user_id=user.id,
type="gain",
amount=daily_gain,
description=f"Gain quotidien - {metal.name}",
status="completed",
)
db.session.add(transaction)
processed_count += 1
print(
f" + Added {daily_gain:.0f} FCFA to user {user.phone} for {metal.name}"
)
# Process referral commission using config rate
referrer = user.get_referrer()
if referrer:
try:
# Calculate commission using config rate
commission_amount = daily_gain * daily_gain_commission_rate
# Create the commission record
commission = ReferralCommission(
referrer_id=referrer.id,
referred_user_id=user.id,
level=1,
commission_type="daily_gain",
commission_percentage=daily_gain_commission_rate * 100,
commission_amount=commission_amount,
gain_amount=daily_gain,
)
db.session.add(commission)
# Add commission to referrer's balance
referrer.balance += commission_amount
referrer.referral_earnings = (
referrer.referral_earnings or 0
) + commission_amount
total_commissions_paid += commission_amount
# Only notify for significant amounts to avoid spam
if commission_amount >= 5:
referrer_notification = Notification(
user_id=referrer.id,
title="Commission sur Gains",
message=f"Vous avez reçu {commission_amount:.0f} FCFA ({daily_gain_commission_rate * 100:.0f}%) sur les gains de {user.name}.",
type="referral",
)
db.session.add(referrer_notification)
commission_count += 1
print(
f" → Commission of {commission_amount:.2f} FCFA to referrer {referrer.phone}"
)
except Exception as e:
print(f" ! Error processing referral commission: {e}")
db.session.commit()
print(f"[{datetime.now()}] Daily gains completed:")
print(f" - Processed: {processed_count} investments")
print(f" - Expired: {expired_count} investments")
print(f" - Commissions: {commission_count} paid")
print(f" - Total gains distributed: {total_gains_distributed:.0f} FCFA")
print(f" - Total commissions paid: {total_commissions_paid:.0f} FCFA")
def auto_process_withdrawals():
"""
Auto-approve withdrawals that have passed their configurable delay without admin action.
Only runs on business days (Monday to Friday).
"""
app = create_app()
with app.app_context():
now = datetime.now(timezone.utc)
# Get config values
withdrawal_fee_percentage = app.config.get("WITHDRAWAL_FEE_PERCENTAGE", 0.15)
withdrawal_delay_hours = app.config.get("WITHDRAWAL_DELAY_HOURS", 24)
# Skip weekends
if now.weekday() in [5, 6]: # Saturday = 5, Sunday = 6
print(f"[{datetime.now()}] Skipping auto-withdrawal processing (weekend)")
return
print(f"[{datetime.now()}] Starting auto-withdrawal processing")
print(
f" Config: Fee = {withdrawal_fee_percentage * 100}%, Delay = {withdrawal_delay_hours}h"
)
# Find pending withdrawals that can be auto-processed
pending_withdrawals = Transaction.query.filter(
Transaction.type == "withdrawal",
Transaction.status == "pending",
Transaction.admin_action.is_(None),
Transaction.scheduled_process_time <= now,
).all()
processed_count = 0
total_fees_collected = 0
for transaction in pending_withdrawals:
transaction.status = "approved"
transaction.processed_at = now
transaction.admin_action = "auto_approved"
transaction.admin_action_time = now
net_amount = transaction.net_amount or transaction.amount
fee_amount = transaction.fee_amount or 0
total_fees_collected += fee_amount
notification = Notification(
user_id=transaction.user_id,
title="Retrait Traité ✅",
message=(
f"Votre retrait de {transaction.amount:.0f} FCFA a été traité automatiquement.\n"
f"Frais ({withdrawal_fee_percentage * 100:.0f}%): {fee_amount:.0f} FCFA\n"
f"Montant envoyé: {net_amount:.0f} FCFA"
),
type="withdrawal",
)
db.session.add(notification)
processed_count += 1
print(
f" + Auto-approved withdrawal #{transaction.id} for user {transaction.user_id} ({net_amount:.0f} FCFA net)"
)
db.session.commit()
print(f"[{datetime.now()}] Auto-withdrawal processing completed:")
print(f" - Processed: {processed_count} withdrawals")
print(f" - Total fees collected: {total_fees_collected:.0f} FCFA")
def cleanup_old_notifications():
"""
Optional: Clean up old read notifications (older than 30 days).
"""
app = create_app()
with app.app_context():
from datetime import timedelta
cutoff_date = datetime.now(timezone.utc) - timedelta(days=30)
old_notifications = Notification.query.filter(
Notification.is_read == True,
Notification.created_at < cutoff_date,
).all()
count = len(old_notifications)
for notification in old_notifications:
db.session.delete(notification)
db.session.commit()
print(f"[{datetime.now()}] Cleaned up {count} old notifications")
def print_config_summary():
"""Print current configuration values."""
app = create_app()
with app.app_context():
print("\n" + "=" * 60)
print("CONFIGURATION SUMMARY")
print("=" * 60)
print(f"Registration Bonus: {app.config.get('REGISTRATION_BONUS', 1000)} FCFA")
print(f"Daily Login Bonus: {app.config.get('DAILY_LOGIN_BONUS', 30)} FCFA")
print(
f"Referral Purchase Commission: {app.config.get('REFERRAL_PURCHASE_COMMISSION', 0.15) * 100}%"
)
print(
f"Referral Daily Gain Commission: {app.config.get('REFERRAL_DAILY_GAIN_COMMISSION', 0.03) * 100}%"
)
print(
f"Withdrawal Fee: {app.config.get('WITHDRAWAL_FEE_PERCENTAGE', 0.15) * 100}%"
)
print(f"Withdrawal Delay: {app.config.get('WITHDRAWAL_DELAY_HOURS', 24)} hours")
print(
f"Withdrawal Min Amount: {app.config.get('WITHDRAWAL_MIN_AMOUNT', 500)} FCFA"
)
print("=" * 60 + "\n")
def print_platform_summary():
"""Print a summary of the platform statistics."""
app = create_app()
with app.app_context():
total_users = User.query.count()
active_investments = UserMetal.query.filter_by(is_active=True).count()
pending_withdrawals = Transaction.query.filter_by(
type="withdrawal", status="pending"
).count()
total_gains_today = (
db.session.query(db.func.sum(Transaction.amount))
.filter(
Transaction.type == "gain",
Transaction.created_at
>= datetime.now(timezone.utc).replace(
hour=0, minute=0, second=0, microsecond=0
),
)
.scalar()
or 0
)
total_commissions_today = (
db.session.query(db.func.sum(ReferralCommission.commission_amount))
.filter(
ReferralCommission.created_at
>= datetime.now(timezone.utc).replace(
hour=0, minute=0, second=0, microsecond=0
),
)
.scalar()
or 0
)
total_withdrawal_fees = Transaction.get_total_withdrawal_fees()
print("\n" + "=" * 60)
print("PLATFORM SUMMARY")
print("=" * 60)
print(f"Total Users: {total_users}")
print(f"Active Investments: {active_investments}")
print(f"Pending Withdrawals: {pending_withdrawals}")
print(f"Total Gains Today: {total_gains_today:.0f} FCFA")
print(f"Total Commissions Today: {total_commissions_today:.0f} FCFA")
print(f"Total Withdrawal Fees Collected: {total_withdrawal_fees:.0f} FCFA")
print("=" * 60 + "\n")
if __name__ == "__main__":
print("\n" + "=" * 60)
print("DAILY PROCESSING SCRIPT")
print(f"Started at: {datetime.now()}")
print("=" * 60 + "\n")
# Print current configuration
print_config_summary()
# Run all daily tasks
calculate_daily_gains()
auto_process_withdrawals()
cleanup_old_notifications()
print_platform_summary()
print("All daily tasks completed successfully!")
print(f"Finished at: {datetime.now()}\n")