727 lines
31 KiB
Python
727 lines
31 KiB
Python
import math
|
|
import os
|
|
import platform
|
|
import random
|
|
from collections import Counter
|
|
|
|
from django import get_version as django_version
|
|
from django.shortcuts import render, redirect, get_object_or_404
|
|
from django.http import JsonResponse
|
|
import json
|
|
from django.utils import timezone
|
|
from django.contrib import messages
|
|
|
|
from .forms import LotterySimulatorForm
|
|
from .models import Lottery, DrawResult, AdminAccess
|
|
|
|
def check_admin(request):
|
|
"""Verifica se a chave privada na sessão é válida."""
|
|
key = request.session.get('admin_key')
|
|
return AdminAccess.objects.filter(private_key=key).exists()
|
|
|
|
def admin_login(request):
|
|
"""Tela de login simples para a Chave Privada."""
|
|
if request.method == 'POST':
|
|
key = request.POST.get('private_key')
|
|
if AdminAccess.objects.filter(private_key=key).exists():
|
|
request.session['admin_key'] = key
|
|
return redirect('admin_dashboard')
|
|
else:
|
|
messages.error(request, "Chave Privada Inválida!")
|
|
return render(request, 'core/admin_login.html')
|
|
|
|
def admin_logout(request):
|
|
request.session.flush()
|
|
return redirect('home')
|
|
|
|
def admin_dashboard(request):
|
|
"""Painel principal do administrador."""
|
|
if not check_admin(request):
|
|
return redirect('admin_login')
|
|
|
|
loterias = Lottery.objects.all()
|
|
return render(request, 'core/admin_dashboard.html', {'loterias': loterias})
|
|
|
|
def edit_lottery(request, lottery_id):
|
|
"""Editor específico para cada jogo (anular números)."""
|
|
if not check_admin(request):
|
|
return redirect('admin_login')
|
|
|
|
lottery = get_object_or_404(Lottery, id=lottery_id)
|
|
# Ajuste para Lotomania (0-99 se necessário, mas mantendo 1-100 por padrão)
|
|
numbers = range(1, lottery.max_number + 1)
|
|
annulled = [int(n) for n in lottery.annulled_numbers.split(',') if n]
|
|
|
|
if request.method == 'POST':
|
|
selected_numbers = request.POST.getlist('numbers')
|
|
lottery.annulled_numbers = ",".join(selected_numbers)
|
|
lottery.save()
|
|
messages.success(request, f"Configurações da {lottery.get_name_display()} salvas!")
|
|
return redirect('admin_dashboard')
|
|
|
|
return render(request, 'core/edit_lottery.html', {
|
|
'lottery': lottery,
|
|
'numbers': numbers,
|
|
'annulled': annulled
|
|
})
|
|
|
|
def _format_odds(total_combinations):
|
|
if total_combinations >= 1_000_000_000_000:
|
|
return f"1 em {total_combinations:.2e}"
|
|
return f"1 em {total_combinations:,}".replace(",", ".")
|
|
|
|
def _format_percent(odds):
|
|
percent = 100 / odds
|
|
if percent < 0.000001:
|
|
return "<0,000001%"
|
|
return f"{percent:.6f}%".replace(".", ",")
|
|
|
|
def ai_auto_predict(request, lottery_id):
|
|
"""Calcula automaticamente os números quentes via IA baseada em estatísticas reais e excluindo anulados."""
|
|
if not check_admin(request):
|
|
return redirect('admin_login')
|
|
|
|
lottery = get_object_or_404(Lottery, id=lottery_id)
|
|
annulled = [int(n) for n in lottery.annulled_numbers.split(',') if n]
|
|
|
|
# Pega todos os sorteios (Histórico Completo)
|
|
draws_db = DrawResult.objects.filter(lottery=lottery)
|
|
|
|
if not draws_db.exists():
|
|
messages.warning(request, "Não há sorteios reais cadastrados para análise completa.")
|
|
return redirect('edit_lottery', lottery_id=lottery_id)
|
|
|
|
# Lógica de IA: Frequência + Atraso (Delay)
|
|
draw_lists = [[int(n) for n in d.numbers.split(',')] for d in draws_db]
|
|
frequency = Counter(number for draw in draw_lists for number in draw)
|
|
|
|
# Calcular atraso
|
|
last_seen = {}
|
|
for i, draw in enumerate(reversed(draw_lists)):
|
|
for num in draw:
|
|
if num not in last_seen:
|
|
last_seen[num] = i
|
|
|
|
# Score IA = (Frequência * 0.7) + (Atraso * 0.3)
|
|
# SISTEMA ROTATIVO: Ignora números anulados no cálculo
|
|
scores = []
|
|
for num in range(1, lottery.max_number + 1):
|
|
if num in annulled:
|
|
continue
|
|
freq = frequency.get(num, 0)
|
|
delay = last_seen.get(num, len(draw_lists))
|
|
score = (freq * 0.7) + (delay * 0.3)
|
|
scores.append((num, score))
|
|
|
|
# Pega os 20% melhores números restantes como "IA Hot"
|
|
scores.sort(key=lambda x: x[1], reverse=True)
|
|
top_numbers = [str(n[0]) for n in scores[:int(lottery.max_number * 0.25)]]
|
|
|
|
lottery.ai_predictions = ",".join(top_numbers)
|
|
lottery.save()
|
|
|
|
messages.success(request, f"IA Rotativa: Probabilidades calculadas ignorando {len(annulled)} números anulados!")
|
|
return redirect('edit_lottery', lottery_id=lottery_id)
|
|
|
|
def home(request):
|
|
"""Gera números a serem sorteados com base em análise matemática."""
|
|
host_name = request.get_host().lower()
|
|
agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic"
|
|
now = timezone.now()
|
|
|
|
loterias_db = Lottery.objects.all()
|
|
lottery_choices = [(l.name, l.get_name_display()) for l in loterias_db]
|
|
|
|
lottery_cards = []
|
|
for l in loterias_db:
|
|
total_combinations = math.comb(l.max_number, l.numbers_to_draw)
|
|
lottery_cards.append({
|
|
"key": l.name,
|
|
"label": l.get_name_display(),
|
|
"tagline": f"{l.numbers_to_draw} dezenas entre {l.max_number}",
|
|
"range_max": l.max_number,
|
|
"picks": l.numbers_to_draw,
|
|
"odds": _format_odds(total_combinations),
|
|
})
|
|
|
|
form = LotterySimulatorForm(
|
|
request.POST or None,
|
|
lottery_choices=lottery_choices,
|
|
)
|
|
result = None
|
|
|
|
if form.is_valid():
|
|
lottery_key = form.cleaned_data["lottery_type"]
|
|
draws_to_consider = int(form.cleaned_data["draws_to_consider"])
|
|
games_to_generate = int(form.cleaned_data["games_to_generate"])
|
|
|
|
lottery_obj = Lottery.objects.get(name=lottery_key)
|
|
annulled = [int(n) for n in lottery_obj.annulled_numbers.split(',') if n]
|
|
ai_hot = [int(n) for n in lottery_obj.ai_predictions.split(',') if n]
|
|
|
|
# Filtro de histórico (0 = Todos)
|
|
if draws_to_consider == 0:
|
|
draws_db = DrawResult.objects.filter(lottery=lottery_obj)
|
|
else:
|
|
draws_db = DrawResult.objects.filter(lottery=lottery_obj)[:draws_to_consider]
|
|
|
|
draw_lists = [[int(n) for n in d.numbers.split(',')] for d in draws_db]
|
|
|
|
# Fallback para dados simulados se vazio
|
|
if not draw_lists:
|
|
rng_mock = random.Random(42)
|
|
pop = list(range(1, lottery_obj.max_number + 1))
|
|
for _ in range(50): # Simula 50 sorteios para cálculo
|
|
draw_lists.append(rng_mock.sample(pop, lottery_obj.numbers_to_draw))
|
|
|
|
frequency = Counter(number for draw in draw_lists for number in draw)
|
|
numbers = [n for n in range(1, lottery_obj.max_number + 1) if n not in annulled]
|
|
|
|
# Definição de Cores: Verde (Hot) e Vermelho (Frio)
|
|
# MATEMÁTICA AO VIVO: Calcula a elite estatística no momento do acesso, ignorando anulados.
|
|
available_numbers = [n for n in range(1, lottery_obj.max_number + 1) if n not in annulled]
|
|
|
|
# IA DE RECLAMAÇÃO: Identifica números anulados com alta probabilidade de sorteio imediato
|
|
# Calculamos o atraso (delay) para todos os números
|
|
last_seen_all = {}
|
|
for i, draw in enumerate(reversed(draw_lists)):
|
|
for num in draw:
|
|
if num not in last_seen_all:
|
|
last_seen_all[num] = i
|
|
|
|
# Score de Reclamação para Anulados
|
|
reclaimed_numbers = []
|
|
for n in annulled:
|
|
freq = frequency.get(n, 0)
|
|
delay = last_seen_all.get(n, len(draw_lists))
|
|
# Se o número tem frequência alta E está "atrasado", ele é um forte candidato a voltar
|
|
reclaim_score = (freq * 0.6) + (delay * 0.4)
|
|
# Se o score dele estiver no topo 15% de probabilidade global, nós o "reclamamos"
|
|
if reclaim_score > (max(frequency.values()) * 0.8):
|
|
reclaimed_numbers.append(n)
|
|
|
|
# Ordena os números disponíveis pela frequência (quentes primeiro)
|
|
sorted_by_freq = sorted(available_numbers, key=lambda n: frequency.get(n, 0), reverse=True)
|
|
|
|
# A "Elite Verde" será composta pelos top 25% dos números mais frequentes disponíveis
|
|
hot_count = max(6, int(len(available_numbers) * 0.25))
|
|
hot_numbers = sorted_by_freq[:hot_count]
|
|
|
|
cold_numbers = [n for n in sorted_by_freq[hot_count:]]
|
|
|
|
suggestions = []
|
|
# Suporte a múltiplas escalas de Trilhões
|
|
is_trillion = (games_to_generate >= 1_000_000_000_000)
|
|
actual_games_to_gen = 6 if is_trillion else games_to_generate
|
|
|
|
trillion_label = ""
|
|
if games_to_generate == 1_000_000_000_000: trillion_label = "1 Trilhão"
|
|
elif games_to_generate == 10_000_000_000_000: trillion_label = "10 Trilhões"
|
|
elif games_to_generate == 30_000_000_000_000: trillion_label = "30 Trilhões"
|
|
elif games_to_generate == 60_000_000_000_000: trillion_label = "60 Trilhões"
|
|
|
|
for _ in range(actual_games_to_gen):
|
|
if len(numbers) >= lottery_obj.numbers_to_draw:
|
|
temp_numbers = numbers.copy()
|
|
game = []
|
|
for _p in range(lottery_obj.numbers_to_draw):
|
|
ws = [frequency.get(n, 0) * 2 if n in hot_numbers else frequency.get(n, 0) + 1 for n in temp_numbers]
|
|
idx = random.choices(range(len(temp_numbers)), weights=ws, k=1)[0]
|
|
game.append(temp_numbers[idx])
|
|
del temp_numbers[idx]
|
|
suggestions.append(sorted(game))
|
|
|
|
total_combinations = math.comb(lottery_obj.max_number, lottery_obj.numbers_to_draw)
|
|
|
|
result = {
|
|
"lottery": lottery_obj.get_name_display(),
|
|
"draws_used": len(draw_lists),
|
|
"is_trillion": is_trillion,
|
|
"trillion_label": trillion_label,
|
|
"suggestions": suggestions,
|
|
"hot_numbers": hot_numbers,
|
|
"cold_numbers": cold_numbers[:15], # Amostra de frios
|
|
"annulled_numbers": annulled,
|
|
"reclaimed_numbers": reclaimed_numbers,
|
|
}
|
|
|
|
context = {
|
|
"project_name": "Gerador de Números Sorteados",
|
|
"project_description": "IA de Alta Performance para Loterias - Histórico Completo",
|
|
"agent_brand": agent_brand,
|
|
"django_version": django_version(),
|
|
"python_version": platform.python_version(),
|
|
"current_time": now,
|
|
"form": form,
|
|
"result": result,
|
|
"lottery_cards": lottery_cards,
|
|
"is_admin": check_admin(request),
|
|
}
|
|
return render(request, "core/index.html", context)
|
|
|
|
def live_math(request):
|
|
"""Calcula a probabilidade matemática ao vivo para números selecionados manualmente."""
|
|
if request.method == "POST":
|
|
try:
|
|
data = json.loads(request.body)
|
|
lottery_key = data.get("lottery")
|
|
manual_numbers = [int(n) for n in data.get("numbers", [])]
|
|
|
|
if not manual_numbers or len(manual_numbers) < 6:
|
|
return JsonResponse({"error": "Selecione ao menos 6 números."}, status=400)
|
|
|
|
lottery = get_object_or_404(Lottery, name=lottery_key)
|
|
draws_db = DrawResult.objects.filter(lottery=lottery)
|
|
|
|
# Se não houver sorteios, usamos uma base simulada para a matemática ao vivo
|
|
if not draws_db.exists():
|
|
frequency = {n: random.randint(5, 15) for n in range(1, lottery.max_number + 1)}
|
|
else:
|
|
draw_lists = [[int(n) for n in d.numbers.split(',')] for d in draws_db]
|
|
frequency = Counter(number for draw in draw_lists for number in draw)
|
|
|
|
ai_hot = [int(n) for n in lottery.ai_predictions.split(',') if n]
|
|
annulled = [int(n) for n in lottery.annulled_numbers.split(',') if n]
|
|
|
|
# Cálculo de "Calor" (Heat Score)
|
|
# 1. Quantos são Verdes (IA Hot)
|
|
hits_hot = len([n for n in manual_numbers if n in ai_hot])
|
|
# 2. Quantos foram anulados (Erro crítico)
|
|
hits_annulled = len([n for n in manual_numbers if n in annulled])
|
|
|
|
# Média de frequência dos números escolhidos
|
|
avg_freq = sum(frequency.get(n, 0) for n in manual_numbers) / len(manual_numbers)
|
|
max_freq = max(frequency.values()) if frequency else 1
|
|
|
|
# Score final de 0 a 100
|
|
score = (hits_hot / len(manual_numbers) * 50) + (avg_freq / max_freq * 50)
|
|
if hits_annulled > 0:
|
|
score = score * (1 - (hits_annulled / len(manual_numbers)))
|
|
|
|
return JsonResponse({
|
|
"score": round(score, 2),
|
|
"hits_hot": hits_hot,
|
|
"hits_annulled": hits_annulled,
|
|
"message": "Cálculo Matemático Ao Vivo Concluído!",
|
|
"status": "success" if score > 50 else "warning"
|
|
})
|
|
except Exception as e:
|
|
return JsonResponse({"error": str(e)}, status=500)
|
|
return JsonResponse({"error": "Método inválido"}, status=405)
|
|
|
|
def sequential_generator(request):
|
|
"""Página do Gerador Sequencial Inteligente."""
|
|
loterias = Lottery.objects.all()
|
|
return render(request, "core/sequential_generator.html", {"loterias": loterias})
|
|
|
|
def lottery_info_api(request, lottery_key):
|
|
"""Retorna informações de IA para uma loteria específica via JSON."""
|
|
lottery = get_object_or_404(Lottery, name=lottery_key)
|
|
annulled = [int(n) for n in lottery.annulled_numbers.split(',') if n]
|
|
|
|
# Pegamos os sorteios para o Radar de Reclamação
|
|
draws_db = DrawResult.objects.filter(lottery=lottery)
|
|
draw_lists = [[int(n) for n in d.numbers.split(',')] for d in draws_db]
|
|
|
|
if not draw_lists:
|
|
# Mock se vazio
|
|
frequency = {n: random.randint(1, 10) for n in range(1, lottery.max_number + 1)}
|
|
else:
|
|
frequency = Counter(number for draw in draw_lists for number in draw)
|
|
|
|
# Lógica de Elite Verde (Top 25% dos disponíveis)
|
|
available_numbers = [n for n in range(1, lottery.max_number + 1) if n not in annulled]
|
|
sorted_by_freq = sorted(available_numbers, key=lambda n: frequency.get(n, 0), reverse=True)
|
|
hot_count = max(6, int(len(available_numbers) * 0.25))
|
|
elite_greens = sorted_by_freq[:hot_count]
|
|
|
|
# IA Radar de Reclamação
|
|
last_seen_all = {}
|
|
for i, draw in enumerate(reversed(draw_lists)):
|
|
for num in draw:
|
|
if num not in last_seen_all:
|
|
last_seen_all[num] = i
|
|
|
|
reclaimed = []
|
|
for n in annulled:
|
|
freq = frequency.get(n, 0)
|
|
delay = last_seen_all.get(n, len(draw_lists))
|
|
reclaim_score = (freq * 0.6) + (delay * 0.4)
|
|
if reclaim_score > (max(frequency.values() or [1]) * 0.8):
|
|
reclaimed.append(n)
|
|
|
|
return JsonResponse({
|
|
"name": lottery.get_name_display(),
|
|
"max_number": lottery.max_number,
|
|
"numbers_to_draw": lottery.numbers_to_draw,
|
|
"elite_greens": elite_greens,
|
|
"annulled_numbers": [n for n in annulled if n not in reclaimed],
|
|
"reclaimed_numbers": reclaimed,
|
|
})
|
|
|
|
import requests
|
|
from datetime import datetime
|
|
|
|
def sync_results():
|
|
"""Busca resultados reais das loterias brasileiras via API pública."""
|
|
loterias = Lottery.objects.all()
|
|
# Mapeamento de nomes internos para nomes da API
|
|
mapping = {
|
|
'mega_sena': 'megasena',
|
|
'quina': 'quina',
|
|
'dupla_sena': 'duplasena',
|
|
'lotomania': 'lotomania',
|
|
'lotofacil': 'lotofacil',
|
|
'timemania': 'timemania',
|
|
'dia_de_sorte': 'diadesorte',
|
|
'federal': 'federal',
|
|
'super_sete': 'supersete',
|
|
'maismilionaria': 'maismilionaria',
|
|
}
|
|
|
|
for lottery in loterias:
|
|
api_name = mapping.get(lottery.name)
|
|
if not api_name: continue
|
|
|
|
try:
|
|
# URL da API de Loterias (Exemplo de API estável e pública)
|
|
response = requests.get(f"https://loteriascaixa-api.herokuapp.com/api/{api_name}/latest", timeout=5)
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
draw_number = int(data.get('concurso'))
|
|
|
|
# Verifica se já temos esse concurso
|
|
if not DrawResult.objects.filter(lottery=lottery, draw_number=draw_number).exists():
|
|
# Processa a data (formato DD/MM/YYYY)
|
|
date_str = data.get('data')
|
|
draw_date = datetime.strptime(date_str, "%d/%m/%Y").date()
|
|
|
|
# Processa os números (preserva zeros à esquerda para Federal/Super Sete)
|
|
numbers_list = data.get('dezenas', [])
|
|
# Se for Federal ou Super Sete, mantemos a formatação original (zeros à esquerda)
|
|
if lottery.name in ['federal', 'super_sete']:
|
|
numbers_str = ",".join([str(n).zfill(5 if lottery.name == 'federal' else 1) for n in numbers_list])
|
|
else:
|
|
numbers_str = ",".join([str(int(n)) for n in numbers_list])
|
|
|
|
DrawResult.objects.create(
|
|
lottery=lottery,
|
|
draw_number=draw_number,
|
|
draw_date=draw_date,
|
|
numbers=numbers_str
|
|
)
|
|
print(f"Sincronizado: {lottery.name} Concurso {draw_number}")
|
|
except Exception as e:
|
|
print(f"Erro ao sincronizar {lottery.name}: {e}")
|
|
|
|
from django.http import JsonResponse, HttpResponse
|
|
import json
|
|
|
|
def download_funnel(request, lottery_id):
|
|
"""Gera um arquivo TXT com a elite do funil para download."""
|
|
lottery = get_object_or_404(Lottery, id=lottery_id)
|
|
all_draws = DrawResult.objects.filter(lottery=lottery).order_by('-draw_number')
|
|
|
|
if not all_draws.exists():
|
|
return HttpResponse("Ainda não há dados para gerar o funil.")
|
|
|
|
# Repete a lógica do funil para garantir sincronia total
|
|
current_draw = all_draws.first()
|
|
all_numbers_flat = []
|
|
for d in all_draws:
|
|
all_numbers_flat.extend([n.strip() for n in d.numbers.split(',')])
|
|
|
|
frequency = Counter(all_numbers_flat)
|
|
last_seen = {}
|
|
for i, d in enumerate(all_draws):
|
|
for n in d.numbers.split(','):
|
|
n = n.strip()
|
|
if n not in last_seen: last_seen[n] = i
|
|
|
|
if lottery.name == 'federal':
|
|
available = [str(i).zfill(5) for i in range(100000)]
|
|
elif lottery.name == 'super_sete':
|
|
available = [str(i) for i in range(10)]
|
|
else:
|
|
available = [str(i).zfill(2) for i in range(1, lottery.max_number + 1)]
|
|
|
|
last_nums = [n.strip() for n in current_draw.numbers.split(',')]
|
|
funnel_scores = []
|
|
for n in available:
|
|
if n in last_nums: continue
|
|
score = (frequency.get(n, 0) * 0.6) + (last_seen.get(n, all_draws.count()) * 0.4)
|
|
funnel_scores.append((n, score))
|
|
|
|
funnel_scores.sort(key=lambda x: x[1], reverse=True)
|
|
elite = [x[0] for x in funnel_scores[:lottery.numbers_to_draw]]
|
|
elite.sort()
|
|
|
|
content = f"--- SUPERCOMPUTADOR IA V4.0 ---\n"
|
|
content += f"FUNIL DE PROBABILIDADES: {lottery.get_name_display()}\n"
|
|
content += f"DATA: {datetime.now().strftime('%d/%m/%Y %H:%M')}\n"
|
|
content += f"PRÓXIMO CONCURSO: {current_draw.draw_number + 1}\n"
|
|
content += f"-------------------------------\n"
|
|
content += f"NÚMEROS DE ELITE: {' - '.join(elite)}\n"
|
|
content += f"-------------------------------\n"
|
|
content += f"Use com responsabilidade matemática.\n"
|
|
|
|
response = HttpResponse(content, content_type='text/plain')
|
|
filename = f"funil_{lottery.name}_{current_draw.draw_number + 1}.txt"
|
|
response['Content-Disposition'] = f'attachment; filename="{filename}"'
|
|
return response
|
|
|
|
def lottery_results(request):
|
|
"""Central de Resultados com Lógica de Análise Estatística Profunda IA V4.0."""
|
|
# Otimização: Só sincroniza se passaram mais de 10 minutos desde o último sorteio salvo
|
|
# ou se o banco estiver muito vazio, para evitar lentidão no carregamento.
|
|
last_sync = request.session.get('last_sync_time')
|
|
now_ts = timezone.now().timestamp()
|
|
|
|
if not last_sync or (now_ts - last_sync) > 600: # 10 minutos
|
|
try:
|
|
sync_results()
|
|
request.session['last_sync_time'] = now_ts
|
|
except:
|
|
pass
|
|
|
|
loterias = Lottery.objects.all()
|
|
results_data = []
|
|
|
|
for lottery in loterias:
|
|
# Pega todos os sorteios para análise estatística real
|
|
all_draws = DrawResult.objects.filter(lottery=lottery).order_by('-draw_number')
|
|
last_draws = all_draws[:10]
|
|
|
|
if all_draws.exists():
|
|
current_draw = all_draws.first()
|
|
current_numbers_raw = current_draw.numbers.split(',')
|
|
current_numbers = []
|
|
for n in current_numbers_raw:
|
|
if lottery.name == 'federal':
|
|
current_numbers.append(n.zfill(5))
|
|
elif lottery.name == 'super_sete':
|
|
current_numbers.append(n)
|
|
else:
|
|
current_numbers.append(str(int(n)).zfill(2))
|
|
|
|
# --- MOTOR DE ANÁLISE ESTATÍSTICA ---
|
|
all_numbers_flat = []
|
|
for d in all_draws:
|
|
all_numbers_flat.extend([n.strip() for n in d.numbers.split(',')])
|
|
|
|
frequency = Counter(all_numbers_flat)
|
|
|
|
# Cálculo de Atraso (Delay) - Movido para cima para evitar erros no Funil
|
|
last_seen = {}
|
|
for i, d in enumerate(all_draws):
|
|
nums = [n.strip() for n in d.numbers.split(',')]
|
|
for n in nums:
|
|
if n not in last_seen:
|
|
last_seen[n] = i
|
|
|
|
# --- NOVO: FUNIL DE PROBABILIDADES AUTOMÁTICO IA V4.0 ---
|
|
# O Funil anula números automaticamente até encontrar a elite matemática
|
|
available_for_funnel = []
|
|
if lottery.name == 'federal':
|
|
# Para Federal, o funil gera bilhetes de elite
|
|
available_for_funnel = [str(i).zfill(5) for i in range(100000)]
|
|
elif lottery.name == 'super_sete':
|
|
available_for_funnel = [str(i) for i in range(10)]
|
|
else:
|
|
available_for_funnel = [str(i).zfill(2) for i in range(1, lottery.max_number + 1)]
|
|
|
|
# 1. Anula números do último sorteio (Filtro de Repetição)
|
|
last_nums = [n.strip() for n in current_draw.numbers.split(',')]
|
|
funnel_step1 = [n for n in available_for_funnel if n not in last_nums]
|
|
|
|
# 2. Ranking de Probabilidade (Frequência + Atraso)
|
|
funnel_scores = []
|
|
max_analyzed = all_draws.count()
|
|
for n in funnel_step1:
|
|
freq = frequency.get(n, 0)
|
|
delay = last_seen.get(n, max_analyzed)
|
|
# Score: 60% peso para frequência, 40% para tempo sem sair
|
|
score = (freq * 0.6) + (delay * 0.4)
|
|
funnel_scores.append((n, score))
|
|
|
|
# 3. Anulação Automática: Ordena e pega apenas a elite
|
|
funnel_scores.sort(key=lambda x: x[1], reverse=True)
|
|
|
|
# Para a elite, queremos exatamente o número de dezenas do jogo (ou 6 para Mega)
|
|
elite_count = lottery.numbers_to_draw
|
|
funnel_elite = [x[0] for x in funnel_scores[:elite_count]]
|
|
funnel_elite.sort()
|
|
|
|
# Formatação visual para o template
|
|
funnel_display = []
|
|
for n in funnel_elite:
|
|
if lottery.name == 'federal': funnel_display.append(n.zfill(5))
|
|
elif lottery.name == 'super_sete': funnel_display.append(n)
|
|
else: funnel_display.append(n.zfill(2))
|
|
|
|
# Top 10 mais frequentes
|
|
most_common = []
|
|
for num, freq in frequency.most_common(10):
|
|
if lottery.name == 'federal':
|
|
most_common.append((num.zfill(5), freq))
|
|
elif lottery.name == 'super_sete':
|
|
most_common.append((num, freq))
|
|
else:
|
|
most_common.append((num.zfill(2), freq))
|
|
|
|
# Cálculo de Atraso (Delay)
|
|
last_seen = {}
|
|
for i, d in enumerate(all_draws):
|
|
nums = [n.strip() for n in d.numbers.split(',')]
|
|
for n in nums:
|
|
if n not in last_seen:
|
|
last_seen[n] = i
|
|
|
|
# Pegar as 5 dezenas mais atrasadas
|
|
delays = []
|
|
if lottery.name == 'federal':
|
|
# Para federal, analisamos os números que já saíram nos prêmios
|
|
keys = list(frequency.keys())
|
|
for n in keys:
|
|
delays.append({'number': n.zfill(5), 'delay': last_seen.get(n, len(all_draws))})
|
|
elif lottery.name == 'super_sete':
|
|
for n in range(10):
|
|
delays.append({'number': str(n), 'delay': last_seen.get(str(n), len(all_draws))})
|
|
else:
|
|
for n in range(1, lottery.max_number + 1):
|
|
num_str = str(n).zfill(2)
|
|
delays.append({'number': num_str, 'delay': last_seen.get(num_str, len(all_draws))})
|
|
|
|
most_delayed = sorted(delays, key=lambda x: x['delay'], reverse=True)[:5]
|
|
|
|
# Equilíbrio Par/Ímpar do último sorteio
|
|
evens = len([n for n in current_numbers_raw if int(n) % 2 == 0])
|
|
odds = len(current_numbers_raw) - evens
|
|
|
|
# --- Lógica de Determinação Matemática V4.0 ---
|
|
# (Simplificada para suportar Federal e Super Sete de forma coerente)
|
|
random.seed(sum([int(n) for n in current_numbers_raw]) + current_draw.draw_number)
|
|
predicted_numbers = []
|
|
|
|
if lottery.name == 'federal':
|
|
# Predição para Federal: 5 novos bilhetes baseados em tendência
|
|
for _ in range(5):
|
|
pred = str(random.randint(0, 99999)).zfill(5)
|
|
predicted_numbers.append(pred)
|
|
elif lottery.name == 'super_sete':
|
|
# 7 colunas (0-9)
|
|
for _ in range(7):
|
|
predicted_numbers.append(str(random.randint(0, 9)))
|
|
else:
|
|
k_inheritance = max(1, len(current_numbers_raw)//3)
|
|
inheritance = random.sample([int(n) for n in current_numbers_raw], k=k_inheritance)
|
|
for n in inheritance:
|
|
shift = random.choice([-1, 0, 1])
|
|
new_n = n + shift
|
|
if 1 <= new_n <= lottery.max_number and str(new_n).zfill(2) not in predicted_numbers:
|
|
predicted_numbers.append(str(new_n).zfill(2))
|
|
|
|
available = [str(n).zfill(2) for n in range(1, lottery.max_number + 1) if str(n).zfill(2) not in predicted_numbers]
|
|
while len(predicted_numbers) < lottery.numbers_to_draw:
|
|
next_n = random.choice(available)
|
|
predicted_numbers.append(next_n)
|
|
available.remove(next_n)
|
|
predicted_numbers.sort()
|
|
|
|
results_data.append({
|
|
'lottery': lottery,
|
|
'last_result': current_draw,
|
|
'last_numbers': current_numbers,
|
|
'predicted_numbers': predicted_numbers,
|
|
'funnel_numbers': funnel_display,
|
|
'funnel_raw': ",".join(funnel_elite),
|
|
'next_draw_number': current_draw.draw_number + 1,
|
|
'history': last_draws,
|
|
'stats': {
|
|
'most_common': most_common,
|
|
'most_delayed': most_delayed,
|
|
'even_odd': f"{evens}P / {odds}Í",
|
|
'total_analyzed': all_draws.count()
|
|
}
|
|
})
|
|
else:
|
|
results_data.append({
|
|
'lottery': lottery,
|
|
'last_result': None,
|
|
'predicted_numbers': None
|
|
})
|
|
|
|
return render(request, "core/results_ia.html", {
|
|
"results": results_data,
|
|
"current_time": timezone.now()
|
|
})
|
|
|
|
def full_report(request):
|
|
"""Gera um relatório completo de probabilidades de todas as loterias em uma página dedicada."""
|
|
loterias = Lottery.objects.all()
|
|
report_data = []
|
|
|
|
for lottery in loterias:
|
|
all_draws = DrawResult.objects.filter(lottery=lottery).order_by('-draw_number')
|
|
|
|
if all_draws.exists():
|
|
current_draw = all_draws.first()
|
|
|
|
# Cálculo de Frequência
|
|
all_numbers_flat = []
|
|
for d in all_draws:
|
|
all_numbers_flat.extend([n.strip() for n in d.numbers.split(',')])
|
|
frequency = Counter(all_numbers_flat)
|
|
|
|
# Cálculo de Atraso (Delay)
|
|
last_seen = {}
|
|
for i, d in enumerate(all_draws):
|
|
for n in d.numbers.split(','):
|
|
n = n.strip()
|
|
if n not in last_seen: last_seen[n] = i
|
|
|
|
# Construção do Funil de Elite para o Relatório
|
|
if lottery.name == 'federal':
|
|
available = [str(i).zfill(5) for i in range(100000)]
|
|
elif lottery.name == 'super_sete':
|
|
available = [str(i) for i in range(10)]
|
|
else:
|
|
available = [str(i).zfill(2) for i in range(1, lottery.max_number + 1)]
|
|
|
|
last_nums = [n.strip() for n in current_draw.numbers.split(',')]
|
|
funnel_scores = []
|
|
max_analyzed = all_draws.count()
|
|
|
|
for n in available:
|
|
if n in last_nums: continue
|
|
freq = frequency.get(n, 0)
|
|
delay = last_seen.get(n, max_analyzed)
|
|
score = (freq * 0.6) + (delay * 0.4)
|
|
funnel_scores.append((n, score))
|
|
|
|
funnel_scores.sort(key=lambda x: x[1], reverse=True)
|
|
elite = [x[0] for x in funnel_scores[:lottery.numbers_to_draw]]
|
|
elite.sort()
|
|
|
|
# Probabilidade Matemática (Baseada no Score do Top 1)
|
|
top_score = funnel_scores[0][1] if funnel_scores else 0
|
|
prob_index = min(99.9, (top_score / (max(frequency.values() or [1]) * 1.5)) * 100)
|
|
|
|
report_data.append({
|
|
'lottery': lottery,
|
|
'last_concurso': current_draw.draw_number,
|
|
'elite_numbers': elite,
|
|
'prob_index': round(prob_index, 2),
|
|
'total_analyzed': max_analyzed,
|
|
'hot_top': frequency.most_common(5)
|
|
})
|
|
|
|
return render(request, "core/full_report.html", {
|
|
"reports": report_data,
|
|
"current_time": timezone.now()
|
|
})
|
|
|
|
def hits_report(request):
|
|
"""Exibe o relatório detalhado de acertos detectados pelo motor sequencial."""
|
|
return render(request, "core/hits_report.html")
|