CONFIGURAÇÃO 1
This commit is contained in:
parent
b61b163577
commit
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.
42
core/forms.py
Normal file
42
core/forms.py
Normal file
@ -0,0 +1,42 @@
|
||||
from django import forms
|
||||
|
||||
|
||||
class LotterySimulatorForm(forms.Form):
|
||||
lottery_type = forms.ChoiceField(
|
||||
label="Loteria",
|
||||
choices=[],
|
||||
widget=forms.Select(
|
||||
attrs={
|
||||
"class": "form-select form-select-lg",
|
||||
}
|
||||
),
|
||||
)
|
||||
draws_to_consider = forms.IntegerField(
|
||||
label="Ultimos sorteios",
|
||||
min_value=3,
|
||||
max_value=20,
|
||||
initial=10,
|
||||
widget=forms.NumberInput(
|
||||
attrs={
|
||||
"class": "form-control",
|
||||
"inputmode": "numeric",
|
||||
}
|
||||
),
|
||||
)
|
||||
games_to_generate = forms.IntegerField(
|
||||
label="Jogos sugeridos",
|
||||
min_value=1,
|
||||
max_value=12,
|
||||
initial=4,
|
||||
widget=forms.NumberInput(
|
||||
attrs={
|
||||
"class": "form-control",
|
||||
"inputmode": "numeric",
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
lottery_choices = kwargs.pop("lottery_choices", [])
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["lottery_type"].choices = lottery_choices
|
||||
Binary file not shown.
@ -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>
|
||||
|
||||
@ -1,145 +1,257 @@
|
||||
{% 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>
|
||||
<li class="nav-item"><a class="nav-link" href="/admin/">Admin</a></li>
|
||||
<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="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">Quentes no recorte</div>
|
||||
<div class="badge-grid">
|
||||
{% for number in result.hot_numbers %}
|
||||
<span class="badge badge-hot">{{ number }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="group-title">Frias no recorte</div>
|
||||
<div class="badge-grid">
|
||||
{% for number in result.cold_numbers %}
|
||||
<span class="badge badge-cold">{{ number }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</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">
|
||||
{% for suggestion in result.suggestions %}
|
||||
<div class="suggestion">
|
||||
{% for number in suggestion %}
|
||||
<span class="ball">{{ number }}</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 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 %}
|
||||
|
||||
175
core/views.py
175
core/views.py
@ -1,25 +1,194 @@
|
||||
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.utils import timezone
|
||||
|
||||
from .forms import LotterySimulatorForm
|
||||
|
||||
|
||||
LOTTERY_CONFIGS = {
|
||||
"mega_sena": {
|
||||
"label": "Mega-Sena",
|
||||
"range_max": 60,
|
||||
"picks": 6,
|
||||
"sample_draws": 15,
|
||||
"seed": 1982,
|
||||
"tagline": "6 dezenas entre 60",
|
||||
},
|
||||
"quina": {
|
||||
"label": "Quina",
|
||||
"range_max": 80,
|
||||
"picks": 5,
|
||||
"sample_draws": 15,
|
||||
"seed": 1971,
|
||||
"tagline": "5 dezenas entre 80",
|
||||
},
|
||||
"dupla_sena": {
|
||||
"label": "Dupla Sena",
|
||||
"range_max": 50,
|
||||
"picks": 6,
|
||||
"sample_draws": 15,
|
||||
"seed": 1990,
|
||||
"tagline": "6 dezenas entre 50",
|
||||
},
|
||||
"lotomania": {
|
||||
"label": "Lotomania",
|
||||
"range_max": 100,
|
||||
"picks": 50,
|
||||
"sample_draws": 12,
|
||||
"seed": 1999,
|
||||
"tagline": "50 dezenas entre 100",
|
||||
},
|
||||
"lotofacil": {
|
||||
"label": "Lotofacil",
|
||||
"range_max": 25,
|
||||
"picks": 15,
|
||||
"sample_draws": 20,
|
||||
"seed": 1994,
|
||||
"tagline": "15 dezenas entre 25",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _generate_sample_draws(config):
|
||||
rng = random.Random(config["seed"])
|
||||
draws = []
|
||||
population = list(range(1, config["range_max"] + 1))
|
||||
for _ in range(config["sample_draws"]):
|
||||
draws.append(sorted(rng.sample(population, config["picks"])))
|
||||
return draws
|
||||
|
||||
|
||||
def _weighted_unique_sample(numbers, weights, picks, rng):
|
||||
available = list(zip(numbers, weights))
|
||||
selection = []
|
||||
for _ in range(picks):
|
||||
total_weight = sum(weight for _, weight in available)
|
||||
if total_weight <= 0:
|
||||
choice = rng.choice(available)
|
||||
selection.append(choice[0])
|
||||
available.remove(choice)
|
||||
continue
|
||||
pick = rng.uniform(0, total_weight)
|
||||
cumulative = 0
|
||||
for index, (number, weight) in enumerate(available):
|
||||
cumulative += weight
|
||||
if cumulative >= pick:
|
||||
selection.append(number)
|
||||
del available[index]
|
||||
break
|
||||
return selection
|
||||
|
||||
|
||||
def _format_odds(total_combinations):
|
||||
if total_combinations >= 1_000_000_000_000:
|
||||
return f"1 em {total_combinations:.2e}"
|
||||
return f"1 em {total_combinations:,}".replace(",", ".")
|
||||
|
||||
|
||||
def _format_percent(odds):
|
||||
percent = 100 / odds
|
||||
if percent < 0.000001:
|
||||
return "<0,000001%"
|
||||
return f"{percent:.6f}%".replace(".", ",")
|
||||
|
||||
|
||||
def home(request):
|
||||
"""Render the landing screen with loader and environment details."""
|
||||
"""Render the landing screen with lottery simulator and insights."""
|
||||
host_name = request.get_host().lower()
|
||||
agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic"
|
||||
now = timezone.now()
|
||||
|
||||
lottery_choices = [
|
||||
(key, config["label"]) for key, config in LOTTERY_CONFIGS.items()
|
||||
]
|
||||
lottery_cards = []
|
||||
for key, config in LOTTERY_CONFIGS.items():
|
||||
total_combinations = math.comb(config["range_max"], config["picks"])
|
||||
lottery_cards.append(
|
||||
{
|
||||
"key": key,
|
||||
"label": config["label"],
|
||||
"tagline": config["tagline"],
|
||||
"range_max": config["range_max"],
|
||||
"picks": config["picks"],
|
||||
"odds": _format_odds(total_combinations),
|
||||
}
|
||||
)
|
||||
|
||||
form = LotterySimulatorForm(
|
||||
request.POST or None,
|
||||
lottery_choices=lottery_choices,
|
||||
)
|
||||
result = None
|
||||
|
||||
if form.is_valid():
|
||||
lottery_key = form.cleaned_data["lottery_type"]
|
||||
draws_to_consider = form.cleaned_data["draws_to_consider"]
|
||||
games_to_generate = form.cleaned_data["games_to_generate"]
|
||||
config = LOTTERY_CONFIGS[lottery_key]
|
||||
draws = _generate_sample_draws(config)
|
||||
draws_to_consider = min(draws_to_consider, len(draws))
|
||||
recent_draws = draws[-draws_to_consider:]
|
||||
frequency = Counter(
|
||||
number for draw in recent_draws for number in draw
|
||||
)
|
||||
numbers = list(range(1, config["range_max"] + 1))
|
||||
weights = [frequency.get(number, 0) + 1 for number in numbers]
|
||||
rng = random.Random(f"{lottery_key}-{draws_to_consider}-{games_to_generate}")
|
||||
|
||||
suggestions = []
|
||||
for _ in range(games_to_generate):
|
||||
suggestion = _weighted_unique_sample(
|
||||
numbers, weights, config["picks"], rng
|
||||
)
|
||||
suggestions.append(sorted(suggestion))
|
||||
|
||||
total_combinations = math.comb(config["range_max"], config["picks"])
|
||||
hot_numbers = [
|
||||
number for number, _ in frequency.most_common(8)
|
||||
]
|
||||
cold_numbers = [
|
||||
number
|
||||
for number, _ in sorted(
|
||||
frequency.items(), key=lambda item: item[1]
|
||||
)
|
||||
]
|
||||
missing_numbers = [
|
||||
number for number in numbers if number not in frequency
|
||||
]
|
||||
cold_numbers = (missing_numbers + cold_numbers)[:8]
|
||||
result = {
|
||||
"lottery": config["label"],
|
||||
"draws_used": draws_to_consider,
|
||||
"total_combinations": f"{total_combinations:,}".replace(",", "."),
|
||||
"odds": _format_odds(total_combinations),
|
||||
"percent": _format_percent(total_combinations),
|
||||
"suggestions": suggestions,
|
||||
"hot_numbers": hot_numbers,
|
||||
"cold_numbers": cold_numbers,
|
||||
}
|
||||
|
||||
context = {
|
||||
"project_name": "New Style",
|
||||
"project_name": "LotoPulse",
|
||||
"project_description": (
|
||||
"Analise loterias brasileiras, gere jogos e acompanhe "
|
||||
"probabilidades com base nos sorteios mais recentes."
|
||||
),
|
||||
"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", ""),
|
||||
"deployment_timestamp": now.strftime("%Y%m%d%H%M%S"),
|
||||
"form": form,
|
||||
"result": result,
|
||||
"lottery_cards": lottery_cards,
|
||||
}
|
||||
return render(request, "core/index.html", context)
|
||||
|
||||
@ -1,4 +1,479 @@
|
||||
/* 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 {
|
||||
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