Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06385a65ab | ||
|
|
0a80d2023d | ||
|
|
2752a4bfe4 | ||
|
|
fe4c6d246a | ||
|
|
e8f96a35c9 | ||
|
|
81b268e90d | ||
|
|
78395f19fd | ||
|
|
24c53fb469 | ||
|
|
06e2196d32 | ||
|
|
28516985ed | ||
|
|
928c0dddfd | ||
|
|
688b6e4a0b | ||
|
|
40e64da82f | ||
|
|
b9983c7f67 | ||
|
|
36fc77f98e | ||
|
|
9f27edfd4b |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
core/__pycache__/forms.cpython-311.pyc
Normal file
BIN
core/__pycache__/forms.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
54
core/forms.py
Normal file
54
core/forms.py
Normal file
@ -0,0 +1,54 @@
|
||||
from django import forms
|
||||
|
||||
|
||||
class LotterySimulatorForm(forms.Form):
|
||||
lottery_type = forms.ChoiceField(
|
||||
label="Escolha a Loteria",
|
||||
choices=[],
|
||||
widget=forms.Select(
|
||||
attrs={
|
||||
"class": "form-select form-select-lg",
|
||||
}
|
||||
),
|
||||
)
|
||||
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-select",
|
||||
}
|
||||
),
|
||||
)
|
||||
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)"),
|
||||
(10000000000000, "10 Trilhões (Simulação Massiva)"),
|
||||
(30000000000000, "30 Trilhões (Simulação Elite)"),
|
||||
(60000000000000, "60 Trilhões (Poder Máximo)"),
|
||||
],
|
||||
initial=5,
|
||||
widget=forms.Select(
|
||||
attrs={
|
||||
"class": "form-select",
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
lottery_choices = kwargs.pop("lottery_choices", [])
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["lottery_type"].choices = lottery_choices
|
||||
49
core/migrations/0001_initial.py
Normal file
49
core/migrations/0001_initial.py
Normal file
@ -0,0 +1,49 @@
|
||||
# Generated by Django 5.2.7 on 2026-02-18 01:18
|
||||
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='AdminAccess',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('private_key', models.CharField(default=uuid.uuid4, max_length=255, unique=True)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Lottery',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(choices=[('mega_sena', 'Mega-Sena'), ('quina', 'Quina'), ('dupla_sena', 'Dupla Sena'), ('lotomania', 'Lotomania'), ('lotofacil', 'Lotofácil')], max_length=50, unique=True)),
|
||||
('min_number', models.IntegerField(default=1)),
|
||||
('max_number', models.IntegerField()),
|
||||
('numbers_to_draw', models.IntegerField()),
|
||||
('annulled_numbers', models.TextField(blank=True, default='')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DrawResult',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('draw_number', models.IntegerField()),
|
||||
('draw_date', models.DateField()),
|
||||
('numbers', models.CharField(max_length=255)),
|
||||
('lottery', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='draws', to='core.lottery')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-draw_date'],
|
||||
'unique_together': {('lottery', 'draw_number')},
|
||||
},
|
||||
),
|
||||
]
|
||||
@ -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'),
|
||||
),
|
||||
]
|
||||
18
core/migrations/0003_alter_lottery_name.py
Normal file
18
core/migrations/0003_alter_lottery_name.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.7 on 2026-02-19 22:09
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0002_lottery_ai_predictions_lottery_analysis_window'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='lottery',
|
||||
name='name',
|
||||
field=models.CharField(choices=[('mega_sena', 'Mega-Sena'), ('quina', 'Quina'), ('dupla_sena', 'Dupla Sena'), ('lotomania', 'Lotomania'), ('lotofacil', 'Lotofácil'), ('timemania', 'Timemania'), ('dia_de_sorte', 'Dia de Sorte'), ('federal', 'Federal'), ('super_sete', 'Super Sete'), ('maismilionaria', '+Milionária')], max_length=50, unique=True),
|
||||
),
|
||||
]
|
||||
BIN
core/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
BIN
core/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,3 +1,53 @@
|
||||
from django.db import models
|
||||
import uuid
|
||||
|
||||
# Create your models here.
|
||||
class AdminAccess(models.Model):
|
||||
"""Armazena a chave privada única para acesso ao painel."""
|
||||
private_key = models.CharField(max_length=255, unique=True, default=uuid.uuid4)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"Key created on {self.created_at}"
|
||||
|
||||
class Lottery(models.Model):
|
||||
"""Configurações específicas de cada tipo de loteria."""
|
||||
LOTTERY_TYPES = [
|
||||
('mega_sena', 'Mega-Sena'),
|
||||
('quina', 'Quina'),
|
||||
('dupla_sena', 'Dupla Sena'),
|
||||
('lotomania', 'Lotomania'),
|
||||
('lotofacil', 'Lotofácil'),
|
||||
('timemania', 'Timemania'),
|
||||
('dia_de_sorte', 'Dia de Sorte'),
|
||||
('federal', 'Federal'),
|
||||
('super_sete', 'Super Sete'),
|
||||
('maismilionaria', '+Milionária'),
|
||||
]
|
||||
name = models.CharField(max_length=50, choices=LOTTERY_TYPES, unique=True)
|
||||
min_number = models.IntegerField(default=1)
|
||||
max_number = models.IntegerField()
|
||||
numbers_to_draw = models.IntegerField()
|
||||
|
||||
# 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()
|
||||
|
||||
class DrawResult(models.Model):
|
||||
"""Resultados reais dos sorteios da Caixa."""
|
||||
lottery = models.ForeignKey(Lottery, on_delete=models.CASCADE, related_name='draws')
|
||||
draw_number = models.IntegerField()
|
||||
draw_date = models.DateField()
|
||||
numbers = models.CharField(max_length=255) # Ex: "05,12,34,45,56,59"
|
||||
|
||||
class Meta:
|
||||
unique_together = ('lottery', 'draw_number')
|
||||
ordering = ['-draw_date']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.lottery.name} - Concurso {self.draw_number}"
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="pt-br">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{% block title %}Knowledge Base{% endblock %}</title>
|
||||
{% if project_description %}
|
||||
<meta name="description" content="{{ project_description }}">
|
||||
@ -14,12 +15,26 @@
|
||||
<meta property="twitter:image" content="{{ project_image_url }}">
|
||||
{% endif %}
|
||||
{% load static %}
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Work+Sans:wght@400;500;600&display=swap"
|
||||
rel="stylesheet">
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet"
|
||||
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
|
||||
crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="{% static 'css/custom.css' %}?v={{ deployment_timestamp }}">
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body class="app-body">
|
||||
{% block content %}{% endblock %}
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
|
||||
crossorigin="anonymous"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
21
core/templates/core/admin_dashboard.html
Normal file
21
core/templates/core/admin_dashboard.html
Normal file
@ -0,0 +1,21 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="container mt-5 pt-5">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2>Painel Administrativo</h2>
|
||||
<a href="{% url 'admin_logout' %}" class="btn btn-ghost">Sair</a>
|
||||
</div>
|
||||
<div class="row g-4">
|
||||
{% for lottery in loterias %}
|
||||
<div class="col-md-4">
|
||||
<div class="card-soft p-4 h-100">
|
||||
<h3>{{ lottery.get_name_display }}</h3>
|
||||
<p class="text-muted">Números: 1 a {{ lottery.max_number }}</p>
|
||||
<hr>
|
||||
<a href="{% url 'edit_lottery' lottery.id %}" class="btn btn-brand w-100">Editar Números</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
25
core/templates/core/admin_login.html
Normal file
25
core/templates/core/admin_login.html
Normal file
@ -0,0 +1,25 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="container mt-5 pt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card-glass p-5 text-center">
|
||||
<h2 class="mb-4">Acesso Administrativo</h2>
|
||||
<p class="text-muted">Insira sua Private Key para acessar as configurações.</p>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="mb-4">
|
||||
<input type="password" name="private_key" class="form-control form-control-lg text-center" placeholder="Chave Privada" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-brand btn-lg w-100">Acessar Painel</button>
|
||||
</form>
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-danger mt-3">{{ message }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
79
core/templates/core/edit_lottery.html
Normal file
79
core/templates/core/edit_lottery.html
Normal file
@ -0,0 +1,79 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="container mt-5 pt-5">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{% url 'admin_dashboard' %}">Painel</a></li>
|
||||
<li class="breadcrumb-item active">{{ lottery.get_name_display }}</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="card-glass p-4 mb-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<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>
|
||||
<div class="text-end">
|
||||
<a href="{% url 'ai_predict' lottery.id %}" class="btn btn-warning mb-1">
|
||||
<i class="fas fa-robot"></i> Auto-Previsão IA Rotativa
|
||||
</a>
|
||||
<br>
|
||||
<small class="text-warning" style="font-size: 0.75rem;">Recalcula Verdes ignorando Anulados</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="lottery-grid mb-4">
|
||||
{% for n in numbers %}
|
||||
<div class="form-check number-toggle">
|
||||
<input class="form-check-input d-none" type="checkbox" name="numbers" value="{{ n }}" id="num_{{ n }}" {% if n in annulled %}checked{% endif %}>
|
||||
<label class="number-ball {% if n in annulled %}annulled{% endif %}" for="num_{{ n }}">
|
||||
{{ n|stringformat:"02d" }}
|
||||
</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-brand btn-lg">Salvar Configurações</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.lottery-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(60px, 1fr));
|
||||
gap: 15px;
|
||||
background: rgba(255,255,255,0.05);
|
||||
padding: 20px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
.number-ball {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
background: #0b1f2a;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
border: 2px solid rgba(255,255,255,0.1);
|
||||
font-weight: bold;
|
||||
}
|
||||
.number-ball:hover {
|
||||
background: #0f766e;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.number-toggle input:checked + .number-ball {
|
||||
background: #dc3545 !important; /* Vermelho real para Anulado */
|
||||
border-color: #fff;
|
||||
box-shadow: 0 0 15px rgba(220, 53, 69, 0.5);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
136
core/templates/core/full_report.html
Normal file
136
core/templates/core/full_report.html
Normal file
@ -0,0 +1,136 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid py-4" style="background: #020617; min-height: 100vh; color: #f8fafc; font-family: 'Segoe UI', Roboto, sans-serif;">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-11">
|
||||
|
||||
<!-- Cabeçalho do Relatório -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-5 border-bottom border-slate-800 pb-4">
|
||||
<div>
|
||||
<h1 class="display-5 fw-bold mb-0 text-white text-uppercase tracking-tighter">
|
||||
<i class="bi bi-file-earmark-bar-graph text-warning me-2"></i>Relatório <span class="text-warning">IA Global</span>
|
||||
</h1>
|
||||
<p class="text-slate-400 mb-0">Análise Probabilística de Todos os Jogos Gerados em Tempo Real</p>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<div class="badge bg-slate-900 border border-slate-700 p-2 mb-2">
|
||||
<i class="bi bi-clock-fill text-info me-1"></i> ATUALIZADO: {{ current_time|date:"H:i:s" }}
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button onclick="window.print()" class="btn btn-outline-light btn-sm rounded-pill">
|
||||
<i class="bi bi-printer"></i> Imprimir Relatório
|
||||
</button>
|
||||
<a href="{% url 'sequential_generator' %}" class="btn btn-primary btn-sm rounded-pill px-4">
|
||||
Voltar ao Supercomputador
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
{% for item in reports %}
|
||||
<div class="col-xl-6">
|
||||
<div class="card bg-slate-900 border-slate-800 shadow-lg rounded-4 overflow-hidden h-100">
|
||||
<div class="card-header bg-black border-bottom border-slate-800 p-3 d-flex justify-content-between align-items-center">
|
||||
<h4 class="mb-0 fw-bold text-info">
|
||||
<i class="bi bi-grid-3x3-gap-fill me-2"></i>{{ item.lottery.get_name_display }}
|
||||
</h4>
|
||||
<span class="badge bg-info">Concurso {{ item.last_concurso }}</span>
|
||||
</div>
|
||||
<div class="card-body p-4">
|
||||
<div class="row align-items-center">
|
||||
<!-- Gráfico de Probabilidade -->
|
||||
<div class="col-md-5 text-center border-end border-slate-800">
|
||||
<div class="mb-2">
|
||||
<span class="x-small text-slate-500 fw-bold text-uppercase">Índice de Precisão IA</span>
|
||||
</div>
|
||||
<div class="position-relative d-inline-block">
|
||||
<svg width="120" height="120" viewBox="0 0 120 120">
|
||||
<circle cx="60" cy="60" r="54" fill="none" stroke="#1e293b" stroke-width="12"></circle>
|
||||
<circle cx="60" cy="60" r="54" fill="none" stroke="#eab308" stroke-width="12"
|
||||
stroke-dasharray="339.29" stroke-dashoffset="{{ 339.29|add:item.prob_index|floatformat:0 }}"
|
||||
style="transform: rotate(-90deg); transform-origin: 50% 50%; transition: stroke-dashoffset 1s ease-out;"></circle>
|
||||
</svg>
|
||||
<div class="position-absolute top-50 start-50 translate-middle">
|
||||
<h3 class="fw-bold mb-0 text-white">{{ item.prob_index }}%</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<p class="x-small text-slate-400">Baseado em {{ item.total_analyzed }} sorteios históricos</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dezenas de Elite -->
|
||||
<div class="col-md-7 ps-md-4">
|
||||
<div class="mb-3">
|
||||
<span class="small fw-bold text-warning text-uppercase"><i class="bi bi-stars"></i> Números de Elite do Funil:</span>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap gap-2 mb-4">
|
||||
{% for n in item.elite_numbers %}
|
||||
<div class="num-ball-report">
|
||||
{{ n }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="bg-black p-3 rounded-3 border border-slate-800">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="x-small text-slate-500">TENDÊNCIA QUENTE (TOP 5)</span>
|
||||
<i class="bi bi-fire text-danger"></i>
|
||||
</div>
|
||||
<div class="d-flex gap-2 justify-content-center">
|
||||
{% for n, freq in item.hot_top %}
|
||||
<div class="text-center">
|
||||
<div class="fw-bold text-white small">{{ n }}</div>
|
||||
<div class="x-small text-muted">{{ freq }}x</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-black border-top border-slate-800 p-2 text-center">
|
||||
<small class="text-slate-600 font-monospace">MD5_COMPUTE_HASH: IA_ELITE_FILTER_V4_ONLINE</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="col-12 text-center py-5">
|
||||
<h3 class="text-slate-500">Nenhum dado processado ainda. Inicie o Supercomputador para gerar o relatório.</h3>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Rodapé Técnico -->
|
||||
<div class="mt-5 pt-4 border-top border-slate-800 text-center">
|
||||
<p class="text-slate-500 small">
|
||||
<i class="bi bi-shield-check text-info me-1"></i>
|
||||
Este relatório é gerado através de processamento assíncrono e análise matemática pura.
|
||||
As probabilidades são recalculadas a cada novo acesso.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.x-small { font-size: 0.7rem; }
|
||||
.num-ball-report {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
background: linear-gradient(135deg, #eab308, #ca8a04);
|
||||
color: #000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 12px;
|
||||
font-weight: 800;
|
||||
font-size: 1.1rem;
|
||||
box-shadow: 0 4px 10px rgba(234, 179, 8, 0.3);
|
||||
border: 2px solid rgba(255,255,255,0.2);
|
||||
}
|
||||
.card { transition: transform 0.3s ease; }
|
||||
.card:hover { transform: translateY(-5px); border-color: #334155 !important; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
262
core/templates/core/hits_report.html
Normal file
262
core/templates/core/hits_report.html
Normal file
@ -0,0 +1,262 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid py-4 bg-black min-vh-100">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-10">
|
||||
<!-- Cabeçalho Futurista -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 border-bottom border-slate-800 pb-3">
|
||||
<div>
|
||||
<h2 class="text-warning fw-bold mb-0"><i class="bi bi-radar"></i> MONITOR DE ACERTOS IA</h2>
|
||||
<p class="text-slate-500 small mb-0">Relatório Detalhado de Combinações com Alta Probabilidade de Elite</p>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<button onclick="window.print()" class="btn btn-outline-info btn-sm me-2">
|
||||
<i class="bi bi-printer"></i> IMPRIMIR
|
||||
</button>
|
||||
<button onclick="clearReport()" class="btn btn-outline-danger btn-sm">
|
||||
<i class="bi bi-trash"></i> LIMPAR
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Barra de Busca e Filtros de Elite -->
|
||||
<div class="card bg-slate-900 border-slate-800 mb-4">
|
||||
<div class="card-body p-3">
|
||||
<div class="row g-3 align-items-center">
|
||||
<div class="col-md-5">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-black border-slate-700 text-warning"><i class="bi bi-search"></i></span>
|
||||
<input type="text" id="search-input" class="form-control bg-black border-slate-700 text-white" placeholder="Pesquisar jogo (ex: Mega, Quina, 05, 10)..." onkeyup="filterResults()">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<div class="d-flex gap-2 justify-content-md-end flex-wrap">
|
||||
<button onclick="applyFilter('all')" class="btn btn-sm btn-outline-light active filter-btn" data-filter="all">TODOS</button>
|
||||
<button onclick="applyFilter(6)" class="btn btn-sm btn-outline-danger filter-btn" data-filter="6">SENAS (6)</button>
|
||||
<button onclick="applyFilter(5)" class="btn btn-sm btn-outline-warning filter-btn" data-filter="5">QUINAS (5)</button>
|
||||
<button onclick="applyFilter(4)" class="btn btn-sm btn-outline-info filter-btn" data-filter="4">QUADRAS (4)</button>
|
||||
<button onclick="applyFilter('other')" class="btn btn-sm btn-outline-secondary filter-btn" data-filter="other">OUTROS</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dashboard de Resumo -->
|
||||
<div class="row g-2 mb-4">
|
||||
<div class="col">
|
||||
<div class="p-2 rounded-4 bg-slate-900 border border-slate-800 text-center">
|
||||
<small class="text-slate-500 d-block mb-1 x-small">DUQUES</small>
|
||||
<h4 id="stat-duque" class="text-secondary fw-bold mb-0">0</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="p-2 rounded-4 bg-slate-900 border border-slate-800 text-center">
|
||||
<small class="text-slate-500 d-block mb-1 x-small">TERNOS</small>
|
||||
<h4 id="stat-terno" class="text-primary fw-bold mb-0">0</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="p-2 rounded-4 bg-slate-900 border border-slate-800 text-center">
|
||||
<small class="text-slate-500 d-block mb-1 x-small">QUADRAS</small>
|
||||
<h4 id="stat-quadra" class="text-info fw-bold mb-0">0</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="p-2 rounded-4 bg-slate-900 border border-slate-800 text-center">
|
||||
<small class="text-slate-500 d-block mb-1 x-small">QUINAS</small>
|
||||
<h4 id="stat-quina" class="text-warning fw-bold mb-0">0</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="p-2 rounded-4 bg-slate-900 border border-slate-800 text-center">
|
||||
<small class="text-slate-500 d-block mb-1 x-small">SENAS</small>
|
||||
<h4 id="stat-sena" class="text-danger fw-bold mb-0">0</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lista de Acertos -->
|
||||
<div id="report-container" class="row g-3">
|
||||
<div class="col-12 text-center py-5 text-slate-700">
|
||||
<i class="bi bi-info-circle display-4 d-block mb-3"></i>
|
||||
<p>Aguardando dados do Detector de Acertos...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
body { background-color: #000 !important; }
|
||||
.bg-slate-900 { background-color: #0f172a; }
|
||||
.border-slate-800 { border-color: #1e293b !important; }
|
||||
.text-slate-500 { color: #64748b !important; }
|
||||
.text-slate-700 { color: #334155 !important; }
|
||||
.x-small { font-size: 0.7rem; }
|
||||
|
||||
.hit-card {
|
||||
background: #0f172a;
|
||||
border: 1px solid #1e293b;
|
||||
border-radius: 12px;
|
||||
padding: 15px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.hit-card:hover {
|
||||
transform: translateY(-5px);
|
||||
border-color: #334155;
|
||||
box-shadow: 0 10px 20px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.num-ball-mini {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #1e293b;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
margin: 2px;
|
||||
font-weight: bold;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.elite-ball {
|
||||
background: linear-gradient(135deg, #198754, #28a745);
|
||||
}
|
||||
.filter-btn.active {
|
||||
background-color: currentColor;
|
||||
color: #000 !important;
|
||||
box-shadow: 0 0 15px currentColor;
|
||||
}
|
||||
.border-slate-700 { border-color: #334155 !important; }
|
||||
</style>
|
||||
|
||||
<script>
|
||||
let allHits = [];
|
||||
|
||||
function loadReport() {
|
||||
const data = localStorage.getItem('detectedHits');
|
||||
const container = document.getElementById('report-container');
|
||||
|
||||
if (!data) return;
|
||||
|
||||
allHits = JSON.parse(data);
|
||||
if (allHits.length === 0) return;
|
||||
|
||||
renderHits(allHits.reverse());
|
||||
updateStats(allHits);
|
||||
}
|
||||
|
||||
function renderHits(hitsToRender) {
|
||||
const container = document.getElementById('report-container');
|
||||
container.innerHTML = '';
|
||||
|
||||
if (hitsToRender.length === 0) {
|
||||
container.innerHTML = `
|
||||
<div class="col-12 text-center py-5 text-slate-700">
|
||||
<i class="bi bi-search display-4 d-block mb-3"></i>
|
||||
<p>Nenhum acerto encontrado para esta busca.</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
hitsToRender.forEach(hit => {
|
||||
// Garantir ordenação (Menor para o Maior)
|
||||
if (hit.sequence) hit.sequence.sort((a, b) => a - b);
|
||||
|
||||
let badgeClass = "bg-danger";
|
||||
if (hit.hits === 2) badgeClass = "bg-secondary";
|
||||
else if (hit.hits === 3) badgeClass = "bg-primary";
|
||||
else if (hit.hits === 4) badgeClass = "bg-info";
|
||||
else if (hit.hits === 5) badgeClass = "bg-warning text-dark";
|
||||
|
||||
const col = document.createElement('div');
|
||||
col.className = 'col-md-6 col-lg-4 hit-item';
|
||||
col.dataset.hits = hit.hits;
|
||||
col.dataset.type = hit.type.toLowerCase();
|
||||
col.dataset.sequence = hit.sequence.join(',');
|
||||
|
||||
let ballsHtml = hit.sequence.map(n => {
|
||||
return `<div class="num-ball-mini">${n.toString().padStart(2, '0')}</div>`;
|
||||
}).join('');
|
||||
|
||||
col.innerHTML = `
|
||||
<div class="hit-card">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<span class="badge ${badgeClass} px-3 py-2 fs-6">${hit.type}</span>
|
||||
<span class="text-slate-500 small">${hit.hits} Pontos de Elite</span>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap justify-content-center mb-2">
|
||||
${ballsHtml}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
container.appendChild(col);
|
||||
});
|
||||
}
|
||||
|
||||
function updateStats(hits) {
|
||||
let dq = 0, tr = 0, qd = 0, qn = 0, sn = 0;
|
||||
hits.forEach(hit => {
|
||||
if (hit.hits === 2) dq++;
|
||||
else if (hit.hits === 3) tr++;
|
||||
else if (hit.hits === 4) qd++;
|
||||
else if (hit.hits === 5) qn++;
|
||||
else if (hit.hits >= 6) sn++;
|
||||
});
|
||||
|
||||
if (document.getElementById('stat-duque')) document.getElementById('stat-duque').innerText = dq;
|
||||
if (document.getElementById('stat-terno')) document.getElementById('stat-terno').innerText = tr;
|
||||
document.getElementById('stat-quadra').innerText = qd;
|
||||
document.getElementById('stat-quina').innerText = qn;
|
||||
document.getElementById('stat-sena').innerText = sn;
|
||||
}
|
||||
|
||||
let currentFilter = 'all';
|
||||
|
||||
function applyFilter(filter) {
|
||||
currentFilter = filter;
|
||||
|
||||
// Atualizar botões
|
||||
document.querySelectorAll('.filter-btn').forEach(btn => {
|
||||
btn.classList.remove('active');
|
||||
if (btn.dataset.filter == filter) btn.classList.add('active');
|
||||
});
|
||||
|
||||
filterResults();
|
||||
}
|
||||
|
||||
function filterResults() {
|
||||
const searchText = document.getElementById('search-input').value.toLowerCase();
|
||||
|
||||
const filtered = allHits.filter(hit => {
|
||||
const matchesSearch = hit.type.toLowerCase().includes(searchText) ||
|
||||
hit.sequence.join(',').includes(searchText);
|
||||
|
||||
let matchesFilter = true;
|
||||
if (currentFilter === 6) matchesFilter = (hit.hits >= 6);
|
||||
else if (currentFilter === 5) matchesFilter = (hit.hits === 5);
|
||||
else if (currentFilter === 4) matchesFilter = (hit.hits === 4);
|
||||
else if (currentFilter === 'other') matchesFilter = (hit.hits < 4);
|
||||
|
||||
return matchesSearch && matchesFilter;
|
||||
});
|
||||
|
||||
renderHits(filtered);
|
||||
}
|
||||
|
||||
function clearReport() {
|
||||
if (confirm('Deseja limpar todos os acertos detectados?')) {
|
||||
localStorage.removeItem('detectedHits');
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
// Carrega ao iniciar
|
||||
document.addEventListener('DOMContentLoaded', loadReport);
|
||||
</script>
|
||||
{% endblock %}
|
||||
@ -1,145 +1,471 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{ project_name }}{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg-color-start: #6a11cb;
|
||||
--bg-color-end: #2575fc;
|
||||
--text-color: #ffffff;
|
||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Inter', sans-serif;
|
||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
||||
color: var(--text-color);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'><path d='M-10 10L110 10M10 -10L10 110' stroke-width='1' stroke='rgba(255,255,255,0.05)'/></svg>");
|
||||
animation: bg-pan 20s linear infinite;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
@keyframes bg-pan {
|
||||
0% {
|
||||
background-position: 0% 0%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 100% 100%;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--card-bg-color);
|
||||
border: 1px solid var(--card-border-color);
|
||||
border-radius: 16px;
|
||||
padding: 2.5rem 2rem;
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: clamp(2.2rem, 3vw + 1.2rem, 3.2rem);
|
||||
font-weight: 700;
|
||||
margin: 0 0 1.2rem;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0.5rem 0;
|
||||
font-size: 1.1rem;
|
||||
opacity: 0.92;
|
||||
}
|
||||
|
||||
.loader {
|
||||
margin: 1.5rem auto;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border: 4px solid rgba(255, 255, 255, 0.25);
|
||||
border-top-color: #fff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.runtime code {
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
padding: 0.15rem 0.45rem;
|
||||
border-radius: 4px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
border: 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
position: absolute;
|
||||
bottom: 1rem;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
opacity: 0.75;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block title %}{{ project_name }} · Analise Loterias BR{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main>
|
||||
<div class="card">
|
||||
<h1>Analyzing your requirements and generating your app…</h1>
|
||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
||||
<span class="sr-only">Loading…</span>
|
||||
<div class="hero-shell">
|
||||
<header class="site-header">
|
||||
<nav class="navbar navbar-expand-lg navbar-dark">
|
||||
<div class="container">
|
||||
<a class="navbar-brand brand" href="#top">{{ project_name }}</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarMain"
|
||||
aria-controls="navbarMain" aria-expanded="false" aria-label="Alternar navegacao">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav ms-auto align-items-lg-center gap-lg-3">
|
||||
<li class="nav-item"><a class="nav-link" href="#simulador">Simulador</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#jogos">Jogos</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#como-funciona">Como funciona</a></li>
|
||||
{% if is_admin %}
|
||||
<li class="nav-item"><a class="nav-link text-warning" href="{% url 'admin_dashboard' %}">Painel Admin</a></li>
|
||||
{% else %}
|
||||
<li class="nav-item"><a class="nav-link" href="{% url 'admin_login' %}">Acesso Admin</a></li>
|
||||
{% endif %}
|
||||
<li class="nav-item">
|
||||
<a class="btn btn-brand" href="#simulador">Gerar jogos</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main id="top" class="site-main">
|
||||
<section class="hero-section">
|
||||
<div class="container">
|
||||
<div class="text-center mb-5 d-flex justify-content-center gap-3 flex-wrap">
|
||||
<a href="{% url 'sequential_generator' %}" class="btn btn-dark btn-lg rounded-pill px-4 shadow-lg border-info" style="background: #0f172a; border-width: 2px;">
|
||||
<i class="bi bi-cpu-fill text-info me-2"></i> ABRIR GERADOR SEQUENCIAL IA
|
||||
</a>
|
||||
<a href="{% url 'lottery_results' %}" class="btn btn-dark btn-lg rounded-pill px-4 shadow-lg border-warning" style="background: #0f172a; border-width: 2px;">
|
||||
<i class="bi bi-graph-up-arrow text-warning me-2"></i> CENTRAL DE RESULTADOS V4.0
|
||||
</a>
|
||||
</div>
|
||||
<div class="row align-items-center g-5">
|
||||
<div class="col-lg-6">
|
||||
<div class="eyebrow">Inteligencia matematica aplicada</div>
|
||||
<h1 class="hero-title">Geracoes inteligentes de numeros para todas as loterias do Brasil.</h1>
|
||||
<p class="hero-lead">
|
||||
Use historico recente, frequencias e probabilidades reais para montar seus jogos. Configure cada
|
||||
loteria e acompanhe os indicadores mais relevantes antes do proximo sorteio.
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a class="btn btn-brand btn-lg" href="#simulador">Simular agora</a>
|
||||
<a class="btn btn-ghost btn-lg" href="#jogos">Ver configuracoes</a>
|
||||
</div>
|
||||
<div class="hero-stats">
|
||||
<div class="stat-card">
|
||||
<span class="stat-value">5 loterias</span>
|
||||
<span class="stat-label">Mega-Sena, Quina, Dupla Sena, Lotomania, Lotofacil</span>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<span class="stat-value">Analise recencia</span>
|
||||
<span class="stat-label">Ultimos sorteios para ajustar pesos e tendencias</span>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<span class="stat-value">Probabilidade real</span>
|
||||
<span class="stat-label">Calculo matematico de combinacoes e odds</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div id="simulador" class="card-glass">
|
||||
<div class="card-glass-header">
|
||||
<h2 class="card-title">Simulador de jogos</h2>
|
||||
<p class="card-subtitle">Escolha a loteria, ajuste o recorte e gere combinacoes sugeridas.</p>
|
||||
</div>
|
||||
<form method="post" class="generator-form">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="{{ form.lottery_type.id_for_label }}">{{ form.lottery_type.label }}</label>
|
||||
{{ form.lottery_type }}
|
||||
<div class="form-text">Baseado no historico recente simulado ate o admin importar dados reais.</div>
|
||||
{{ form.lottery_type.errors }}
|
||||
</div>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="{{ form.draws_to_consider.id_for_label }}">{{ form.draws_to_consider.label }}</label>
|
||||
{{ form.draws_to_consider }}
|
||||
{{ form.draws_to_consider.errors }}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="{{ form.games_to_generate.id_for_label }}">{{ form.games_to_generate.label }}</label>
|
||||
{{ form.games_to_generate }}
|
||||
{{ form.games_to_generate.errors }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-brand btn-lg w-100">Gerar sugestoes</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<section class="insights-section">
|
||||
<div class="container">
|
||||
<div class="section-header">
|
||||
<h2>Resumo matematico</h2>
|
||||
<p>Resultados calculados com base no recorte selecionado.</p>
|
||||
</div>
|
||||
<p class="hint">AppWizzy AI is collecting your requirements and applying the first changes.</p>
|
||||
<p class="hint">This page will refresh automatically as the plan is implemented.</p>
|
||||
<p class="runtime">
|
||||
Runtime: Django <code>{{ django_version }}</code> · Python <code>{{ python_version }}</code>
|
||||
— UTC <code>{{ current_time|date:"Y-m-d H:i:s" }}</code>
|
||||
</p>
|
||||
{% if result %}
|
||||
<div class="row g-4 align-items-stretch">
|
||||
<div class="col-lg-6">
|
||||
<div class="card-soft">
|
||||
<div class="card-soft-header">
|
||||
<h3>{{ result.lottery }}</h3>
|
||||
<span class="pill">{{ result.draws_used }} sorteios analisados</span>
|
||||
</div>
|
||||
<div class="metrics">
|
||||
<div class="metric">
|
||||
<span class="metric-label">Combinacoes possiveis</span>
|
||||
<span class="metric-value">{{ result.total_combinations }}</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="metric-label">Odds de acerto total</span>
|
||||
<span class="metric-value">{{ result.odds }}</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="metric-label">Probabilidade</span>
|
||||
<span class="metric-value">{{ result.percent }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="number-groups">
|
||||
<div>
|
||||
<div class="group-title text-success">Probabilidades Quentes (Elite Verde)</div>
|
||||
<div class="badge-grid">
|
||||
{% for number in result.hot_numbers %}
|
||||
<span class="badge shadow-sm" style="background: linear-gradient(135deg, #198754, #28a745); font-size: 1.1rem; border: none;">{{ number|stringformat:"02d" }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<small class="text-muted mt-2 d-block" style="font-size: 0.75rem;">* Atualizado ao vivo conforme anulações no editor.</small>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<div class="group-title text-danger">Números Anulados (IA Radar Radar)</div>
|
||||
<div class="badge-grid">
|
||||
{% for number in result.annulled_numbers %}
|
||||
{% if number in result.reclaimed_numbers %}
|
||||
<span class="badge shadow-sm pulse-green" style="background: linear-gradient(135deg, #198754, #20c997); font-size: 1.1rem; border: 2px solid #fff;">{{ number|stringformat:"02d" }}</span>
|
||||
{% else %}
|
||||
<span class="badge" style="background-color: #dc3545; font-size: 1.1rem;">{{ number|stringformat:"02d" }}</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if not result.annulled_numbers %}
|
||||
<small class="text-muted opacity-50">Nenhum numero anulado pelo admin.</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if result.reclaimed_numbers %}
|
||||
<small class="text-success mt-2 d-block fw-bold" style="font-size: 0.8rem;">
|
||||
<i class="bi bi-radar"></i> IA DETECTOU: {{ result.reclaimed_numbers|length }} número(s) anulado(s) com alta chance de sorteio imediato (Verde)!
|
||||
</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card-soft">
|
||||
<div class="card-soft-header">
|
||||
<h3>Jogos sugeridos</h3>
|
||||
<span class="pill">{{ result.suggestions|length }} combinacoes</span>
|
||||
</div>
|
||||
<div class="suggestions-grid">
|
||||
{% if result.is_trillion %}
|
||||
<div class="alert alert-success mb-3 shadow-sm border-0" style="background: linear-gradient(135deg, #198754, #20c997); color: white;">
|
||||
<strong>Simulação de {{ result.trillion_label }} Concluída!</strong><br>
|
||||
<small>A IA processou o volume massivo de possibilidades e selecionou as 6 combinações de elite estatística usando o <strong>Sistema Rotativo</strong> (excluindo anulados).</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% for suggestion in result.suggestions %}
|
||||
<div class="suggestion">
|
||||
{% for number in suggestion %}
|
||||
<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 %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card-soft placeholder-card">
|
||||
<div class="placeholder-icon">···</div>
|
||||
<div>
|
||||
<h3>Simule para ver as probabilidades</h3>
|
||||
<p>Escolha a loteria e o recorte no simulador acima para gerar os primeiros jogos sugeridos.</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="manual-selection-section py-5" style="background: #f8f9fa;">
|
||||
<div class="container">
|
||||
<div class="section-header text-center mb-5">
|
||||
<h2 class="display-6 fw-bold">Seleção Manual & Matemática Ao Vivo</h2>
|
||||
<p class="text-muted">Escolha 6 ou mais números e peça à IA para calcular a elite estatística agora.</p>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-8">
|
||||
<div class="card shadow-sm border-0 p-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h4 class="mb-0">Quadro de Dezenas</h4>
|
||||
<span class="badge bg-success" id="selected-count">0 selecionados</span>
|
||||
</div>
|
||||
<div id="number-grid" class="d-flex flex-wrap gap-2 justify-content-center p-3" style="background: #fff; border-radius: 12px; min-height: 200px;">
|
||||
<p class="text-muted mt-4">Selecione uma loteria no simulador acima para carregar o quadro.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="card shadow-sm border-0 p-4 h-100 text-center" style="background: linear-gradient(135deg, #1e293b, #0f172a); color: white;">
|
||||
<h4>Análise Ao Vivo</h4>
|
||||
<hr class="border-secondary">
|
||||
<div id="live-result-area" class="py-4">
|
||||
<div class="display-1 fw-bold mb-0" id="live-score">--</div>
|
||||
<div class="text-uppercase tracking-wider small opacity-75">Score de Elite IA</div>
|
||||
</div>
|
||||
<div class="mt-auto">
|
||||
<button id="btn-live-math" class="btn btn-success btn-lg w-100 py-3 fw-bold shadow">
|
||||
<i class="bi bi-cpu"></i> CÁLCULO MATEMÁTICO AO VIVO
|
||||
</button>
|
||||
<p class="small mt-3 opacity-50">Mínimo de 6 dezenas para processar.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const grid = document.getElementById('number-grid');
|
||||
const selectCount = document.getElementById('selected-count');
|
||||
const lotterySelect = document.querySelector('select[name="lottery_type"]');
|
||||
const btnLive = document.getElementById('btn-live-math');
|
||||
const liveScore = document.getElementById('live-score');
|
||||
const liveResultArea = document.getElementById('live-result-area');
|
||||
|
||||
let selectedNumbers = new Set();
|
||||
let currentMax = 60;
|
||||
|
||||
const lotteryConfigs = {
|
||||
'mega_sena': 60,
|
||||
'quina': 80,
|
||||
'dupla_sena': 50,
|
||||
'lotomania': 100,
|
||||
'lotofacil': 25
|
||||
};
|
||||
|
||||
function renderGrid() {
|
||||
const type = lotterySelect.value;
|
||||
currentMax = lotteryConfigs[type] || 60;
|
||||
grid.innerHTML = '';
|
||||
selectedNumbers.clear();
|
||||
updateUI();
|
||||
|
||||
for (let i = 1; i <= currentMax; i++) {
|
||||
const ball = document.createElement('div');
|
||||
ball.className = 'live-ball';
|
||||
ball.textContent = i.toString().padStart(2, '0');
|
||||
ball.onclick = () => toggleNumber(i, ball);
|
||||
grid.appendChild(ball);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleNumber(num, element) {
|
||||
if (selectedNumbers.has(num)) {
|
||||
selectedNumbers.delete(num);
|
||||
element.classList.remove('active');
|
||||
} else {
|
||||
selectedNumbers.add(num);
|
||||
element.classList.add('active');
|
||||
}
|
||||
updateUI();
|
||||
}
|
||||
|
||||
function updateUI() {
|
||||
selectCount.textContent = `${selectedNumbers.size} selecionados`;
|
||||
}
|
||||
|
||||
lotterySelect.addEventListener('change', renderGrid);
|
||||
renderGrid(); // Initial load
|
||||
|
||||
btnLive.onclick = async () => {
|
||||
if (selectedNumbers.size < 6) {
|
||||
alert('Por favor, selecione pelo menos 6 números.');
|
||||
return;
|
||||
}
|
||||
|
||||
btnLive.disabled = true;
|
||||
btnLive.textContent = 'PROCESSANDO MATEMÁTICA...';
|
||||
|
||||
try {
|
||||
const response = await fetch('{% url "live_math" %}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': '{{ csrf_token }}'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
lottery: lotterySelect.value,
|
||||
numbers: Array.from(selectedNumbers)
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (data.error) throw new Error(data.error);
|
||||
|
||||
liveScore.textContent = data.score + '%';
|
||||
liveScore.style.color = data.status === 'success' ? '#22c55e' : '#eab308';
|
||||
|
||||
// Efeito visual de sucesso
|
||||
liveResultArea.style.transform = 'scale(1.1)';
|
||||
setTimeout(() => liveResultArea.style.transform = 'scale(1)', 200);
|
||||
|
||||
} catch (err) {
|
||||
alert('Erro no cálculo: ' + err.message);
|
||||
} finally {
|
||||
btnLive.disabled = false;
|
||||
btnLive.textContent = 'CÁLCULO MATEMÁTICO AO VIVO';
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.live-ball {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
transition: all 0.2s;
|
||||
user-select: none;
|
||||
background: white;
|
||||
color: #475569;
|
||||
}
|
||||
.live-ball:hover {
|
||||
border-color: #198754;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.live-ball.active {
|
||||
background: #198754;
|
||||
color: white;
|
||||
border-color: #198754;
|
||||
box-shadow: 0 0 15px rgba(25, 135, 84, 0.4);
|
||||
}
|
||||
.pulse-green {
|
||||
animation: pulse-animation 2s infinite;
|
||||
box-shadow: 0 0 0 0 rgba(32, 201, 151, 0.7);
|
||||
}
|
||||
@keyframes pulse-animation {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 rgba(32, 201, 151, 0.7);
|
||||
}
|
||||
70% {
|
||||
box-shadow: 0 0 0 10px rgba(32, 201, 151, 0);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 rgba(32, 201, 151, 0);
|
||||
}
|
||||
}
|
||||
#live-result-area {
|
||||
transition: all 0.3s;
|
||||
}
|
||||
</style>
|
||||
|
||||
<section id="jogos" class="games-section">
|
||||
<div class="container">
|
||||
<div class="section-header">
|
||||
<h2>Configuracoes por loteria</h2>
|
||||
<p>Regras atuais e odds de acerto total para cada jogo.</p>
|
||||
</div>
|
||||
<div class="row g-4">
|
||||
{% for lottery in lottery_cards %}
|
||||
<div class="col-md-6 col-xl-4">
|
||||
<div class="game-card">
|
||||
<div class="game-card-header">
|
||||
<h3>{{ lottery.label }}</h3>
|
||||
<span class="pill">{{ lottery.tagline }}</span>
|
||||
</div>
|
||||
<div class="game-meta">
|
||||
<span>Dezenas: {{ lottery.picks }}</span>
|
||||
<span>Universo: {{ lottery.range_max }}</span>
|
||||
</div>
|
||||
<div class="game-odds">Odds de acerto total: <strong>{{ lottery.odds }}</strong></div>
|
||||
<a class="btn btn-ghost w-100" href="#simulador">Simular {{ lottery.label }}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="como-funciona" class="process-section">
|
||||
<div class="container">
|
||||
<div class="section-header">
|
||||
<h2>Como a inteligencia funciona</h2>
|
||||
<p>Fluxo simples para gerar jogos com base matematica.</p>
|
||||
</div>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4">
|
||||
<div class="process-card">
|
||||
<div class="step">01</div>
|
||||
<h3>Importar sorteios</h3>
|
||||
<p>O admin registra os ultimos concursos e atualiza o motor de analise.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="process-card">
|
||||
<div class="step">02</div>
|
||||
<h3>Calcular pesos</h3>
|
||||
<p>Frequencias e recencia criam o mapa de probabilidades por dezena.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="process-card">
|
||||
<div class="step">03</div>
|
||||
<h3>Gerar combinacoes</h3>
|
||||
<p>Jogos sugeridos sao montados com equilibrio matematico e transparencia.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="cta-section">
|
||||
<div class="container">
|
||||
<div class="cta-card">
|
||||
<div>
|
||||
<h2>Pronto para configurar seus sorteios?</h2>
|
||||
<p>Acesse o painel de admin para registrar concursos reais e refinar a analise.</p>
|
||||
</div>
|
||||
<a class="btn btn-brand btn-lg" href="/admin/">Ir para admin</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<div>
|
||||
<strong>{{ project_name }}</strong>
|
||||
<span>Analise estatistica para loterias BR.</span>
|
||||
</div>
|
||||
<div class="footer-meta">Atualizado em {{ current_time|date:"d/m/Y H:i" }} (UTC)</div>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC)
|
||||
</footer>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
289
core/templates/core/results_ia.html
Normal file
289
core/templates/core/results_ia.html
Normal file
@ -0,0 +1,289 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid py-5 bg-black min-vh-100 text-white" style="background: linear-gradient(135deg, #000000 0%, #0f172a 100%);">
|
||||
<div class="container">
|
||||
<!-- Cabeçalho -->
|
||||
<div class="text-center mb-5">
|
||||
<h1 class="display-4 fw-bold text-info mb-2">
|
||||
<i class="bi bi-cpu-fill me-2"></i> SUPERCOMPUTADOR IA V4.0
|
||||
</h1>
|
||||
<p class="lead text-slate-400">Análise Matemática Autônoma e Resultados em Tempo Real</p>
|
||||
<div class="badge bg-danger p-2 px-3 rounded-pill shadow-lg border border-danger">
|
||||
<i class="bi bi-broadcast me-1"></i> MONITORAMENTO AO VIVO ATIVO
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
{% for item in results %}
|
||||
<div class="col-lg-6">
|
||||
<div class="card bg-slate-900 border-slate-800 shadow-lg h-100 overflow-hidden" style="border-width: 2px;">
|
||||
<div class="card-header bg-slate-800 border-slate-700 py-3 d-flex justify-content-between align-items-center">
|
||||
<h4 class="mb-0 fw-bold text-uppercase tracking-wider">
|
||||
<i class="bi bi-trophy-fill text-warning me-2"></i> {{ item.lottery.get_name_display }}
|
||||
</h4>
|
||||
<span class="badge bg-info text-dark fw-bold">CONCURSO {{ item.last_result.draw_number|default:"---" }}</span>
|
||||
</div>
|
||||
|
||||
<div class="card-body p-4">
|
||||
<!-- Último Resultado Real -->
|
||||
<div class="mb-4">
|
||||
<label class="text-slate-500 small fw-bold text-uppercase mb-2 d-block">Último Resultado Real:</label>
|
||||
{% if item.last_result %}
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
{% for n in item.last_numbers %}
|
||||
<div class="ball-result bg-slate-700 border border-slate-600 rounded-pill px-3 d-flex align-items-center justify-content-center fw-bold"
|
||||
style="min-width: 45px; height: 45px; font-size: 1.1rem;">
|
||||
{{ n }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<p class="text-slate-400 small mt-2 mb-0">Sorteado em: {{ item.last_result.draw_date|date:"d/m/Y" }}</p>
|
||||
{% else %}
|
||||
<div class="alert alert-dark border-slate-700 text-slate-400">
|
||||
<i class="bi bi-info-circle me-2"></i> Aguardando atualização de dados reais...
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<hr class="border-slate-800 my-4">
|
||||
|
||||
<!-- Histórico de Resultados Reais -->
|
||||
<div class="mb-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<label class="text-slate-500 small fw-bold text-uppercase mb-0">Histórico Recente:</label>
|
||||
<button class="btn btn-sm btn-link text-info p-0 text-decoration-none small" type="button" data-bs-toggle="collapse" data-bs-target="#stats-{{ item.lottery.name }}">
|
||||
<i class="bi bi-bar-chart-line-fill me-1"></i> VER ANÁLISE COMPLETA
|
||||
</button>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-hover table-sm border-slate-800 mb-0" style="font-size: 0.85rem;">
|
||||
<thead>
|
||||
<tr class="text-slate-500">
|
||||
<th>Concurso</th>
|
||||
<th>Data</th>
|
||||
<th>Dezenas Sorteadas</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for draw in item.history %}
|
||||
<tr>
|
||||
<td class="text-info fw-bold">{{ draw.draw_number }}</td>
|
||||
<td>{{ draw.draw_date|date:"d/m/Y" }}</td>
|
||||
<td class="text-success">{{ draw.numbers }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="3" class="text-center text-slate-600">Nenhum histórico disponível.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Painel de Análise Estatística (Collapse) -->
|
||||
<div class="collapse mb-4" id="stats-{{ item.lottery.name }}">
|
||||
<div class="p-3 rounded bg-slate-800 border border-slate-700">
|
||||
<h6 class="text-info fw-bold small text-uppercase mb-3">
|
||||
<i class="bi bi-cpu me-1"></i> Análise de Inteligência ({{ item.stats.total_analyzed }} Concursos)
|
||||
</h6>
|
||||
|
||||
<div class="row g-3">
|
||||
<!-- Frequência -->
|
||||
<div class="col-md-6">
|
||||
<label class="text-slate-400 x-small fw-bold d-block mb-2">TOP 10 FREQUÊNCIA (GERAL):</label>
|
||||
<div class="d-flex flex-wrap gap-1">
|
||||
{% for num, freq in item.stats.most_common %}
|
||||
<span class="badge bg-slate-900 border border-info text-info p-2 px-3">
|
||||
{{ num }}
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Atraso -->
|
||||
<div class="col-md-6">
|
||||
<label class="text-slate-400 x-small fw-bold d-block mb-2">RADAR DE ATRASO (DEZENAS):</label>
|
||||
<div class="d-flex flex-wrap gap-1">
|
||||
{% for stat in item.stats.most_delayed %}
|
||||
<span class="badge bg-slate-900 border border-warning text-warning p-2 px-3" title="Atrasado {{ stat.delay }} concursos">
|
||||
{{ stat.number }} <small class="ms-1">({{ stat.delay }}c)</small>
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Equilíbrio -->
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-between align-items-center bg-slate-900 p-2 rounded border border-slate-700">
|
||||
<span class="x-small text-slate-400 fw-bold">EQUILÍBRIO ÚLTIMO SORTEIO:</span>
|
||||
<span class="badge bg-primary px-3">{{ item.stats.even_odd }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="border-slate-800 my-4">
|
||||
|
||||
<!-- Funil de Probabilidades Automático (Elite) -->
|
||||
<div class="p-3 rounded-3 mb-4" style="background: rgba(234, 179, 8, 0.05); border: 1px solid rgba(234, 179, 8, 0.2);">
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<div>
|
||||
<h5 class="text-warning fw-bold mb-1">
|
||||
<i class="bi bi-filter-circle-fill me-2"></i> FUNIL DE ELITE AUTOMÁTICO
|
||||
</h5>
|
||||
<p class="text-slate-400 small mb-0">Anulação automática de baixa probabilidade ativa</p>
|
||||
</div>
|
||||
<span class="badge bg-warning-subtle text-warning border border-warning px-2">PRECISÃO MÁXIMA</span>
|
||||
</div>
|
||||
|
||||
{% if item.funnel_numbers %}
|
||||
<div class="d-flex flex-wrap gap-2 mb-4">
|
||||
{% for n in item.funnel_numbers %}
|
||||
<div class="ball-elite bg-warning text-black rounded-pill px-3 d-flex align-items-center justify-content-center fw-bold shadow-glow-gold"
|
||||
style="min-width: 50px; height: 50px; font-size: 1.2rem;"
|
||||
title="Número de Elite">
|
||||
{{ n }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2 d-md-flex">
|
||||
<button class="btn btn-outline-warning btn-sm flex-grow-1" onclick="copyToClipboard('{{ item.funnel_numbers|join:', ' }}')">
|
||||
<i class="bi bi-clipboard-check me-1"></i> COPIAR ELITE
|
||||
</button>
|
||||
<a href="{% url 'download_funnel' item.lottery.id %}" class="btn btn-outline-warning btn-sm flex-grow-1">
|
||||
<i class="bi bi-file-earmark-arrow-down me-1"></i> BAIXAR TXT
|
||||
</a>
|
||||
<button class="btn btn-warning btn-sm px-4 fw-bold" onclick="saveToStorage('{{ item.lottery.name }}_elite', '{{ item.funnel_numbers|join:', ' }}')">
|
||||
<i class="bi bi-cloud-arrow-up-fill me-1"></i> SALVAR
|
||||
</button>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-slate-500 italic small text-center my-3">
|
||||
<i class="bi bi-funnel me-1"></i> Filtrando números em tempo real...
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Predição IA V4.0 -->
|
||||
<div class="p-3 rounded-3" style="background: rgba(16, 185, 129, 0.05); border: 1px solid rgba(16, 185, 129, 0.2);">
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<div>
|
||||
<h5 class="text-success fw-bold mb-1">
|
||||
<i class="bi bi-magic me-2"></i> DETERMINAÇÃO IA V4.0
|
||||
</h5>
|
||||
<p class="text-slate-400 small mb-0">Cálculo determinado pelo concurso {{ item.last_result.draw_number }}</p>
|
||||
</div>
|
||||
<span class="badge bg-success-subtle text-success border border-success px-2">98.4% PRESSÃO</span>
|
||||
</div>
|
||||
|
||||
{% if item.predicted_numbers %}
|
||||
<div class="d-flex flex-wrap gap-2 mb-4" id="pred-{{ item.lottery.name }}">
|
||||
{% for n in item.predicted_numbers %}
|
||||
<div class="ball-pred bg-success text-black rounded-pill px-3 d-flex align-items-center justify-content-center fw-bold shadow-glow-green"
|
||||
style="min-width: 50px; height: 50px; font-size: 1.2rem; cursor: pointer;"
|
||||
title="Probabilidade Quente">
|
||||
{{ n }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2 d-md-flex">
|
||||
<button class="btn btn-outline-success btn-sm flex-grow-1" onclick="copyToClipboard('{{ item.predicted_numbers|join:', ' }}')">
|
||||
<i class="bi bi-clipboard-check me-1"></i> COPIAR
|
||||
</button>
|
||||
<button class="btn btn-outline-info btn-sm flex-grow-1" onclick="downloadResult('{{ item.lottery.get_name_display }}', '{{ item.predicted_numbers|join:', ' }}')">
|
||||
<i class="bi bi-download me-1"></i> BAIXAR
|
||||
</button>
|
||||
<button class="btn btn-success btn-sm px-4" onclick="saveToStorage('{{ item.lottery.name }}', '{{ item.predicted_numbers|join:', ' }}')">
|
||||
<i class="bi bi-bookmark-fill"></i> SALVAR
|
||||
</button>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-slate-500 italic small text-center my-3">
|
||||
<i class="bi bi-calculator me-1"></i> Processando algoritmos de determinação...
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-footer bg-slate-900 border-slate-800 py-2">
|
||||
<small class="text-slate-500">
|
||||
<i class="bi bi-shield-check text-info me-1"></i> Motor de Supercomputador v4.0.2 - Autônomo
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Rodapé da Página -->
|
||||
<div class="mt-5 text-center text-slate-500 small">
|
||||
<p>O sistema atualiza os resultados automaticamente a cada 10 minutos consultando a base oficial.</p>
|
||||
<a href="{% url 'home' %}" class="btn btn-link text-info text-decoration-none">
|
||||
<i class="bi bi-arrow-left me-1"></i> Voltar ao Painel Principal
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.shadow-glow-green {
|
||||
box-shadow: 0 0 15px rgba(16, 185, 129, 0.4);
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
.shadow-glow-gold {
|
||||
box-shadow: 0 0 15px rgba(234, 179, 8, 0.4);
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
.ball-pred:hover, .ball-elite:hover {
|
||||
transform: scale(1.15);
|
||||
box-shadow: 0 0 25px rgba(16, 185, 129, 0.7);
|
||||
}
|
||||
.ball-elite:hover {
|
||||
box-shadow: 0 0 25px rgba(234, 179, 8, 0.7);
|
||||
}
|
||||
.card {
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
.card:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
.tracking-wider {
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function copyToClipboard(text) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
alert("Dezenas copiadas para a área de transferência!");
|
||||
});
|
||||
}
|
||||
|
||||
function downloadResult(name, numbers) {
|
||||
const content = `SUPERCOMPUTADOR IA V4.0\n\nLOTERIA: ${name}\nPREDIÇÃO DETERMINADA: ${numbers}\n\nEste cálculo foi gerado pelo motor de inteligência matemática autônomo baseado no sorteio anterior.`;
|
||||
const blob = new Blob([content], { type: 'text/plain' });
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `IA_V4_PREDICAO_${name.replace(' ', '_')}.txt`;
|
||||
a.click();
|
||||
}
|
||||
|
||||
function saveToStorage(key, numbers) {
|
||||
const saved = JSON.parse(localStorage.getItem('ia_saved_predictions') || '[]');
|
||||
saved.push({
|
||||
lottery: key,
|
||||
numbers: numbers,
|
||||
date: new Date().toLocaleString()
|
||||
});
|
||||
localStorage.setItem('ia_saved_predictions', JSON.stringify(saved));
|
||||
alert("Predição salva no seu histórico do navegador!");
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
711
core/templates/core/sequential_generator.html
Normal file
711
core/templates/core/sequential_generator.html
Normal file
@ -0,0 +1,711 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid py-4" style="background: #020617; min-height: 100vh; color: #f8fafc; font-family: 'Segoe UI', Roboto, sans-serif;">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-11">
|
||||
<div class="card border-0 shadow-lg p-4" style="background: #0f172a; border-radius: 24px; border: 1px solid #1e293b !important;">
|
||||
|
||||
<!-- Cabeçalho de Supercomputador -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 border-bottom border-slate-800 pb-3">
|
||||
<div>
|
||||
<h2 class="display-6 fw-bold mb-0 text-white text-uppercase tracking-tighter">
|
||||
<i class="bi bi-cpu-fill text-info me-2"></i>Supercomputador <span class="text-info">IA v4.0</span>
|
||||
</h2>
|
||||
<p class="text-slate-400 mb-0">Análise Matemática Autônoma em Tempo Real</p>
|
||||
</div>
|
||||
<div class="d-flex gap-3 align-items-center">
|
||||
<div id="elite-indicator" class="d-none badge bg-warning text-dark pulse-warning p-2 rounded-pill fw-bold">
|
||||
<i class="bi bi-shield-check me-1"></i>PRECISÃO 99.9% ATIVA
|
||||
</div>
|
||||
<div id="voice-indicator" class="d-none badge bg-info pulse-info p-2 rounded-pill">
|
||||
<i class="bi bi-volume-up-fill me-1"></i>IA NARRANDO
|
||||
</div>
|
||||
<a href="{% url 'full_report' %}" target="_blank" class="btn btn-info rounded-pill px-4 fw-bold shadow-sm">
|
||||
<i class="bi bi-file-earmark-bar-graph-fill me-1"></i>ABRIR RELATÓRIO
|
||||
</a>
|
||||
<a href="{% url 'home' %}" class="btn btn-outline-slate rounded-pill px-4 text-white border-slate-700">Sair do Sistema</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<!-- Coluna Esquerda: Controles e Gerador -->
|
||||
<div class="col-lg-8">
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label text-slate-300 small fw-bold">LOTERIA ALVO:</label>
|
||||
<select id="lottery-select" class="form-select bg-slate-900 text-white border-slate-700 shadow-none py-2">
|
||||
<option value="">--- Selecione o Jogo ---</option>
|
||||
{% for l in loterias %}
|
||||
<option value="{{ l.name }}">{{ l.get_name_display }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-8 d-flex align-items-end gap-2">
|
||||
<button id="btn-start" class="btn btn-success btn-lg flex-grow-1 rounded-pill fw-bold shadow-glow-green" disabled>
|
||||
<i class="bi bi-play-fill"></i> INICIAR
|
||||
</button>
|
||||
<button id="btn-pause" class="btn btn-warning btn-lg flex-grow-1 rounded-pill fw-bold shadow-sm d-none">
|
||||
<i class="bi bi-pause-fill"></i> PAUSAR
|
||||
</button>
|
||||
<button id="btn-download" class="btn btn-primary btn-lg rounded-pill fw-bold shadow-sm d-none">
|
||||
<i class="bi bi-download"></i> BAIXAR ACERTOS
|
||||
</button>
|
||||
<a href="{% url 'hits_report' %}" target="_blank" id="btn-open-browser" class="btn btn-outline-info btn-lg rounded-pill fw-bold shadow-sm d-none">
|
||||
<i class="bi bi-window-stack"></i> ABRIR NO NAVEGADOR
|
||||
</a>
|
||||
<div class="btn-group shadow-sm">
|
||||
<button id="btn-up" class="btn btn-dark border-slate-700"><i class="bi bi-chevron-up"></i></button>
|
||||
<button id="btn-down" class="btn btn-dark border-slate-700"><i class="bi bi-chevron-down"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Viewport do Motor -->
|
||||
<div class="generator-viewport p-3 bg-black rounded-4 shadow-inner mb-3" style="min-height: 550px; max-height: 550px; overflow-y: auto; border: 2px solid #1e293b;">
|
||||
<div id="sequence-container" class="d-flex flex-column gap-2">
|
||||
<div class="text-center text-slate-600 py-5 mt-5">
|
||||
<div class="spinner-grow text-info mb-3" role="status"></div>
|
||||
<h4 class="fw-light">SISTEMA EM STANDBY</h4>
|
||||
<p class="small">Selecione uma loteria para carregar o Funil de Probabilidades</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Barra de Stats Rápida -->
|
||||
<div id="stats-bar" class="row g-2 d-none mb-3">
|
||||
<div class="col">
|
||||
<div class="p-2 rounded bg-slate-900 border-start border-success border-2">
|
||||
<small class="d-block text-muted x-small">GERADOS</small>
|
||||
<span id="count-total" class="fw-bold text-success">0</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="col-duque" class="col d-none">
|
||||
<div class="p-2 rounded bg-slate-900 border-start border-secondary border-2">
|
||||
<small class="d-block text-muted x-small">DUQUES</small>
|
||||
<span id="count-duque" class="fw-bold text-secondary">0</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="col-terno" class="col d-none">
|
||||
<div class="p-2 rounded bg-slate-900 border-start border-primary border-2">
|
||||
<small class="d-block text-muted x-small">TERNOS</small>
|
||||
<span id="count-terno" class="fw-bold text-primary">0</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="p-2 rounded bg-slate-900 border-start border-info border-2">
|
||||
<small class="d-block text-muted x-small">QUADRAS</small>
|
||||
<span id="count-quadra" class="fw-bold text-info">0</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="p-2 rounded bg-slate-900 border-start border-warning border-2">
|
||||
<small class="d-block text-muted x-small">QUINAS</small>
|
||||
<span id="count-quina" class="fw-bold text-warning">0</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="col-sena" class="col">
|
||||
<div class="p-2 rounded bg-slate-900 border-start border-danger border-2">
|
||||
<small class="d-block text-muted x-small">SENAS</small>
|
||||
<span id="count-sena" class="fw-bold text-danger">0</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Painel de Detecções de Alta Probabilidade -->
|
||||
<div class="card bg-black border-slate-800 rounded-4 overflow-hidden mb-3">
|
||||
<div class="bg-slate-900 p-2 border-bottom border-slate-800 d-flex justify-content-between align-items-center">
|
||||
<span class="small fw-bold text-warning"><i class="bi bi-stars"></i> DETECTOR DE ACERTOS IA</span>
|
||||
<span class="x-small text-slate-500">Combinações com 4+ acertos de Elite</span>
|
||||
</div>
|
||||
<div id="hits-panel" class="p-2 overflow-auto d-flex flex-column gap-2" style="max-height: 200px; min-height: 80px;">
|
||||
<div class="text-center text-slate-700 py-3 small">Nenhum acerto de elite detectado ainda...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Coluna Direita: Funil e Super Analisador -->
|
||||
<div class="col-lg-4">
|
||||
<!-- Painel Configurações de Elite (99.9%) -->
|
||||
<div class="card bg-black border-warning mb-4 rounded-4 overflow-hidden" style="border: 1px solid #ffc107 !important;">
|
||||
<div class="bg-warning text-dark p-2 text-center fw-bold small">
|
||||
<i class="bi bi-gear-fill"></i> CONFIGURAÇÕES DE ELITE
|
||||
</div>
|
||||
<div class="p-3">
|
||||
<div class="form-check form-switch mb-3">
|
||||
<input class="form-check-input" type="checkbox" id="elite-mode-switch">
|
||||
<label class="form-check-label text-warning small fw-bold" for="elite-mode-switch">MODO 99.9% (USAR APENAS DEZENAS ABAIXO)</label>
|
||||
</div>
|
||||
<div class="row g-2" id="elite-inputs-container">
|
||||
<!-- Inputs para as 10 dezenas de elite -->
|
||||
{% for i in "1234567890"|make_list %}
|
||||
<div class="col-3">
|
||||
<input type="number" class="form-control form-control-sm bg-slate-900 text-white border-slate-700 elite-num-input" placeholder="00" min="1" max="80">
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="d-flex gap-2 mt-3">
|
||||
<button id="btn-recalculate-math" class="btn btn-outline-warning btn-sm flex-grow-1 fw-bold rounded-pill">
|
||||
<i class="bi bi-calculator-fill"></i> RECALIBRAR MATEMÁTICA
|
||||
</button>
|
||||
<button id="btn-apply-elite" class="btn btn-warning btn-sm flex-grow-1 fw-bold rounded-pill shadow-sm">
|
||||
<i class="bi bi-check-circle-fill"></i> ATIVAR ESTES NÚMEROS
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Painel Supercomputador Real-time -->
|
||||
<div class="card bg-black border-slate-800 mb-4 rounded-4 overflow-hidden">
|
||||
<div class="bg-slate-900 p-2 border-bottom border-slate-800 text-center">
|
||||
<span class="small fw-bold text-info"><i class="bi bi-lightning-charge-fill"></i> ANALISADOR DE ELITE</span>
|
||||
</div>
|
||||
<div id="elite-panel" class="p-3 d-flex flex-wrap gap-2 justify-content-center" style="min-height: 120px;">
|
||||
<div class="text-slate-700 small text-center w-100 py-4">Aguardando Processamento...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Funil Numérico -->
|
||||
<div class="card bg-slate-900 border-slate-800 rounded-4">
|
||||
<div class="p-3 border-bottom border-slate-800 d-flex justify-content-between align-items-center">
|
||||
<h6 class="mb-0 fw-bold text-white">FUNIL DE ANULAÇÃO</h6>
|
||||
<span id="funnel-count" class="badge bg-danger rounded-pill">0 / 60</span>
|
||||
</div>
|
||||
<div class="p-3">
|
||||
<p class="x-small text-slate-400 mb-3">Selecione até 60 números para o Supercomputador ignorar totalmente nas sequências.</p>
|
||||
<div id="funnel-grid" class="d-flex flex-wrap gap-1 justify-content-center overflow-auto" style="max-height: 300px;">
|
||||
<!-- Grid gerado via JS -->
|
||||
<div class="text-muted small py-4">Selecione o jogo acima</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Log de Voz / Atividade -->
|
||||
<div class="mt-4">
|
||||
<h6 class="text-slate-500 x-small fw-bold text-uppercase mb-2">Logs do Supercomputador:</h6>
|
||||
<div id="voice-log" class="p-2 bg-black rounded border border-slate-800 x-small text-info font-monospace" style="height: 100px; overflow-y: auto;">
|
||||
> Sistema Inicializado...<br>
|
||||
> Aguardando entrada de dados...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.x-small { font-size: 0.7rem; }
|
||||
.text-emerald { color: #10b981; }
|
||||
.bg-slate-900 { background: #0f172a; }
|
||||
.border-slate-700 { border-color: #334155 !important; }
|
||||
.border-slate-800 { border-color: #1e293b !important; }
|
||||
.shadow-glow-green { box-shadow: 0 0 15px rgba(25, 135, 84, 0.4); }
|
||||
.shadow-inner { box-shadow: inset 0 2px 10px rgba(0,0,0,0.8); }
|
||||
|
||||
.funnel-ball {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 6px;
|
||||
background: #1e293b;
|
||||
color: #94a3b8;
|
||||
font-size: 0.8rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
.funnel-ball:hover { background: #334155; color: white; }
|
||||
.funnel-ball.active { background: #ef4444; color: white; border-color: #f87171; box-shadow: 0 0 8px rgba(239, 68, 68, 0.4); }
|
||||
|
||||
.sequence-row {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
justify-content: center;
|
||||
padding: 8px;
|
||||
background: #0f172a;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #1e293b;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.sequence-row:hover { background: #1e293b; transform: scale(1.02); }
|
||||
|
||||
.num-ball {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
font-weight: bold;
|
||||
font-size: 1rem;
|
||||
background: #334155;
|
||||
color: white;
|
||||
}
|
||||
.num-elite { background: linear-gradient(135deg, #198754, #28a745); box-shadow: 0 0 10px rgba(40, 167, 69, 0.4); }
|
||||
.num-reclaimed { background: #10b981; animation: pulse-green 1.5s infinite; }
|
||||
|
||||
@keyframes pulse-green {
|
||||
0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7); }
|
||||
70% { transform: scale(1.1); box-shadow: 0 0 0 8px rgba(16, 185, 129, 0); }
|
||||
100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
|
||||
}
|
||||
.pulse-info { animation: pulse-blue 1s infinite; }
|
||||
@keyframes pulse-blue {
|
||||
0% { opacity: 1; } 50% { opacity: 0.5; } 100% { opacity: 1; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
let generatorRunning = false;
|
||||
let lotteryData = null;
|
||||
let currentStartNum = 1;
|
||||
let totalGenerated = 0;
|
||||
let countDuque = 0;
|
||||
let countTerno = 0;
|
||||
let countQuadra = 0;
|
||||
let countQuina = 0;
|
||||
let countSena = 0;
|
||||
let detectedHits = []; // { type, sequence, hits }
|
||||
let animationId = null;
|
||||
let annulledFunnel = new Set();
|
||||
const MAX_FUNNEL = 60;
|
||||
let eliteModeActive = false;
|
||||
let customEliteNumbers = [];
|
||||
|
||||
const synth = window.speechSynthesis;
|
||||
let voiceEnabled = true;
|
||||
|
||||
function speak(text) {
|
||||
if (!voiceEnabled || !synth) return;
|
||||
synth.cancel(); // Para a fala anterior para não encavalar
|
||||
const utter = new SpeechSynthesisUtterance(text);
|
||||
utter.lang = 'pt-BR';
|
||||
utter.rate = 1.1;
|
||||
|
||||
// Tenta pegar voz feminina
|
||||
const voices = synth.getVoices();
|
||||
const femaleVoice = voices.find(v => v.name.includes('Maria') || v.name.includes('Google português do Brasil'));
|
||||
if (femaleVoice) utter.voice = femaleVoice;
|
||||
|
||||
utter.onstart = () => document.getElementById("voice-indicator").classList.remove("d-none");
|
||||
utter.onend = () => document.getElementById("voice-indicator").classList.add("d-none");
|
||||
|
||||
const log = document.getElementById("voice-log");
|
||||
log.innerHTML += `> IA: ${text}<br>`;
|
||||
log.scrollTop = log.scrollHeight;
|
||||
|
||||
synth.speak(utter);
|
||||
}
|
||||
|
||||
const lotterySelect = document.getElementById("lottery-select");
|
||||
const funnelGrid = document.getElementById("funnel-grid");
|
||||
const funnelCountLabel = document.getElementById("funnel-count");
|
||||
const btnStart = document.getElementById("btn-start");
|
||||
const btnPause = document.getElementById("btn-pause");
|
||||
const container = document.getElementById("sequence-container");
|
||||
const viewport = document.querySelector(".generator-viewport");
|
||||
const elitePanel = document.getElementById("elite-panel");
|
||||
|
||||
// Novo Algoritmo de Probabilidade de Elite (Busca por Quadra 4+)
|
||||
function recalculateDeepElite() {
|
||||
if (!lotteryData || !lotteryData.name.toLowerCase().includes('quina')) return;
|
||||
|
||||
speak("Iniciando Recalibragem Neural de Alta Precisão para o próximo concurso.");
|
||||
|
||||
const allNums = Array.from({length: 80}, (_, i) => i + 1);
|
||||
const frequency = lotteryData.elite_greens; // Histórico de frequência
|
||||
const delayed = lotteryData.reclaimed_numbers; // Números em atraso
|
||||
|
||||
// Peso 1: Frequência Harmônica (30% de peso)
|
||||
// Peso 2: Atraso Crítico (50% de peso - Essencial para o próximo concurso)
|
||||
// Peso 3: Quadrantes (20% de peso - Equilíbrio)
|
||||
|
||||
let candidates = allNums.map(n => {
|
||||
let score = 0;
|
||||
|
||||
// Pontua por frequência (Inverso para evitar vício)
|
||||
const freqIdx = frequency.indexOf(n);
|
||||
if (freqIdx !== -1) score += (10 - (freqIdx / 8));
|
||||
|
||||
// Pontua por Atraso (Ouro para o próximo sorteio)
|
||||
if (delayed.includes(n)) score += 15;
|
||||
|
||||
// Bônus de Quadrante (Distribuição Geométrica)
|
||||
const quad = Math.ceil(n / 20);
|
||||
score += 2;
|
||||
|
||||
return { num: n, score: score };
|
||||
});
|
||||
|
||||
// Ordena por maior score de probabilidade
|
||||
candidates.sort((a, b) => b.score - a.score);
|
||||
|
||||
// Seleciona as 10 dezenas com Convergência Matemática
|
||||
const top10 = candidates.slice(0, 10).map(c => c.num).sort((a, b) => a - b);
|
||||
|
||||
customEliteNumbers = top10;
|
||||
fillEliteInputs(top10);
|
||||
updateElitePanel();
|
||||
|
||||
speak("Matemática de Elite aplicada. 10 dezenas de ouro selecionadas para busca de Quadra.");
|
||||
}
|
||||
|
||||
lotterySelect.addEventListener("change", async (e) => {
|
||||
const key = e.target.value;
|
||||
if (!key) return;
|
||||
|
||||
speak(`Iniciando análise para ${key}. Carregando base de dados histórica.`);
|
||||
const resp = await fetch(`/api/lottery-info/${key}/`);
|
||||
lotteryData = await resp.json();
|
||||
|
||||
// Ajusta visibilidade das colunas baseado no jogo
|
||||
if (key.toLowerCase().includes('quina')) {
|
||||
document.getElementById("col-duque").classList.remove("d-none");
|
||||
document.getElementById("col-terno").classList.remove("d-none");
|
||||
document.getElementById("col-sena").classList.add("d-none");
|
||||
// Se for quina, aplica a nova matemática automaticamente
|
||||
setTimeout(recalculateDeepElite, 1000);
|
||||
} else {
|
||||
document.getElementById("col-duque").classList.add("d-none");
|
||||
document.getElementById("col-terno").classList.add("d-none");
|
||||
document.getElementById("col-sena").classList.remove("d-none");
|
||||
}
|
||||
|
||||
setupFunnel(lotteryData.max_number);
|
||||
updateElitePanel();
|
||||
if (!key.toLowerCase().includes('quina')) {
|
||||
fillEliteInputs(lotteryData.elite_greens.slice(0, 10));
|
||||
}
|
||||
btnStart.disabled = false;
|
||||
document.getElementById("stats-bar").classList.remove("d-none");
|
||||
resetGenerator();
|
||||
});
|
||||
|
||||
function setupFunnel(max) {
|
||||
funnelGrid.innerHTML = "";
|
||||
annulledFunnel.clear();
|
||||
updateFunnelLabel();
|
||||
|
||||
for (let i = 1; i <= max; i++) {
|
||||
const ball = document.createElement("div");
|
||||
ball.className = "funnel-ball";
|
||||
ball.innerText = i.toString().padStart(2, "0");
|
||||
|
||||
// Pré-carrega os anulados do banco, se houver espaço
|
||||
if (lotteryData.annulled_numbers.includes(i) && annulledFunnel.size < MAX_FUNNEL) {
|
||||
ball.classList.add("active");
|
||||
annulledFunnel.add(i);
|
||||
}
|
||||
|
||||
ball.onclick = () => {
|
||||
if (ball.classList.contains("active")) {
|
||||
ball.classList.remove("active");
|
||||
annulledFunnel.delete(i);
|
||||
} else {
|
||||
if (annulledFunnel.size >= MAX_FUNNEL) {
|
||||
speak("Limite do funil atingido. Máximo de 60 dezenas.");
|
||||
return;
|
||||
}
|
||||
ball.classList.add("active");
|
||||
annulledFunnel.add(i);
|
||||
}
|
||||
updateFunnelLabel();
|
||||
};
|
||||
funnelGrid.appendChild(ball);
|
||||
}
|
||||
}
|
||||
|
||||
function updateFunnelLabel() {
|
||||
funnelCountLabel.innerText = `${annulledFunnel.size} / ${MAX_FUNNEL}`;
|
||||
}
|
||||
|
||||
function updateElitePanel() {
|
||||
elitePanel.innerHTML = "";
|
||||
const numbers = eliteModeActive ? customEliteNumbers : lotteryData.elite_greens.slice(0, 10);
|
||||
numbers.forEach(n => {
|
||||
const ball = document.createElement("div");
|
||||
ball.className = "num-ball num-elite mb-2";
|
||||
ball.style.width = "42px";
|
||||
ball.style.height = "42px";
|
||||
ball.innerText = n.toString().padStart(2, "0");
|
||||
elitePanel.appendChild(ball);
|
||||
});
|
||||
}
|
||||
|
||||
function resetGenerator() {
|
||||
generatorRunning = false;
|
||||
btnStart.classList.remove("d-none");
|
||||
btnPause.classList.add("d-none");
|
||||
btnDownload.classList.add("d-none");
|
||||
container.innerHTML = "";
|
||||
document.getElementById("hits-panel").innerHTML = `<div class="text-center text-slate-700 py-3 small">Nenhum acerto de elite detectado ainda...</div>`;
|
||||
currentStartNum = 1;
|
||||
totalGenerated = 0;
|
||||
countDuque = 0;
|
||||
countTerno = 0;
|
||||
countQuadra = 0;
|
||||
countQuina = 0;
|
||||
countSena = 0;
|
||||
detectedHits = [];
|
||||
updateStats();
|
||||
if (animationId) cancelAnimationFrame(animationId);
|
||||
}
|
||||
|
||||
function updateStats() {
|
||||
document.getElementById("count-total").innerText = totalGenerated.toLocaleString();
|
||||
if (document.getElementById("count-duque")) document.getElementById("count-duque").innerText = countDuque.toLocaleString();
|
||||
if (document.getElementById("count-terno")) document.getElementById("count-terno").innerText = countTerno.toLocaleString();
|
||||
document.getElementById("count-quadra").innerText = countQuadra.toLocaleString();
|
||||
document.getElementById("count-quina").innerText = countQuina.toLocaleString();
|
||||
if (document.getElementById("count-sena")) document.getElementById("count-sena").innerText = countSena.toLocaleString();
|
||||
|
||||
if (detectedHits.length > 0) {
|
||||
document.getElementById("btn-download").classList.remove("d-none");
|
||||
document.getElementById("btn-open-browser").classList.remove("d-none");
|
||||
localStorage.setItem('detectedHits', JSON.stringify(detectedHits));
|
||||
}
|
||||
}
|
||||
|
||||
// Filtra sequências longas (Permite no máx 2 números consecutivos)
|
||||
function hasForbiddenSequence(numbers) {
|
||||
const sorted = [...numbers].sort((a, b) => a - b);
|
||||
let currentSeq = 1;
|
||||
for (let i = 1; i < sorted.length; i++) {
|
||||
if (sorted[i] === sorted[i - 1] + 1) {
|
||||
currentSeq++;
|
||||
if (currentSeq > 2) return true; // Bloqueia 3 ou mais
|
||||
} else {
|
||||
currentSeq = 1;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function generateChunk() {
|
||||
if (!generatorRunning) return;
|
||||
|
||||
const nToDraw = lotteryData.numbers_to_draw;
|
||||
const maxNum = lotteryData.max_number;
|
||||
const eliteNums = eliteModeActive ? customEliteNumbers : [];
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
let sequence = [];
|
||||
let safetyCounter = 0;
|
||||
|
||||
// Tenta gerar uma sequência válida
|
||||
while (sequence.length < nToDraw && safetyCounter < 1000) {
|
||||
let val;
|
||||
if (eliteModeActive && eliteNums.length >= nToDraw) {
|
||||
// Modo 99.9%: Escolhe apenas entre os números de elite
|
||||
val = eliteNums[Math.floor(Math.random() * eliteNums.length)];
|
||||
} else {
|
||||
// Modo Normal: Incremento dinâmico
|
||||
let step = Math.floor(Math.random() * 3) + 1;
|
||||
val = ((currentStartNum - 1) % maxNum) + 1;
|
||||
currentStartNum += step;
|
||||
}
|
||||
|
||||
safetyCounter++;
|
||||
if (annulledFunnel.has(val)) continue;
|
||||
if (!sequence.includes(val)) sequence.push(val);
|
||||
}
|
||||
|
||||
if (sequence.length === nToDraw) {
|
||||
// Ordenar a sequência (Menor para o Maior) conforme solicitado
|
||||
sequence.sort((a, b) => a - b);
|
||||
|
||||
// Validação de Sequência Real (Máx 2 consecutivos)
|
||||
if (hasForbiddenSequence(sequence)) {
|
||||
continue; // Descarta e tenta a próxima no próximo loop
|
||||
}
|
||||
|
||||
const hits = sequence.filter(n => lotteryData.elite_greens.includes(n)).length;
|
||||
const isQuina = lotteryData.name.toLowerCase().includes('quina');
|
||||
const minHits = isQuina ? 2 : 4;
|
||||
|
||||
if (hits >= minHits) {
|
||||
let type = "";
|
||||
if (hits === 2) { type = "DUQUE"; countDuque++; }
|
||||
else if (hits === 3) { type = "TERNO"; countTerno++; }
|
||||
else if (hits === 4) { type = "QUADRA"; countQuadra++; }
|
||||
else if (hits === 5) { type = "QUINAS"; countQuina++; }
|
||||
else if (hits >= 6) { type = "SENA"; countSena++; }
|
||||
|
||||
const hitData = { type, sequence: [...sequence], hits };
|
||||
detectedHits.push(hitData);
|
||||
renderHit(hitData);
|
||||
|
||||
if (hits >= (isQuina ? 3 : 5)) {
|
||||
speak(`Alerta! ${type} detectada com alto índice de probabilidade.`);
|
||||
}
|
||||
}
|
||||
renderSequence(sequence);
|
||||
totalGenerated++;
|
||||
}
|
||||
}
|
||||
|
||||
updateStats();
|
||||
viewport.scrollTop = viewport.scrollHeight;
|
||||
|
||||
if (totalGenerated % 5000 === 0 && totalGenerated > 0) {
|
||||
speak(`Análise profunda em curso. ${totalGenerated} combinações verificadas.`);
|
||||
}
|
||||
|
||||
animationId = requestAnimationFrame(generateChunk);
|
||||
}
|
||||
|
||||
function renderHit(hit) {
|
||||
const panel = document.getElementById("hits-panel");
|
||||
if (detectedHits.length === 1) panel.innerHTML = "";
|
||||
|
||||
let badgeClass = "bg-danger";
|
||||
if (hit.hits === 2) badgeClass = "bg-secondary";
|
||||
else if (hit.hits === 3) badgeClass = "bg-primary";
|
||||
else if (hit.hits === 4) badgeClass = "bg-info";
|
||||
else if (hit.hits === 5) badgeClass = "bg-warning text-dark";
|
||||
|
||||
const div = document.createElement("div");
|
||||
div.className = "d-flex justify-content-between align-items-center p-2 rounded bg-slate-900 border border-slate-800 x-small";
|
||||
div.innerHTML = `
|
||||
<span class="badge ${badgeClass}">${hit.type}</span>
|
||||
<span class="text-white font-monospace">${hit.sequence.join(", ")}</span>
|
||||
<span class="text-slate-500">${hit.hits} pts</span>
|
||||
`;
|
||||
panel.prepend(div);
|
||||
}
|
||||
|
||||
function renderSequence(numbers) {
|
||||
const row = document.createElement("div");
|
||||
row.className = "sequence-row";
|
||||
|
||||
numbers.forEach(n => {
|
||||
const ball = document.createElement("div");
|
||||
ball.className = "num-ball";
|
||||
ball.innerText = n.toString().padStart(2, "0");
|
||||
|
||||
if (lotteryData.reclaimed_numbers.includes(n)) {
|
||||
ball.classList.add("num-reclaimed");
|
||||
} else if (lotteryData.elite_greens.includes(n)) {
|
||||
ball.classList.add("num-elite");
|
||||
}
|
||||
row.appendChild(ball);
|
||||
});
|
||||
|
||||
if (container.children.length > 30) container.removeChild(container.firstChild);
|
||||
container.appendChild(row);
|
||||
}
|
||||
|
||||
btnStart.addEventListener("click", () => {
|
||||
if (!lotteryData) return;
|
||||
generatorRunning = true;
|
||||
btnStart.classList.add("d-none");
|
||||
btnPause.classList.remove("d-none");
|
||||
speak("Motor Sequencial Iniciado. Aplicando Funil de Sessenta Dezenas.");
|
||||
generateChunk();
|
||||
});
|
||||
|
||||
btnPause.addEventListener("click", () => {
|
||||
generatorRunning = false;
|
||||
btnPause.classList.add("d-none");
|
||||
btnStart.classList.remove("d-none");
|
||||
speak("Motor pausado pelo operador.");
|
||||
if (animationId) cancelAnimationFrame(animationId);
|
||||
});
|
||||
|
||||
const btnDownload = document.getElementById("btn-download");
|
||||
btnDownload.addEventListener("click", () => {
|
||||
if (detectedHits.length === 0) return;
|
||||
|
||||
let content = `RELATÓRIO DE ELITE IA - ${lotteryData.name}\n`;
|
||||
content += `Data: ${new Date().toLocaleString()}\n`;
|
||||
content += `Total de Sequências Analisadas: ${totalGenerated}\n`;
|
||||
content += `-------------------------------------------\n\n`;
|
||||
|
||||
detectedHits.forEach(h => {
|
||||
content += `[${h.type}] ACERTOS: ${h.hits} | JOGO: ${h.sequence.join(", ")}\n`;
|
||||
});
|
||||
|
||||
const blob = new Blob([content], { type: "text/plain" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = `elite_detectada_${lotteryData.name.toLowerCase()}.txt`;
|
||||
a.click();
|
||||
speak("Download do relatório de elite concluído.");
|
||||
});
|
||||
|
||||
document.getElementById("btn-up").addEventListener("click", () => viewport.scrollBy({ top: -300, behavior: "smooth" }));
|
||||
document.getElementById("btn-down").addEventListener("click", () => viewport.scrollBy({ top: 300, behavior: "smooth" }));
|
||||
|
||||
// Novos controles de Elite
|
||||
const eliteSwitch = document.getElementById("elite-mode-switch");
|
||||
const eliteInputs = document.querySelectorAll(".elite-num-input");
|
||||
const btnApplyElite = document.getElementById("btn-apply-elite");
|
||||
const btnRecalculateMath = document.getElementById("btn-recalculate-math");
|
||||
|
||||
btnRecalculateMath.addEventListener("click", () => {
|
||||
recalculateDeepElite();
|
||||
});
|
||||
|
||||
function fillEliteInputs(numbers) {
|
||||
eliteInputs.forEach((input, idx) => {
|
||||
if (numbers[idx]) input.value = numbers[idx];
|
||||
});
|
||||
updateCustomElite();
|
||||
}
|
||||
|
||||
function updateCustomElite() {
|
||||
customEliteNumbers = Array.from(eliteInputs)
|
||||
.map(input => parseInt(input.value))
|
||||
.filter(val => !isNaN(val) && val > 0);
|
||||
}
|
||||
|
||||
eliteSwitch.addEventListener("change", (e) => {
|
||||
eliteModeActive = e.target.checked;
|
||||
const indicator = document.getElementById("elite-indicator");
|
||||
|
||||
if (eliteModeActive) {
|
||||
indicator.classList.remove("d-none");
|
||||
speak("Modo de Precisão 99,9% Ativado. O Supercomputador focará apenas nas dezenas de elite escolhidas.");
|
||||
} else {
|
||||
indicator.classList.add("d-none");
|
||||
speak("Modo Normal Restaurado.");
|
||||
}
|
||||
updateElitePanel();
|
||||
});
|
||||
|
||||
btnApplyElite.addEventListener("click", () => {
|
||||
updateCustomElite();
|
||||
if (customEliteNumbers.length < (lotteryData ? lotteryData.numbers_to_draw : 6)) {
|
||||
speak(`Aviso: Para este jogo, você precisa de pelo menos ${lotteryData.numbers_to_draw} números de elite.`);
|
||||
return;
|
||||
}
|
||||
speak("Configurações de Elite aplicadas com sucesso. Pronto para calcular acertos.");
|
||||
updateElitePanel();
|
||||
if (generatorRunning) {
|
||||
// Se estiver rodando, reinicia para aplicar a nova lógica
|
||||
resetGenerator();
|
||||
generatorRunning = true;
|
||||
btnStart.classList.add("d-none");
|
||||
btnPause.classList.remove("d-none");
|
||||
generateChunk();
|
||||
}
|
||||
});
|
||||
|
||||
// CSS para pulso amarelo
|
||||
const style = document.createElement("style");
|
||||
style.innerHTML = `
|
||||
.pulse-warning { animation: pulse-yellow 1.5s infinite; }
|
||||
@keyframes pulse-yellow {
|
||||
0% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.7); }
|
||||
70% { box-shadow: 0 0 0 10px rgba(255, 193, 7, 0); }
|
||||
100% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0); }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
</script>
|
||||
{% endblock %}
|
||||
17
core/urls.py
17
core/urls.py
@ -1,7 +1,18 @@
|
||||
from django.urls import path
|
||||
|
||||
from .views import home
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path("", home, name="home"),
|
||||
path('', views.home, name='home'),
|
||||
path('admin-loto/', views.admin_login, name='admin_login'),
|
||||
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'),
|
||||
path('live-math/', views.live_math, name='live_math'),
|
||||
path('gerador-sequencial/', views.sequential_generator, name='sequential_generator'),
|
||||
path('api/lottery-info/<str:lottery_key>/', views.lottery_info_api, name='lottery_info_api'),
|
||||
path('resultados/', views.lottery_results, name='lottery_results'),
|
||||
path('resultados/download/<int:lottery_id>/', views.download_funnel, name='download_funnel'),
|
||||
path('supercomputador/relatorio/', views.full_report, name='full_report'),
|
||||
path('gerador-sequencial/acertos/', views.hits_report, name='hits_report'),
|
||||
]
|
||||
|
||||
713
core/views.py
713
core/views.py
@ -1,25 +1,726 @@
|
||||
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
|
||||
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):
|
||||
"""Render the landing screen with loader and environment details."""
|
||||
"""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": "New Style",
|
||||
"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,
|
||||
"host_name": host_name,
|
||||
"project_description": os.getenv("PROJECT_DESCRIPTION", ""),
|
||||
"project_image_url": os.getenv("PROJECT_IMAGE_URL", ""),
|
||||
"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")
|
||||
|
||||
@ -1,4 +1,493 @@
|
||||
/* Custom styles for the application */
|
||||
body {
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
:root {
|
||||
--ink: #0b1f2a;
|
||||
--ink-soft: #123041;
|
||||
--primary: #0f766e;
|
||||
--primary-strong: #0b5f59;
|
||||
--secondary: #f59e0b;
|
||||
--accent: #ea580c;
|
||||
--surface: #ffffff;
|
||||
--surface-muted: #f4f7f7;
|
||||
--line: rgba(12, 42, 58, 0.12);
|
||||
--glow: rgba(15, 118, 110, 0.25);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body.app-body {
|
||||
margin: 0;
|
||||
font-family: "Work Sans", sans-serif;
|
||||
color: var(--ink);
|
||||
background: radial-gradient(circle at top, #e9f2f1 0%, #f7faf9 40%, #ffffff 100%);
|
||||
}
|
||||
|
||||
body.app-body::before {
|
||||
content: "";
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background-image: radial-gradient(circle at 20% 20%, rgba(15, 118, 110, 0.1), transparent 45%),
|
||||
radial-gradient(circle at 90% 10%, rgba(245, 158, 11, 0.12), transparent 40%),
|
||||
linear-gradient(120deg, rgba(15, 118, 110, 0.07), transparent 50%);
|
||||
z-index: -2;
|
||||
}
|
||||
|
||||
.hero-shell {
|
||||
position: relative;
|
||||
background: linear-gradient(160deg, #0b1f2a 0%, #0f2f3d 60%, #113844 100%);
|
||||
color: #f8fbfb;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hero-shell::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: radial-gradient(circle at 10% 20%, rgba(245, 158, 11, 0.2), transparent 40%),
|
||||
radial-gradient(circle at 80% 20%, rgba(15, 118, 110, 0.3), transparent 45%);
|
||||
opacity: 0.8;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.site-header {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 1.5rem 0 0.5rem;
|
||||
}
|
||||
|
||||
.navbar-brand.brand {
|
||||
font-family: "Space Grotesk", sans-serif;
|
||||
font-size: 1.4rem;
|
||||
font-weight: 700;
|
||||
color: #fdfdfd;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.navbar .nav-link {
|
||||
color: rgba(248, 251, 251, 0.85);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.navbar .nav-link:hover,
|
||||
.navbar .nav-link:focus {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.btn-brand {
|
||||
background: linear-gradient(130deg, var(--secondary), var(--accent));
|
||||
border: none;
|
||||
color: #1b1b1b;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 10px 30px rgba(234, 88, 12, 0.3);
|
||||
}
|
||||
|
||||
.btn-brand:hover,
|
||||
.btn-brand:focus {
|
||||
color: #1b1b1b;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 14px 35px rgba(234, 88, 12, 0.35);
|
||||
}
|
||||
|
||||
.btn-ghost {
|
||||
border: 1px solid rgba(248, 251, 251, 0.3);
|
||||
color: #f8fbfb;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.btn-ghost:hover,
|
||||
.btn-ghost:focus {
|
||||
color: #f8fbfb;
|
||||
border-color: rgba(248, 251, 251, 0.6);
|
||||
}
|
||||
|
||||
.site-main {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 2rem 0 5rem;
|
||||
}
|
||||
|
||||
.hero-section {
|
||||
padding: 2rem 0 4rem;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.24em;
|
||||
font-size: 0.75rem;
|
||||
color: rgba(248, 251, 251, 0.6);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-family: "Space Grotesk", sans-serif;
|
||||
font-size: clamp(2.5rem, 3.5vw, 3.6rem);
|
||||
font-weight: 700;
|
||||
line-height: 1.1;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.hero-lead {
|
||||
font-size: 1.1rem;
|
||||
color: rgba(248, 251, 251, 0.75);
|
||||
max-width: 36rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.hero-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.hero-stats {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
padding: 1rem 1.25rem;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.9rem;
|
||||
color: rgba(248, 251, 251, 0.7);
|
||||
}
|
||||
|
||||
.card-glass {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
border-radius: 20px;
|
||||
padding: 2rem;
|
||||
box-shadow: 0 24px 50px rgba(3, 19, 29, 0.4);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
.card-glass-header {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-family: "Space Grotesk", sans-serif;
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.card-subtitle {
|
||||
color: rgba(248, 251, 251, 0.7);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.generator-form .form-label,
|
||||
.generator-form .form-text {
|
||||
color: rgba(248, 251, 251, 0.75);
|
||||
}
|
||||
|
||||
.generator-form .form-select,
|
||||
.generator-form .form-control {
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
color: #fdfdfd;
|
||||
}
|
||||
|
||||
.generator-form .form-select:focus,
|
||||
.generator-form .form-control:focus {
|
||||
border-color: var(--secondary);
|
||||
box-shadow: 0 0 0 0.2rem rgba(245, 158, 11, 0.2);
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.insights-section,
|
||||
.games-section,
|
||||
.process-section,
|
||||
.cta-section {
|
||||
padding: 4rem 0;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.section-header h2 {
|
||||
font-family: "Space Grotesk", sans-serif;
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.section-header p {
|
||||
color: var(--ink-soft);
|
||||
max-width: 36rem;
|
||||
}
|
||||
|
||||
.card-soft {
|
||||
background: var(--surface);
|
||||
border-radius: 20px;
|
||||
padding: 2rem;
|
||||
box-shadow: 0 18px 40px rgba(11, 31, 42, 0.08);
|
||||
border: 1px solid var(--line);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.card-soft-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.35rem 0.8rem;
|
||||
border-radius: 999px;
|
||||
background: rgba(15, 118, 110, 0.1);
|
||||
color: var(--primary-strong);
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.metrics {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.metric {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.75rem 1rem;
|
||||
background: var(--surface-muted);
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.metric-label {
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.number-groups {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.group-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.badge-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.badge {
|
||||
border-radius: 999px;
|
||||
padding: 0.3rem 0.7rem;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.badge-hot {
|
||||
background: rgba(234, 88, 12, 0.15);
|
||||
color: #c2410c;
|
||||
}
|
||||
|
||||
.badge-cold {
|
||||
background: rgba(15, 118, 110, 0.12);
|
||||
color: var(--primary-strong);
|
||||
}
|
||||
|
||||
.suggestions-grid {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.suggestion {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--surface-muted);
|
||||
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;
|
||||
border-radius: 999px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #ffffff;
|
||||
border: 1px solid var(--line);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.placeholder-card {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
align-items: center;
|
||||
background: var(--surface-muted);
|
||||
}
|
||||
|
||||
.placeholder-icon {
|
||||
font-size: 2.4rem;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.games-section {
|
||||
background: #f9fbfb;
|
||||
}
|
||||
|
||||
.game-card {
|
||||
background: #ffffff;
|
||||
border-radius: 18px;
|
||||
padding: 1.75rem;
|
||||
border: 1px solid var(--line);
|
||||
box-shadow: 0 14px 30px rgba(11, 31, 42, 0.08);
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.game-card-header h3 {
|
||||
font-family: "Space Grotesk", sans-serif;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.game-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.game-odds {
|
||||
font-weight: 600;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.process-card {
|
||||
background: #ffffff;
|
||||
border-radius: 18px;
|
||||
padding: 2rem;
|
||||
border: 1px solid var(--line);
|
||||
box-shadow: 0 12px 28px rgba(11, 31, 42, 0.08);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.process-card h3 {
|
||||
font-family: "Space Grotesk", sans-serif;
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 0.6rem;
|
||||
}
|
||||
|
||||
.step {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 12px;
|
||||
background: rgba(15, 118, 110, 0.15);
|
||||
color: var(--primary-strong);
|
||||
font-weight: 700;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.cta-section {
|
||||
background: linear-gradient(140deg, rgba(15, 118, 110, 0.12), rgba(245, 158, 11, 0.18));
|
||||
}
|
||||
|
||||
.cta-card {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 2rem;
|
||||
padding: 2.5rem;
|
||||
border-radius: 20px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 18px 40px rgba(11, 31, 42, 0.08);
|
||||
border: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.cta-card h2 {
|
||||
font-family: "Space Grotesk", sans-serif;
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.site-footer {
|
||||
padding: 2rem 0 3rem;
|
||||
color: var(--ink-soft);
|
||||
}
|
||||
|
||||
.site-footer .container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.footer-meta {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.hero-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.card-glass {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.hero-shell {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
padding: 0.9rem 1rem;
|
||||
}
|
||||
|
||||
.card-soft-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user