153 lines
14 KiB
Python
153 lines
14 KiB
Python
"""Seed MIDAS database with demo data"""
|
||
import asyncio
|
||
import random
|
||
from datetime import datetime, timedelta
|
||
from app.database import engine, async_session, Base
|
||
from app.models.user import User
|
||
from app.models.bet import Bet
|
||
from app.models.bankroll import Bankroll
|
||
from app.models.alert import Alert
|
||
from app.models.achievement import Achievement, UserAchievement
|
||
from app.models.lesson import Lesson, UserLesson
|
||
from app.utils.auth import hash_password
|
||
|
||
ACHIEVEMENTS = [
|
||
{"name": "Primeiro Passo", "description": "Registrou sua primeira aposta", "icon": "👣", "category": "beginner", "requirement_type": "bets", "requirement_value": 1, "points": 10},
|
||
{"name": "Estudante", "description": "Completou sua primeira lição", "icon": "📚", "category": "education", "requirement_type": "lessons", "requirement_value": 1, "points": 15},
|
||
{"name": "Disciplinado", "description": "5 dias sem aposta impulsiva", "icon": "🧘", "category": "discipline", "requirement_type": "streak", "requirement_value": 5, "points": 25},
|
||
{"name": "Analista", "description": "Registrou 10 apostas", "icon": "📊", "category": "beginner", "requirement_type": "bets", "requirement_value": 10, "points": 20},
|
||
{"name": "Scholar", "description": "Completou 5 lições", "icon": "🎓", "category": "education", "requirement_type": "lessons", "requirement_value": 5, "points": 30},
|
||
{"name": "Consistente", "description": "30 dias usando o MIDAS", "icon": "📅", "category": "discipline", "requirement_type": "days", "requirement_value": 30, "points": 50},
|
||
{"name": "Mestre da Banca", "description": "Não estourou o limite por 30 dias", "icon": "💰", "category": "bankroll", "requirement_type": "bankroll_days", "requirement_value": 30, "points": 40},
|
||
{"name": "Zen", "description": "10 dias seguidos sem aposta impulsiva", "icon": "☯️", "category": "discipline", "requirement_type": "streak", "requirement_value": 10, "points": 35},
|
||
{"name": "Veterano", "description": "50 apostas registradas", "icon": "⭐", "category": "beginner", "requirement_type": "bets", "requirement_value": 50, "points": 30},
|
||
{"name": "Iluminado", "description": "Completou todas as lições", "icon": "💡", "category": "education", "requirement_type": "lessons", "requirement_value": 8, "points": 50},
|
||
]
|
||
|
||
LESSONS = [
|
||
{"title": "O que é Valor Esperado", "content": "Valor Esperado (EV) é o conceito mais importante em apostas. É a média de quanto você ganharia ou perderia por aposta se repetisse a mesma aposta infinitas vezes.\n\n**Fórmula:** EV = (Probabilidade de ganhar × Lucro) - (Probabilidade de perder × Valor apostado)\n\n**Exemplo:** Aposta de R$100 em odds 2.0 com 50% de chance real:\nEV = (0.50 × R$100) - (0.50 × R$100) = R$0\n\nSe a casa oferece odds 1.90 para o mesmo evento:\nEV = (0.50 × R$90) - (0.50 × R$100) = -R$5\n\nIsso significa que, em média, você perde R$5 por aposta. Com o tempo, isso se acumula.", "category": "fundamentos", "difficulty": "iniciante", "duration_min": 8, "order_num": 1},
|
||
{"title": "Como a Casa Sempre Ganha", "content": "A margem da casa (vig/juice) é como as casas de apostas garantem lucro.\n\n**Como funciona:** Em um cara ou coroa justo, as odds deveriam ser 2.0 para ambos os lados. Mas a casa oferece 1.90 para cada lado.\n\nSe 100 pessoas apostam R$100 em cara e 100 em coroa:\n- Casa recebe: R$20.000\n- Casa paga: 100 × R$190 = R$19.000\n- Lucro da casa: R$1.000 (5%)\n\n**A margem média das casas:**\n- Futebol: 5-8%\n- Tênis: 6-8%\n- Basquete: 4-6%\n\nNão importa quem ganha — a casa SEMPRE lucra no longo prazo.", "category": "fundamentos", "difficulty": "iniciante", "duration_min": 6, "order_num": 2},
|
||
{"title": "Gestão de Banca 101", "content": "Gestão de banca é a skill #1 que separa apostadores disciplinados de apostadores que quebram.\n\n**Regras fundamentais:**\n1. **Defina um orçamento mensal** que NÃO comprometa suas contas\n2. **Máximo 2-5% da banca por aposta** — NUNCA mais que 10%\n3. **Nunca aposte dinheiro do aluguel/comida/contas**\n4. **Separe o dinheiro de apostas** do dinheiro do dia-a-dia\n\n**Exemplo prático:**\n- Banca mensal: R$500\n- Max por aposta (5%): R$25\n- Se perder 50% da banca: PARE e reavalie\n\n**A regra de ouro:** Se você não pode perder esse dinheiro sem afetar sua vida, NÃO aposte.", "category": "gestão", "difficulty": "iniciante", "duration_min": 7, "order_num": 3},
|
||
{"title": "Vieses Cognitivos nas Apostas", "content": "Seu cérebro é seu pior inimigo nas apostas. Conheça os vieses:\n\n**1. Falácia do Jogador:** 'Saiu vermelho 5 vezes, agora vai sair preto!' — ERRADO. Cada evento é independente.\n\n**2. Viés de Confirmação:** Você lembra das apostas que ganhou e esquece as que perdeu.\n\n**3. Excesso de Confiança:** Após uma sequência de vitórias, você acha que 'entende' do assunto e aumenta os valores.\n\n**4. Aversão à Perda:** A dor de perder R$100 é 2x mais intensa que a alegria de ganhar R$100.\n\n**5. Efeito Ancoragem:** Se a odd era 3.0 e caiu para 2.5, parece 'ruim' — mas pode ser o valor justo.\n\n**Como se proteger:** Tenha regras fixas ANTES de apostar. Emoção é o inimigo.", "category": "psicologia", "difficulty": "intermediário", "duration_min": 10, "order_num": 4},
|
||
{"title": "Quando Parar", "content": "Saber quando parar é mais importante que saber quando apostar.\n\n**Sinais de que você deve parar AGORA:**\n🔴 Apostando para recuperar perdas\n🔴 Aumentando valores depois de perder\n🔴 Apostando após beber ou com raiva\n🔴 Escondendo apostas de família/amigos\n🔴 Pegando dinheiro emprestado para apostar\n🔴 Pensando em apostas o tempo todo\n\n**Regras práticas:**\n- Definiu o limite diário? Atingiu = PAROU\n- Perdeu 3 seguidas? PAROU por hoje\n- Está emocional? NÃO aposte\n- São 23h+? Melhor dormir\n\n**Lembre-se:** As casas de apostas estão abertas 24/7. Sempre haverá 'a próxima oportunidade'. Não precisa ser AGORA.", "category": "disciplina", "difficulty": "iniciante", "duration_min": 6, "order_num": 5},
|
||
{"title": "Recuperação é Armadilha", "content": "O 'chasing losses' (tentar recuperar perdas) é o comportamento mais destrutivo nas apostas.\n\n**O ciclo da destruição:**\n1. Perde R$50\n2. Aposta R$100 para recuperar\n3. Perde de novo → agora está -R$150\n4. Aposta R$200 'para empatar'\n5. Perde → -R$350 em um dia\n\n**Por que seu cérebro faz isso:**\n- Aversão à perda: quer 'zerar' o dia\n- Ilusão de controle: 'agora eu sei'\n- Dopamina: a possibilidade de recuperar é excitante\n\n**A verdade cruel:** Cada aposta é INDEPENDENTE. Suas perdas passadas não aumentam suas chances futuras.\n\n**Solução:** Aceite a perda. Feche o app. Vá fazer outra coisa. R$50 perdidos são R$50. Não transforme em R$350.", "category": "psicologia", "difficulty": "intermediário", "duration_min": 8, "order_num": 6},
|
||
{"title": "Bankroll Management Avançado", "content": "Estratégias avançadas de gestão de banca:\n\n**1. Método de Unidades:**\n- 1 unidade = 1-2% da banca\n- Aposta normal: 1 unidade\n- Aposta com edge claro: 2 unidades\n- NUNCA mais que 3 unidades\n\n**2. Critério de Kelly:**\nKelly % = (bp - q) / b\n- b = odds decimais - 1\n- p = probabilidade real de ganhar\n- q = 1 - p\n\nExemplo: Odds 2.5, chance real 45%\nKelly = (1.5 × 0.45 - 0.55) / 1.5 = 8.3%\nUse 1/4 Kelly na prática = ~2%\n\n**3. Flat Betting:**\nAposta o mesmo valor SEMPRE. Simples e eficaz.\n\n**4. Regra do Drawdown:**\n- Perdeu 20% da banca? Reduza unidade pela metade\n- Perdeu 50%? PARE e reavalie", "category": "gestão", "difficulty": "avançado", "duration_min": 12, "order_num": 7},
|
||
{"title": "Apostas Responsáveis", "content": "Apostar pode ser entretenimento — mas precisa ser com RESPONSABILIDADE.\n\n**Princípios do MIDAS:**\n✅ Aposte apenas dinheiro que pode perder\n✅ Defina limites ANTES de começar\n✅ Registre TODAS as apostas (é pra isso que o MIDAS existe)\n✅ Analise seus padrões regularmente\n✅ Faça pausas regulares\n✅ Converse abertamente sobre apostas\n\n**Recursos de ajuda:**\n📞 CVV: 188 (24h)\n📱 Jogadores Anônimos: www.jogadoresanonimos.org.br\n📋 Teste SOGS: avalie se suas apostas são problemáticas\n\n**O MIDAS existe para te ajudar a ter CONSCIÊNCIA.** Não somos uma plataforma de apostas. Somos sua ferramenta de autoconhecimento financeiro.\n\n**Se as apostas estão causando sofrimento, procure ajuda profissional. Não há vergonha nisso.**", "category": "responsabilidade", "difficulty": "iniciante", "duration_min": 5, "order_num": 8},
|
||
]
|
||
|
||
DEMO_BETS = [
|
||
{"sport": "futebol", "event_name": "Flamengo vs Palmeiras", "platform": "Bet365", "amount": 25, "odds": 2.10, "result": "win", "emotion": "😎"},
|
||
{"sport": "futebol", "event_name": "Real Madrid vs Barcelona", "platform": "Betano", "amount": 30, "odds": 1.85, "result": "win", "emotion": "😎"},
|
||
{"sport": "futebol", "event_name": "Liverpool vs Man City", "platform": "Bet365", "amount": 20, "odds": 3.20, "result": "loss", "emotion": "😰"},
|
||
{"sport": "UFC", "event_name": "UFC 310: Pantoja vs Asakura", "platform": "Betano", "amount": 15, "odds": 1.65, "result": "win", "emotion": "🤑"},
|
||
{"sport": "basquete", "event_name": "Lakers vs Celtics", "platform": "Bet365", "amount": 25, "odds": 2.40, "result": "loss", "emotion": "😤"},
|
||
{"sport": "futebol", "event_name": "Corinthians vs São Paulo", "platform": "Betano", "amount": 20, "odds": 2.00, "result": "win", "emotion": "😎"},
|
||
{"sport": "UFC", "event_name": "UFC 311: Makhachev vs Tsarukyan", "platform": "Bet365", "amount": 30, "odds": 1.45, "result": "win", "emotion": "😎"},
|
||
{"sport": "futebol", "event_name": "Argentina vs Brasil", "platform": "Betano", "amount": 50, "odds": 2.80, "result": "loss", "emotion": "😤"},
|
||
{"sport": "basquete", "event_name": "Warriors vs Bucks", "platform": "Bet365", "amount": 15, "odds": 1.90, "result": "win", "emotion": "😎"},
|
||
{"sport": "futebol", "event_name": "PSG vs Bayern", "platform": "Bet365", "amount": 20, "odds": 2.50, "result": "loss", "emotion": "😰"},
|
||
{"sport": "futebol", "event_name": "Botafogo vs Fluminense", "platform": "Betano", "amount": 15, "odds": 1.75, "result": "win", "emotion": "🤑"},
|
||
{"sport": "UFC", "event_name": "UFC Fight Night: Moreno vs Royval", "platform": "Bet365", "amount": 20, "odds": 2.20, "result": "loss", "emotion": "😰"},
|
||
{"sport": "basquete", "event_name": "Nets vs Knicks", "platform": "Betano", "amount": 25, "odds": 1.95, "result": "win", "emotion": "😎"},
|
||
{"sport": "futebol", "event_name": "Inter vs Grêmio", "platform": "Bet365", "amount": 20, "odds": 2.05, "result": "win", "emotion": "😎"},
|
||
{"sport": "futebol", "event_name": "Chelsea vs Arsenal", "platform": "Betano", "amount": 35, "odds": 3.00, "result": "loss", "emotion": "😤"},
|
||
{"sport": "basquete", "event_name": "Heat vs 76ers", "platform": "Bet365", "amount": 20, "odds": 2.15, "result": "pending", "emotion": "😎"},
|
||
{"sport": "UFC", "event_name": "UFC 312: Du Plessis vs Strickland", "platform": "Betano", "amount": 25, "odds": 1.80, "result": "pending", "emotion": "🤑"},
|
||
{"sport": "futebol", "event_name": "Atlético MG vs Cruzeiro", "platform": "Bet365", "amount": 15, "odds": 2.30, "result": "win", "emotion": "😎"},
|
||
{"sport": "futebol", "event_name": "Juventus vs Milan", "platform": "Betano", "amount": 20, "odds": 2.60, "result": "loss", "emotion": "😰"},
|
||
{"sport": "basquete", "event_name": "Suns vs Nuggets", "platform": "Bet365", "amount": 30, "odds": 2.00, "result": "pending", "emotion": "😎"},
|
||
]
|
||
|
||
async def seed():
|
||
async with engine.begin() as conn:
|
||
await conn.run_sync(Base.metadata.drop_all)
|
||
await conn.run_sync(Base.metadata.create_all)
|
||
|
||
async with async_session() as db:
|
||
# Create demo user
|
||
user = User(
|
||
email="demo@midas.com",
|
||
password_hash=hash_password("Midas@2026"),
|
||
name="Demo User",
|
||
plan="premium",
|
||
streak_days=7,
|
||
total_points=185,
|
||
risk_level="yellow"
|
||
)
|
||
db.add(user)
|
||
await db.flush()
|
||
|
||
# Bankroll
|
||
bankroll = Bankroll(
|
||
user_id=user.id,
|
||
monthly_budget=500,
|
||
weekly_limit=150,
|
||
daily_limit=50,
|
||
bet_max_pct=5,
|
||
month_spent=245,
|
||
week_spent=85,
|
||
day_spent=30
|
||
)
|
||
db.add(bankroll)
|
||
|
||
# Achievements
|
||
achievement_objs = []
|
||
for a in ACHIEVEMENTS:
|
||
obj = Achievement(**a)
|
||
db.add(obj)
|
||
achievement_objs.append(obj)
|
||
await db.flush()
|
||
|
||
# Unlock first 5 achievements for demo user
|
||
for a in achievement_objs[:5]:
|
||
ua = UserAchievement(user_id=user.id, achievement_id=a.id)
|
||
db.add(ua)
|
||
|
||
# Lessons
|
||
lesson_objs = []
|
||
for l in LESSONS:
|
||
obj = Lesson(**l)
|
||
db.add(obj)
|
||
lesson_objs.append(obj)
|
||
await db.flush()
|
||
|
||
# Complete first 3 lessons for demo
|
||
for l in lesson_objs[:3]:
|
||
ul = UserLesson(user_id=user.id, lesson_id=l.id)
|
||
db.add(ul)
|
||
|
||
# Bets (spread over last 30 days)
|
||
now = datetime.utcnow()
|
||
for i, b in enumerate(DEMO_BETS):
|
||
days_ago = 30 - (i * 1.5)
|
||
created = now - timedelta(days=days_ago, hours=random.randint(8, 22))
|
||
profit = 0
|
||
if b["result"] == "win":
|
||
profit = b["amount"] * (b["odds"] - 1)
|
||
elif b["result"] == "loss":
|
||
profit = -b["amount"]
|
||
bet = Bet(
|
||
user_id=user.id, sport=b["sport"], event_name=b["event_name"],
|
||
platform=b["platform"], amount=b["amount"], odds=b["odds"],
|
||
result=b["result"], profit=profit, emotion=b["emotion"],
|
||
created_at=created
|
||
)
|
||
db.add(bet)
|
||
|
||
# Some alerts
|
||
alerts = [
|
||
Alert(user_id=user.id, type="recovery", severity="red", message="Aposta de recuperação detectada após perda em Liverpool vs Man City"),
|
||
Alert(user_id=user.id, type="limit", severity="yellow", message="Você já usou 49% do limite mensal"),
|
||
Alert(user_id=user.id, type="streak", severity="green", message="Parabéns! 7 dias sem aposta impulsiva 🔥"),
|
||
]
|
||
for a in alerts:
|
||
db.add(a)
|
||
|
||
await db.commit()
|
||
print("✅ Seed data created successfully!")
|
||
print(f" User: demo@midas.com / Midas@2026")
|
||
print(f" {len(DEMO_BETS)} bets, {len(ACHIEVEMENTS)} achievements, {len(LESSONS)} lessons")
|
||
|
||
if __name__ == "__main__":
|
||
asyncio.run(seed())
|