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 home(request): """Render the landing screen with lottery simulator and insights.""" 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 = form.cleaned_data["draws_to_consider"] games_to_generate = 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] # Busca sorteios reais no banco draws_db = DrawResult.objects.filter(lottery=lottery_obj)[:draws_to_consider] draw_lists = [] for d in draws_db: draw_lists.append([int(n) for n in d.numbers.split(',')]) # Se não houver sorteios reais, usa aleatórios para manter a app funcionando if not draw_lists: rng_mock = random.Random(42) population = list(range(1, lottery_obj.max_number + 1)) for _ in range(draws_to_consider): draw_lists.append(rng_mock.sample(population, 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] # Números Quentes (Verde) e Frios (Vermelho) # Quentes: Maior frequência e não anulados hot_candidates = frequency.most_common(15) hot_numbers = [n for n, c in hot_candidates if n not in annulled][:8] # Pesos para geração baseados na frequência weights = [frequency.get(number, 0) + 1 for number in numbers] rng = random.Random(f"{lottery_key}-{draws_to_consider}-{games_to_generate}") suggestions = [] for _ in range(games_to_generate): if len(numbers) >= lottery_obj.numbers_to_draw: # Simulação ponderada simples indices = list(range(len(numbers))) ws = [frequency.get(numbers[i], 0) + 1 for i in indices] selected_indices = random.choices(indices, weights=ws, k=lottery_obj.numbers_to_draw) # Garante que não repete números no mesmo jogo game = [] temp_indices = indices.copy() for _p in range(lottery_obj.numbers_to_draw): ws_temp = [frequency.get(numbers[i], 0) + 1 for i in temp_indices] idx = random.choices(range(len(temp_indices)), weights=ws_temp, k=1)[0] game.append(numbers[temp_indices[idx]]) del temp_indices[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), "total_combinations": f"{total_combinations:,}".replace(",", "."), "odds": _format_odds(total_combinations), "percent": _format_percent(total_combinations), "suggestions": suggestions, "hot_numbers": hot_numbers, "annulled_numbers": annulled, } context = { "project_name": "LotoPulse", "project_description": "Análise matemática e editor de probabilidades para Loterias Caixa.", "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)