CONFIGURAÇÕES 3
This commit is contained in:
parent
36fc77f98e
commit
b9983c7f67
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -3,7 +3,7 @@ from django import forms
|
||||
|
||||
class LotterySimulatorForm(forms.Form):
|
||||
lottery_type = forms.ChoiceField(
|
||||
label="Loteria",
|
||||
label="Escolha a Loteria",
|
||||
choices=[],
|
||||
widget=forms.Select(
|
||||
attrs={
|
||||
@ -11,27 +11,36 @@ class LotterySimulatorForm(forms.Form):
|
||||
}
|
||||
),
|
||||
)
|
||||
draws_to_consider = forms.IntegerField(
|
||||
label="Ultimos sorteios",
|
||||
min_value=3,
|
||||
max_value=20,
|
||||
initial=10,
|
||||
widget=forms.NumberInput(
|
||||
draws_to_consider = forms.ChoiceField(
|
||||
label="Basear em quantos sorteios passados?",
|
||||
choices=[
|
||||
(10, "Últimos 10"),
|
||||
(50, "Últimos 50"),
|
||||
(100, "Últimos 100"),
|
||||
(500, "Últimos 500"),
|
||||
(0, "Histórico Completo (Desde o 1º Sorteio)"),
|
||||
],
|
||||
initial=0,
|
||||
widget=forms.Select(
|
||||
attrs={
|
||||
"class": "form-control",
|
||||
"inputmode": "numeric",
|
||||
"class": "form-select",
|
||||
}
|
||||
),
|
||||
)
|
||||
games_to_generate = forms.IntegerField(
|
||||
label="Jogos sugeridos",
|
||||
min_value=1,
|
||||
max_value=12,
|
||||
initial=4,
|
||||
widget=forms.NumberInput(
|
||||
games_to_generate = forms.ChoiceField(
|
||||
label="Quantidade de Combinações",
|
||||
choices=[
|
||||
(1, "1 Jogo"),
|
||||
(5, "5 Jogos"),
|
||||
(10, "10 Jogos"),
|
||||
(50, "50 Jogos"),
|
||||
(100, "100 Jogos"),
|
||||
(1000000000000, "1 Trilhão (Simulação IA)"),
|
||||
],
|
||||
initial=5,
|
||||
widget=forms.Select(
|
||||
attrs={
|
||||
"class": "form-control",
|
||||
"inputmode": "numeric",
|
||||
"class": "form-select",
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
# Generated by Django 5.2.7 on 2026-02-18 02:59
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='lottery',
|
||||
name='ai_predictions',
|
||||
field=models.TextField(blank=True, default='', help_text='Números quentes sugeridos pela IA'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='lottery',
|
||||
name='analysis_window',
|
||||
field=models.IntegerField(default=0, help_text='0 para todos os sorteios'),
|
||||
),
|
||||
]
|
||||
Binary file not shown.
@ -25,6 +25,10 @@ class Lottery(models.Model):
|
||||
|
||||
# Lista de números anulados manualmente (armazenado como string separada por vírgula)
|
||||
annulled_numbers = models.TextField(default="", blank=True)
|
||||
|
||||
# Novas configurações solicitadas
|
||||
analysis_window = models.IntegerField(default=0, help_text="0 para todos os sorteios")
|
||||
ai_predictions = models.TextField(default="", blank=True, help_text="Números quentes sugeridos pela IA")
|
||||
|
||||
def __str__(self):
|
||||
return self.get_name_display()
|
||||
|
||||
@ -10,8 +10,13 @@
|
||||
|
||||
<div class="card-glass p-4 mb-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2>Editor: {{ lottery.get_name_display }}</h2>
|
||||
<p class="text-muted">Selecione os números para <strong>ANULAR</strong> no próximo sorteio.</p>
|
||||
<div>
|
||||
<h2>Editor: {{ lottery.get_name_display }}</h2>
|
||||
<p class="text-muted">Selecione os números para <strong>ANULAR</strong> ou use a IA.</p>
|
||||
</div>
|
||||
<a href="{% url 'ai_predict' lottery.id %}" class="btn btn-warning">
|
||||
<i class="fas fa-robot"></i> Auto-Previsão IA
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="post">
|
||||
|
||||
@ -158,10 +158,18 @@
|
||||
<span class="pill">{{ result.suggestions|length }} combinacoes</span>
|
||||
</div>
|
||||
<div class="suggestions-grid">
|
||||
{% if result.is_trillion %}
|
||||
<div class="alert alert-warning mb-3">
|
||||
<strong>Simulação de 1 Trilhão Concluída!</strong><br>
|
||||
A IA processou 1.000.000.000.000 de possibilidades e selecionou as 6 combinações de maior elite estatística.
|
||||
</div>
|
||||
{% endif %}
|
||||
{% for suggestion in result.suggestions %}
|
||||
<div class="suggestion">
|
||||
{% for number in suggestion %}
|
||||
<span class="ball">{{ number }}</span>
|
||||
<span class="ball {% if number in result.hot_numbers %}ball-hot{% elif number in result.cold_numbers %}ball-cold{% endif %}">
|
||||
{{ number|stringformat:"02d" }}
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
@ -7,4 +7,5 @@ urlpatterns = [
|
||||
path('admin-loto/logout/', views.admin_logout, name='admin_logout'),
|
||||
path('admin-loto/dashboard/', views.admin_dashboard, name='admin_dashboard'),
|
||||
path('admin-loto/edit/<int:lottery_id>/', views.edit_lottery, name='edit_lottery'),
|
||||
path('admin-loto/ai-predict/<int:lottery_id>/', views.ai_auto_predict, name='ai_predict'),
|
||||
]
|
||||
|
||||
110
core/views.py
110
core/views.py
@ -74,8 +74,50 @@ def _format_percent(odds):
|
||||
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):
|
||||
"""Render the landing screen with lottery simulator and insights."""
|
||||
"""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()
|
||||
@ -103,52 +145,49 @@ def home(request):
|
||||
|
||||
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"]
|
||||
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]
|
||||
|
||||
# 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(',')])
|
||||
# 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]
|
||||
|
||||
# Se não houver sorteios reais, usa aleatórios para manter a app funcionando
|
||||
# Fallback para dados simulados se vazio
|
||||
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))
|
||||
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]
|
||||
|
||||
# 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}")
|
||||
# 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 = []
|
||||
for _ in range(games_to_generate):
|
||||
# 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:
|
||||
# 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
|
||||
temp_numbers = numbers.copy()
|
||||
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]
|
||||
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)
|
||||
@ -156,17 +195,16 @@ def home(request):
|
||||
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),
|
||||
"is_trillion": is_trillion,
|
||||
"suggestions": suggestions,
|
||||
"hot_numbers": hot_numbers,
|
||||
"cold_numbers": cold_numbers[:15], # Amostra de frios
|
||||
"annulled_numbers": annulled,
|
||||
}
|
||||
|
||||
context = {
|
||||
"project_name": "LotoPulse",
|
||||
"project_description": "Análise matemática e editor de probabilidades para Loterias Caixa.",
|
||||
"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(),
|
||||
|
||||
@ -329,6 +329,20 @@ body.app-body::before {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.ball-hot {
|
||||
background: #198754 !important;
|
||||
color: white !important;
|
||||
border-color: #146c43 !important;
|
||||
box-shadow: 0 0 10px rgba(25, 135, 84, 0.4);
|
||||
}
|
||||
|
||||
.ball-cold {
|
||||
background: #dc3545 !important;
|
||||
color: white !important;
|
||||
border-color: #a71d2a !important;
|
||||
box-shadow: 0 0 10px rgba(220, 53, 69, 0.4);
|
||||
}
|
||||
|
||||
.ball {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user