mirror of
https://github.com/adrigongv23/G26---Telemetry-Software.git
synced 2026-08-25 19:43:16 +02:00
Inicio del Django y base de datos
This commit is contained in:
parent
57494f02e5
commit
0e0c8d7d1b
66 changed files with 647 additions and 69 deletions
|
|
@ -2,34 +2,28 @@ import socket
|
|||
import time
|
||||
import math
|
||||
import random
|
||||
from datetime import datetime # <--- IMPORTANTE: Nueva librería
|
||||
|
||||
# --- CONFIGURACIÓN ---
|
||||
UDP_IP = "127.0.0.1"
|
||||
UDP_PORT = 4210
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
|
||||
print("TELEMETRÍA SIMULADA - ENVÍO DE DATOS")
|
||||
print(" Formato: ECT | RPM | BATT | SPEED | HH:MM:SS")
|
||||
print(f"--- SIMULADOR DE COCHE G26 ---")
|
||||
print(f"Enviando datos falsos a {UDP_IP}:{UDP_PORT}")
|
||||
|
||||
t = 0
|
||||
while True:
|
||||
# Generación de datos (Igual que antes)
|
||||
ect = 85 + (5 * math.sin(t / 5.0)) + random.uniform(-0.2, 0.2)
|
||||
rpm = 3000 + (1000 * math.sin(t / 2.0))
|
||||
batt = 13.8 + random.uniform(-0.1, 0.1)
|
||||
speed = 90 + (30 * math.sin(t / 3.0))
|
||||
# Generar una temperatura que sube y baja mucho para probar todos los colores
|
||||
# Oscilará entre 40ºC y 120ºC
|
||||
ect = 80 + (40 * math.sin(t / 10.0)) + random.uniform(-0.5, 0.5)
|
||||
|
||||
# --- CAMBIO AQUÍ: OBTENER HORA REAL ---
|
||||
# Obtenemos hora actual y la convertimos a texto
|
||||
hora_real = datetime.now().strftime("%H:%M:%S.%f")[:-3] # Hora con milisegundos
|
||||
|
||||
# Mensaje con la hora legible al final
|
||||
mensaje = f"{ect:.2f}|{int(rpm)}|{batt:.2f}|{int(speed)}|{hora_real}"
|
||||
# Enviar solo el número
|
||||
mensaje = f"{ect:.2f}"
|
||||
|
||||
sock.sendto(mensaje.encode(), (UDP_IP, UDP_PORT))
|
||||
|
||||
print(f"TX: {mensaje}")
|
||||
print(f"Simulando: {mensaje} °C")
|
||||
|
||||
t += 0.1
|
||||
time.sleep(0.002)
|
||||
time.sleep(0.05)
|
||||
|
|
@ -2,91 +2,90 @@ import socket
|
|||
import matplotlib.pyplot as plt
|
||||
import matplotlib.animation as animation
|
||||
from collections import deque
|
||||
from datetime import datetime
|
||||
|
||||
# --- CONFIGURACIÓN ---
|
||||
UDP_IP = "0.0.0.0"
|
||||
UDP_PORT = 4210
|
||||
MAX_PUNTOS = 150
|
||||
UDP_IP = "0.0.0.0" # Escuchamos en Todas las interfaces posibles (WiFi, Ethernet...)
|
||||
UDP_PORT = 4210 # Mismo puerto que usamos para la ESP32
|
||||
MAX_PUNTOS = 200 # Vamos a mostrar en la gráfica los últimos 200 datos
|
||||
|
||||
# Cola de datos
|
||||
data_ect = deque([0]*MAX_PUNTOS, maxlen=MAX_PUNTOS)
|
||||
data_rpm = deque([0]*MAX_PUNTOS, maxlen=MAX_PUNTOS)
|
||||
data_bat = deque([0]*MAX_PUNTOS, maxlen=MAX_PUNTOS)
|
||||
data_spd = deque([0]*MAX_PUNTOS, maxlen=MAX_PUNTOS)
|
||||
|
||||
#Ocultamos estos valores ya que por el momento únicamente vamos a trabajar con la temperatura
|
||||
#data_rpm = deque([0]*MAX_PUNTOS, maxlen=MAX_PUNTOS)
|
||||
#data_bat = deque([0]*MAX_PUNTOS, maxlen=MAX_PUNTOS)
|
||||
|
||||
# Configuración del socket UDP
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.bind((UDP_IP, UDP_PORT))
|
||||
sock.setblocking(False)
|
||||
|
||||
# --- DISEÑO GRÁFICO ---
|
||||
plt.style.use('dark_background')
|
||||
fig, (ax1, ax2, ax3, ax4) = plt.subplots(4, 1, sharex=True, figsize=(8, 10))
|
||||
fig, ax1 = plt.subplots(figsize=(10, 6)) # Usaremos una única ventana grande
|
||||
|
||||
# Título principal + RELOJ
|
||||
fig.canvas.manager.set_window_title('G26 Telemetry - Monitor de Temperatura')
|
||||
fig.suptitle('GADES TELEMETRY SYSTEM', fontsize=16, fontweight='bold', color='white')
|
||||
# Creamos un texto vacío arriba a la derecha para la hora
|
||||
texto_reloj = fig.text(0.85, 0.97, "--:--:--", fontsize=12, color='cyan', ha='center', fontweight='bold')
|
||||
|
||||
def setup_plot(ax, color, label, y_min, y_max):
|
||||
line, = ax.plot([], [], color=color, lw=2)
|
||||
ax.set_ylabel(label)
|
||||
ax.set_ylim(y_min, y_max)
|
||||
ax.grid(True, alpha=0.3, linestyle='--')
|
||||
props = dict(boxstyle='round', facecolor='black', alpha=0.7, edgecolor=color)
|
||||
text = ax.text(0.03, 0.85, '', transform=ax.transAxes,
|
||||
fontsize=14, color=color, fontweight='bold', bbox=props)
|
||||
return line, text
|
||||
# Reloj en la esquina superior derecha
|
||||
txt_reloj = fig.text(0.85, 0.95, '--:--:--', fontsize=12, color='white', fontweight='bold')
|
||||
|
||||
line_ect, txt_ect = setup_plot(ax1, '#ff3333', 'ECT (°C)', 50, 110)
|
||||
line_rpm, txt_rpm = setup_plot(ax2, '#ffff33', 'RPM', 0, 5000)
|
||||
line_bat, txt_bat = setup_plot(ax3, '#33ffff', 'BATT (V)', 12, 15)
|
||||
line_spd, txt_spd = setup_plot(ax4, '#33ff33', 'SPEED', 0, 140)
|
||||
# Configuración de la línea (empieza en blanco pero cambiará)
|
||||
line_ect, = ax1.plot([], [], color='white', lw=3)
|
||||
ax1.set_ylabel('Temperatura (°C)', fontsize=14)
|
||||
ax1.set_ylim(0, 130) # Rango de temperatura (0 a 130 grados)
|
||||
ax1.grid(True, alpha=0.3, linestyle='--')
|
||||
|
||||
# Caja de texto con el valor actual
|
||||
props = dict(boxstyle='round', facecolor='black', alpha=0.8, edgecolor='white')
|
||||
txt_ect = ax1.text(0.02, 0.90, 'ESPERANDO...', transform=ax1.transAxes,
|
||||
fontsize=16, color='white', fontweight='bold', bbox=props)
|
||||
|
||||
def get_color(temp):
|
||||
if temp > 105:
|
||||
return '#ff3333' # ROJO (Peligro)
|
||||
elif temp >= 95:
|
||||
return '#ffff33' # AMARILLO (Precaución)
|
||||
elif temp >= 65:
|
||||
return '#33ff33' # VERDE (Ok)
|
||||
else:
|
||||
return '#33ffff' # AZUL (Frío)
|
||||
|
||||
def update(frame):
|
||||
# 1. Actualizar el Reloj con la hora del PC
|
||||
ahora = datetime.now().strftime("%H:%M:%S")
|
||||
txt_reloj.set_text(f"HORA: {ahora}")
|
||||
try:
|
||||
while True:
|
||||
data, addr = sock.recvfrom(1024)
|
||||
msg = data.decode('utf-8')
|
||||
partes = msg.split('|')
|
||||
# partes = msg.split('|')
|
||||
|
||||
# Extraer valores numéricos
|
||||
val_ect = float(partes[0])
|
||||
val_rpm = float(partes[1])
|
||||
val_bat = float(partes[2])
|
||||
val_spd = float(partes[3])
|
||||
try:
|
||||
val_ect = float(msg) # Convertimos texto a número
|
||||
|
||||
# --- EXTRAER LA HORA (Es el elemento 4) ---
|
||||
val_time = partes[4]
|
||||
data_ect.append(val_ect) # Guardamos el dato
|
||||
color_actual = get_color(val_ect) # Para que el valor cambie de color segun la temepratura
|
||||
|
||||
# Guardar datos
|
||||
data_ect.append(val_ect)
|
||||
data_rpm.append(val_rpm)
|
||||
data_bat.append(val_bat)
|
||||
data_spd.append(val_spd)
|
||||
txt_ect.set_text(f"TEMP: {val_ect: .1f} °C")
|
||||
txt_ect.set_color(color_actual)
|
||||
|
||||
# Actualizar textos gráficas
|
||||
txt_ect.set_text(f"TEMP: {val_ect:.1f} °C")
|
||||
txt_rpm.set_text(f"RPM: {int(val_rpm)}")
|
||||
txt_bat.set_text(f"BATT: {val_bat:.2f} V")
|
||||
txt_spd.set_text(f"VEL: {int(val_spd)} Km/h")
|
||||
|
||||
# --- ACTUALIZAR EL RELOJ DE ARRIBA ---
|
||||
texto_reloj.set_text(f"{val_time}")
|
||||
except ValueError:
|
||||
print(f"Error de formato: {msg}")
|
||||
|
||||
except BlockingIOError:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
|
||||
# Actualizamos la línea gráfica
|
||||
x_range = range(len(data_ect))
|
||||
line_ect.set_data(x_range, data_ect)
|
||||
line_rpm.set_data(x_range, data_rpm)
|
||||
line_bat.set_data(x_range, data_bat)
|
||||
line_spd.set_data(x_range, data_spd)
|
||||
ax1.set_xlim(0, max(len(data_ect), 1))
|
||||
|
||||
ax1.set_xlim(0, len(data_ect))
|
||||
return line_ect, txt_ect, txt_reloj
|
||||
|
||||
return line_ect, line_rpm, line_bat, line_spd, txt_ect, txt_rpm, txt_bat, txt_spd, texto_reloj
|
||||
|
||||
plt.tight_layout(rect=[0, 0, 1, 0.96]) # Dejar hueco arriba para el reloj
|
||||
ani = animation.FuncAnimation(fig, update, interval=20, blit=False, cache_frame_data=False)
|
||||
plt.show()
|
||||
0
Plataform_Web/documentos/__init__.py
Normal file
0
Plataform_Web/documentos/__init__.py
Normal file
BIN
Plataform_Web/documentos/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
Plataform_Web/documentos/__pycache__/__init__.cpython-314.pyc
Normal file
Binary file not shown.
BIN
Plataform_Web/documentos/__pycache__/admin.cpython-314.pyc
Normal file
BIN
Plataform_Web/documentos/__pycache__/admin.cpython-314.pyc
Normal file
Binary file not shown.
BIN
Plataform_Web/documentos/__pycache__/apps.cpython-314.pyc
Normal file
BIN
Plataform_Web/documentos/__pycache__/apps.cpython-314.pyc
Normal file
Binary file not shown.
BIN
Plataform_Web/documentos/__pycache__/models.cpython-314.pyc
Normal file
BIN
Plataform_Web/documentos/__pycache__/models.cpython-314.pyc
Normal file
Binary file not shown.
14
Plataform_Web/documentos/admin.py
Normal file
14
Plataform_Web/documentos/admin.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
from django.contrib import admin
|
||||
from .models import Documento
|
||||
|
||||
@admin.register(Documento)
|
||||
class DocumentoAdmin(admin.ModelAdmin):
|
||||
list_display = ('titulo', 'categoria', 'tipo', 'temporada', 'subido_por', 'fecha_subida')
|
||||
list_filter = ('temporada', 'categoria', 'tipo') # ¡Filtros laterales muy útiles!
|
||||
search_fields = ('titulo', 'descripcion')
|
||||
|
||||
# Esto hace que el campo "subido_por" se rellene solo con tu usuario al crear un doc
|
||||
def save_model(self, request, obj, form, change):
|
||||
if not obj.subido_por:
|
||||
obj.subido_por = request.user
|
||||
super().save_model(request, obj, form, change)
|
||||
5
Plataform_Web/documentos/apps.py
Normal file
5
Plataform_Web/documentos/apps.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class DocumentosConfig(AppConfig):
|
||||
name = 'documentos'
|
||||
37
Plataform_Web/documentos/migrations/0001_initial.py
Normal file
37
Plataform_Web/documentos/migrations/0001_initial.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Generated by Django 6.0.2 on 2026-02-17 11:36
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('temporadas', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Documento',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('titulo', models.CharField(max_length=100, verbose_name='Título del documento')),
|
||||
('archivo', models.FileField(upload_to='ingenieria_docs/')),
|
||||
('categoria', models.CharField(choices=[('aerodinamica', 'Aerodinámica'), ('chasis', 'Chasis'), ('suspension', 'Suspensión'), ('motor', 'Motor/Powertrain'), ('electronica', 'Electrónica'), ('general', 'General / Normativa')], max_length=20)),
|
||||
('tipo', models.CharField(choices=[('diseno', 'Diseño / CAD'), ('simulacion', 'Simulación'), ('informe', 'Informe Técnico'), ('factura', 'Factura / Presupuesto'), ('otro', 'Otro')], default='informe', max_length=20)),
|
||||
('descripcion', models.TextField(blank=True, null=True, verbose_name='Descripción o notas')),
|
||||
('fecha_subida', models.DateTimeField(auto_now_add=True)),
|
||||
('subido_por', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='documentos_subidos', to=settings.AUTH_USER_MODEL)),
|
||||
('temporada', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='temporadas.temporada')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Documento Técnico',
|
||||
'verbose_name_plural': 'Documentos de Ingeniería',
|
||||
'ordering': ['-fecha_subida'],
|
||||
},
|
||||
),
|
||||
]
|
||||
0
Plataform_Web/documentos/migrations/__init__.py
Normal file
0
Plataform_Web/documentos/migrations/__init__.py
Normal file
Binary file not shown.
Binary file not shown.
59
Plataform_Web/documentos/models.py
Normal file
59
Plataform_Web/documentos/models.py
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
from django.db import models
|
||||
from users.models import CustomUser
|
||||
from temporadas.models import Temporada
|
||||
import os
|
||||
|
||||
class Documento(models.Model):
|
||||
# Opciones de categorías (puedes añadir más)
|
||||
CATEGORIAS = (
|
||||
('aerodinamica', 'Aerodinámica'),
|
||||
('chasis', 'Chasis'),
|
||||
('suspension', 'Suspensión'),
|
||||
('motor', 'Motor/Powertrain'),
|
||||
('electronica', 'Electrónica'),
|
||||
('general', 'General / Normativa'),
|
||||
('software', 'Software')
|
||||
)
|
||||
|
||||
TIPO_DOC = (
|
||||
('diseno', 'Diseño / CAD'),
|
||||
('simulacion', 'Simulación'),
|
||||
('informe', 'Informe Técnico'),
|
||||
('factura', 'Factura / Presupuesto'),
|
||||
('otro', 'Otro'),
|
||||
)
|
||||
|
||||
titulo = models.CharField(max_length=100, verbose_name="Título del documento")
|
||||
|
||||
# Aquí definimos dónde se guardan los archivos.
|
||||
# upload_to='ingenieria/' creará esa carpeta automáticamente.
|
||||
archivo = models.FileField(upload_to='ingenieria_docs/')
|
||||
|
||||
categoria = models.CharField(max_length=20, choices=CATEGORIAS)
|
||||
tipo = models.CharField(max_length=20, choices=TIPO_DOC, default='informe')
|
||||
|
||||
descripcion = models.TextField(blank=True, null=True, verbose_name="Descripción o notas")
|
||||
|
||||
# RELACIONES (La parte potente)
|
||||
# 1. Si borras una temporada, ¿borramos sus documentos? -> models.CASCADE (Sí)
|
||||
temporada = models.ForeignKey(Temporada, on_delete=models.CASCADE)
|
||||
|
||||
# 2. Si borras un usuario, ¿borramos sus docs? -> models.SET_NULL (No, mejor mantenemos el doc y ponemos usuario a null)
|
||||
subido_por = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True, related_name="documentos_subidos")
|
||||
|
||||
fecha_subida = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Documento Técnico"
|
||||
verbose_name_plural = "Documentos de Ingeniería"
|
||||
ordering = ['-fecha_subida']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.titulo} ({self.temporada})"
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
# Esto borra el archivo físico del disco duro cuando borras la entrada en la base de datos
|
||||
if self.archivo:
|
||||
if os.path.isfile(self.archivo.path):
|
||||
os.remove(self.archivo.path)
|
||||
super().delete(*args, **kwargs)
|
||||
3
Plataform_Web/documentos/tests.py
Normal file
3
Plataform_Web/documentos/tests.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
3
Plataform_Web/documentos/views.py
Normal file
3
Plataform_Web/documentos/views.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
0
Plataform_Web/gades_manager/__init__.py
Normal file
0
Plataform_Web/gades_manager/__init__.py
Normal file
BIN
Plataform_Web/gades_manager/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
Plataform_Web/gades_manager/__pycache__/__init__.cpython-314.pyc
Normal file
Binary file not shown.
BIN
Plataform_Web/gades_manager/__pycache__/settings.cpython-314.pyc
Normal file
BIN
Plataform_Web/gades_manager/__pycache__/settings.cpython-314.pyc
Normal file
Binary file not shown.
BIN
Plataform_Web/gades_manager/__pycache__/urls.cpython-314.pyc
Normal file
BIN
Plataform_Web/gades_manager/__pycache__/urls.cpython-314.pyc
Normal file
Binary file not shown.
BIN
Plataform_Web/gades_manager/__pycache__/wsgi.cpython-314.pyc
Normal file
BIN
Plataform_Web/gades_manager/__pycache__/wsgi.cpython-314.pyc
Normal file
Binary file not shown.
16
Plataform_Web/gades_manager/asgi.py
Normal file
16
Plataform_Web/gades_manager/asgi.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
"""
|
||||
ASGI config for gades_manager project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/6.0/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'gades_manager.settings')
|
||||
|
||||
application = get_asgi_application()
|
||||
130
Plataform_Web/gades_manager/settings.py
Normal file
130
Plataform_Web/gades_manager/settings.py
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
"""
|
||||
Django settings for gades_manager project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 6.0.2.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/6.0/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/6.0/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-mwlyo^0ax9u9ts5(hw(e&vlgmr$nbla-3az0d%s(lymmp5^)$-'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'users',
|
||||
'temporadas',
|
||||
'documentos',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'gades_manager.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'gades_manager.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/6.0/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'NAME': 'formula_gades_bd', # El nombre exacto que pusiste en Workbench
|
||||
'USER': 'root',
|
||||
'PASSWORD': 'admin1234', # ¡La que apuntaste en el papel!
|
||||
'HOST': 'localhost', # Significa "este ordenador"
|
||||
'PORT': '3306',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/6.0/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/6.0/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/6.0/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
|
||||
AUTH_USER_MODEL = 'users.CustomUser'
|
||||
|
||||
MEDIA_URL = '/media/' # La URL pública (ej: tudominio.com/media/archivo.pdf)
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # La carpeta física en tu ordenador
|
||||
35
Plataform_Web/gades_manager/urls.py
Normal file
35
Plataform_Web/gades_manager/urls.py
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
"""
|
||||
URL configuration for gades_manager project.
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/6.0/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
|
||||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
|
||||
# 1. IMPORTA TU VISTA NUEVA
|
||||
from gestion import views as gestion_views
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
|
||||
# 2. AÑADE ESTA RUTA (La cadena vacía '' significa "la página principal")
|
||||
path('', gestion_views.home, name='home'),
|
||||
]
|
||||
|
||||
# Configuración de archivos (esto déjalo como estaba)
|
||||
if settings.DEBUG:
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
16
Plataform_Web/gades_manager/wsgi.py
Normal file
16
Plataform_Web/gades_manager/wsgi.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
"""
|
||||
WSGI config for gades_manager project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/6.0/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'gades_manager.settings')
|
||||
|
||||
application = get_wsgi_application()
|
||||
0
Plataform_Web/gestion/__init__.py
Normal file
0
Plataform_Web/gestion/__init__.py
Normal file
BIN
Plataform_Web/gestion/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
Plataform_Web/gestion/__pycache__/__init__.cpython-314.pyc
Normal file
Binary file not shown.
BIN
Plataform_Web/gestion/__pycache__/views.cpython-314.pyc
Normal file
BIN
Plataform_Web/gestion/__pycache__/views.cpython-314.pyc
Normal file
Binary file not shown.
3
Plataform_Web/gestion/admin.py
Normal file
3
Plataform_Web/gestion/admin.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
5
Plataform_Web/gestion/apps.py
Normal file
5
Plataform_Web/gestion/apps.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class GestionConfig(AppConfig):
|
||||
name = 'gestion'
|
||||
0
Plataform_Web/gestion/migrations/__init__.py
Normal file
0
Plataform_Web/gestion/migrations/__init__.py
Normal file
3
Plataform_Web/gestion/models.py
Normal file
3
Plataform_Web/gestion/models.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
3
Plataform_Web/gestion/tests.py
Normal file
3
Plataform_Web/gestion/tests.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
6
Plataform_Web/gestion/views.py
Normal file
6
Plataform_Web/gestion/views.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
from django.shortcuts import render
|
||||
from django.http import HttpResponse
|
||||
|
||||
def home(request):
|
||||
# Esta función es la que decide qué mostrar cuando alguien entra a la web
|
||||
return HttpResponse("<h1>¡Bienvenido a la Plataforma de Gades! 🏎️💨</h1><p>Sistema de Telemetría y Gestión v1.0</p>")
|
||||
22
Plataform_Web/manage.py
Normal file
22
Plataform_Web/manage.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env python
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'gades_manager.settings')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
BIN
Plataform_Web/media/ingenieria_docs/PruebaTest1.png
Normal file
BIN
Plataform_Web/media/ingenieria_docs/PruebaTest1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 118 KiB |
0
Plataform_Web/temporadas/__init__.py
Normal file
0
Plataform_Web/temporadas/__init__.py
Normal file
BIN
Plataform_Web/temporadas/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
Plataform_Web/temporadas/__pycache__/__init__.cpython-314.pyc
Normal file
Binary file not shown.
BIN
Plataform_Web/temporadas/__pycache__/admin.cpython-314.pyc
Normal file
BIN
Plataform_Web/temporadas/__pycache__/admin.cpython-314.pyc
Normal file
Binary file not shown.
BIN
Plataform_Web/temporadas/__pycache__/apps.cpython-314.pyc
Normal file
BIN
Plataform_Web/temporadas/__pycache__/apps.cpython-314.pyc
Normal file
Binary file not shown.
BIN
Plataform_Web/temporadas/__pycache__/models.cpython-314.pyc
Normal file
BIN
Plataform_Web/temporadas/__pycache__/models.cpython-314.pyc
Normal file
Binary file not shown.
13
Plataform_Web/temporadas/admin.py
Normal file
13
Plataform_Web/temporadas/admin.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
from django.contrib import admin
|
||||
from .models import Temporada
|
||||
|
||||
@admin.register(Temporada)
|
||||
class TemporadaAdmin(admin.ModelAdmin):
|
||||
# Esto define qué columnas se ven en la lista
|
||||
list_display = ('nombre', 'fecha_inicio', 'fecha_fin', 'presupuesto', 'actual')
|
||||
|
||||
# Esto añade un filtro a la derecha para ver rápido cuál es la actual
|
||||
list_filter = ('actual',)
|
||||
|
||||
# Esto añade una barra de búsqueda por nombre
|
||||
search_fields = ('nombre',)
|
||||
5
Plataform_Web/temporadas/apps.py
Normal file
5
Plataform_Web/temporadas/apps.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class TemporadasConfig(AppConfig):
|
||||
name = 'temporadas'
|
||||
30
Plataform_Web/temporadas/migrations/0001_initial.py
Normal file
30
Plataform_Web/temporadas/migrations/0001_initial.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# Generated by Django 6.0.2 on 2026-02-17 11:28
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Temporada',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('nombre', models.CharField(max_length=50, unique=True)),
|
||||
('fecha_inicio', models.DateField()),
|
||||
('fecha_fin', models.DateField()),
|
||||
('presupuesto', models.DecimalField(decimal_places=2, default=0.0, max_digits=10)),
|
||||
('actual', models.BooleanField(default=False, verbose_name='¿Es la temporada actual?')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Temporada',
|
||||
'verbose_name_plural': 'Temporadas',
|
||||
'ordering': ['-fecha_inicio'],
|
||||
},
|
||||
),
|
||||
]
|
||||
0
Plataform_Web/temporadas/migrations/__init__.py
Normal file
0
Plataform_Web/temporadas/migrations/__init__.py
Normal file
Binary file not shown.
Binary file not shown.
30
Plataform_Web/temporadas/models.py
Normal file
30
Plataform_Web/temporadas/models.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
from django.db import models
|
||||
|
||||
class Temporada(models.Model):
|
||||
# El nombre será algo como "Gades 2024-25"
|
||||
nombre = models.CharField(max_length=50, unique=True)
|
||||
|
||||
fecha_inicio = models.DateField()
|
||||
fecha_fin = models.DateField()
|
||||
|
||||
# Presupuesto total para ese año
|
||||
presupuesto = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
|
||||
|
||||
# Checkbox para marcar cuál es la temporada que estamos viviendo ahora
|
||||
actual = models.BooleanField(default=False, verbose_name="¿Es la temporada actual?")
|
||||
|
||||
class Meta:
|
||||
# Esto es para que en el panel salga "Temporadas" y no "Temporadas"
|
||||
verbose_name = "Temporada"
|
||||
verbose_name_plural = "Temporadas"
|
||||
ordering = ['-fecha_inicio'] # Ordena las más nuevas primero
|
||||
|
||||
def __str__(self):
|
||||
return self.nombre
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# TRUCO PRO: Si marco esta temporada como "actual", desmarco todas las demás
|
||||
# Así evitamos que haya dos temporadas activas a la vez.
|
||||
if self.actual:
|
||||
Temporada.objects.filter(actual=True).exclude(pk=self.pk).update(actual=False)
|
||||
super().save(*args, **kwargs)
|
||||
3
Plataform_Web/temporadas/tests.py
Normal file
3
Plataform_Web/temporadas/tests.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
3
Plataform_Web/temporadas/views.py
Normal file
3
Plataform_Web/temporadas/views.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
0
Plataform_Web/users/__init__.py
Normal file
0
Plataform_Web/users/__init__.py
Normal file
BIN
Plataform_Web/users/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
Plataform_Web/users/__pycache__/__init__.cpython-314.pyc
Normal file
Binary file not shown.
BIN
Plataform_Web/users/__pycache__/admin.cpython-314.pyc
Normal file
BIN
Plataform_Web/users/__pycache__/admin.cpython-314.pyc
Normal file
Binary file not shown.
BIN
Plataform_Web/users/__pycache__/apps.cpython-314.pyc
Normal file
BIN
Plataform_Web/users/__pycache__/apps.cpython-314.pyc
Normal file
Binary file not shown.
BIN
Plataform_Web/users/__pycache__/models.cpython-314.pyc
Normal file
BIN
Plataform_Web/users/__pycache__/models.cpython-314.pyc
Normal file
Binary file not shown.
18
Plataform_Web/users/admin.py
Normal file
18
Plataform_Web/users/admin.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
from django.contrib import admin
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
from .models import CustomUser
|
||||
|
||||
class CustomUserAdmin(UserAdmin):
|
||||
model = CustomUser
|
||||
|
||||
# Esto añade una sección nueva al editar un usuario existente
|
||||
fieldsets = UserAdmin.fieldsets + (
|
||||
('Información del Equipo (TFG)', {'fields': ('rol', 'especialidad')}),
|
||||
)
|
||||
|
||||
# Esto permite añadir estos campos cuando creas un usuario nuevo desde el admin
|
||||
add_fieldsets = UserAdmin.add_fieldsets + (
|
||||
('Información del Equipo (TFG)', {'fields': ('rol', 'especialidad')}),
|
||||
)
|
||||
|
||||
admin.site.register(CustomUser, CustomUserAdmin)
|
||||
5
Plataform_Web/users/apps.py
Normal file
5
Plataform_Web/users/apps.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class UsersConfig(AppConfig):
|
||||
name = 'users'
|
||||
46
Plataform_Web/users/migrations/0001_initial.py
Normal file
46
Plataform_Web/users/migrations/0001_initial.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# Generated by Django 6.0.2 on 2026-02-17 10:36
|
||||
|
||||
import django.contrib.auth.models
|
||||
import django.contrib.auth.validators
|
||||
import django.utils.timezone
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('auth', '0012_alter_user_first_name_max_length'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CustomUser',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
||||
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
|
||||
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
||||
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
|
||||
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||
('rol', models.CharField(choices=[('directiva', 'Directiva'), ('jefe_area', 'Jefe de Área'), ('empleado', 'Empleado')], default='empleado', max_length=20)),
|
||||
('especialidad', models.CharField(blank=True, choices=[('aerodinamica', 'Aerodinámica'), ('chasis', 'Chasis'), ('business', 'Business & Operations'), ('epowertrain', 'E-Powertrain'), ('electronica', 'Electrónica'), ('sdf', 'SDF'), ('motor_transmision', 'Motor & Transmisión'), ('software', 'Software')], max_length=30, null=True)),
|
||||
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
|
||||
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'user',
|
||||
'verbose_name_plural': 'users',
|
||||
'abstract': False,
|
||||
},
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 6.0.2 on 2026-02-17 11:15
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='customuser',
|
||||
name='especialidad',
|
||||
field=models.CharField(blank=True, choices=[('aerodinamica', 'Aerodinámica'), ('chasis', 'Chasis'), ('business', 'Business & Operations'), ('epowertrain', 'E-Powertrain'), ('electronica', 'Electrónica'), ('sdf', 'SDF'), ('motor_transmision', 'Motor & Transmisión'), ('software', 'Software')], max_length=30, null=True, verbose_name='Área Técnica'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='customuser',
|
||||
name='rol',
|
||||
field=models.CharField(choices=[('directiva', 'Directiva'), ('jefe_area', 'Jefe de Área'), ('empleado', 'Empleado')], default='empleado', max_length=20, verbose_name='Rol en el equipo'),
|
||||
),
|
||||
]
|
||||
0
Plataform_Web/users/migrations/__init__.py
Normal file
0
Plataform_Web/users/migrations/__init__.py
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
43
Plataform_Web/users/models.py
Normal file
43
Plataform_Web/users/models.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
from django.contrib.auth.models import AbstractUser
|
||||
from django.db import models
|
||||
|
||||
class CustomUser(AbstractUser):
|
||||
# --- 1. OPCIONES (El Menú) ---
|
||||
ROL_CHOICES = (
|
||||
('directiva', 'Directiva'),
|
||||
('jefe_area', 'Jefe de Área'),
|
||||
('empleado', 'Empleado'),
|
||||
)
|
||||
|
||||
ESPECIALIDAD_CHOICES = (
|
||||
('aerodinamica', 'Aerodinámica'),
|
||||
('chasis', 'Chasis'),
|
||||
('business', 'Business & Operations'),
|
||||
('epowertrain', 'E-Powertrain'),
|
||||
('electronica', 'Electrónica'),
|
||||
('sdf', 'SDF'),
|
||||
('motor_transmision', 'Motor & Transmisión'),
|
||||
('software', 'Software'),
|
||||
)
|
||||
|
||||
# --- 2. CAMPOS (Las Columnas en la BD) ---
|
||||
# Usamos la versión con 'verbose_name' porque queda mejor en la web
|
||||
rol = models.CharField(
|
||||
max_length=20,
|
||||
choices=ROL_CHOICES,
|
||||
default='empleado',
|
||||
verbose_name="Rol en el equipo"
|
||||
)
|
||||
|
||||
especialidad = models.CharField(
|
||||
max_length=30,
|
||||
choices=ESPECIALIDAD_CHOICES,
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name="Área Técnica"
|
||||
)
|
||||
|
||||
# --- 3. MÉTODOS ---
|
||||
def __str__(self):
|
||||
# Muestra: "Nombre Apellido (usuario) - Rol"
|
||||
return f"{self.first_name} {self.last_name} ({self.username}) - {self.get_rol_display()}"
|
||||
3
Plataform_Web/users/tests.py
Normal file
3
Plataform_Web/users/tests.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
3
Plataform_Web/users/views.py
Normal file
3
Plataform_Web/users/views.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
Loading…
Add table
Add a link
Reference in a new issue