218 lines
8.5 KiB
Python
218 lines
8.5 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.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."""
|
|
if not check_admin(request):
|
|
return redirect('admin_login')
|
|
|
|
lottery = get_object_or_404(Lottery, id=lottery_id)
|
|
# 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 (há quantos sorteios o número não sai)
|
|
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)
|
|
scores = []
|
|
for num in range(1, lottery.max_number + 1):
|
|
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 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.2)]]
|
|
|
|
lottery.ai_predictions = ",".join(top_numbers)
|
|
lottery.save()
|
|
|
|
messages.success(request, f"IA calculou as probabilidades para {lottery.get_name_display()} com sucesso!")
|
|
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)
|
|
hot_numbers = ai_hot if ai_hot else [n for n, c in frequency.most_common(10)]
|
|
cold_numbers = [n for n in range(1, lottery_obj.max_number + 1) if n not in hot_numbers and n not in annulled]
|
|
|
|
suggestions = []
|
|
# Se for 1 Trilhão, usamos simulação de Monte Carlo para gerar apenas as 6 "Melhores de Elite"
|
|
is_trillion = (games_to_generate >= 1_000_000_000_000)
|
|
actual_games_to_gen = 6 if is_trillion else games_to_generate
|
|
|
|
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,
|
|
"suggestions": suggestions,
|
|
"hot_numbers": hot_numbers,
|
|
"cold_numbers": cold_numbers[:15], # Amostra de frios
|
|
"annulled_numbers": annulled,
|
|
}
|
|
|
|
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)
|