Prompt per generar aplicació en Python

Hem utilizat Python perquè es més fácil amb flask per fer la web dinàmica

  • Guardar informació: Com guardar noms d’usuaris guardats a la BBDD de Mysql.
  • Registratse: És fàcil afegir noves funcions sense haver de tornar a començar de zero.

Aquest es el codig que hem utilitzat per fer el formulari:

from http.server import HTTPServer, BaseHTTPRequestHandler

import sqlite3

import json

from datetime import datetime

# — CONFIGURACIÓ DE LA BASE DE DADES —

def inicialitzar_db():

conexion = sqlite3.connect(“usuarios.db”)

cursor = conexion.cursor()

cursor.execute(“””

CREATE TABLE IF NOT EXISTS usuarios (

id INTEGER PRIMARY KEY AUTOINCREMENT,

nombre TEXT NOT NULL,

apellido TEXT NOT NULL,

email TEXT NOT NULL,

instituto TEXT,

hora TEXT,

acompanantes INTEGER,

fecha_registro TEXT,

UNIQUE(nombre, apellido)

)

“””)

conexion.commit()

conexion.close()

def obtenir_estat_cupos():

franges = [“16:00”, “16:15”, “16:30”, “16:45”, “17:00”, “17:15”, “17:30”, “17:45”, “18:00”, “18:15”, “18:30”, “18:45”]

disponibles = {}

conexion = sqlite3.connect(“usuarios.db”)

cursor = conexion.cursor()

for f in franges:

hora_pare = f.split(“:”)[0]

cursor.execute(“SELECT SUM(acompanantes) FROM usuarios WHERE hora LIKE ?”, (f”{hora_pare}:%”,))

total_hora = cursor.fetchone()[0] or 0

cupos_restants = 20 – total_hora

disponibles[f] = {“lliures”: cupos_restants, “rang”: f”{hora_pare}:00″}

conexion.close()

return disponibles

class ServidorSMX(BaseHTTPRequestHandler):

def do_GET(self):

if self.path == “/admin”:

# — SISTEMA DE CONTRASEÑA —

auth_header = self.headers.get(‘Authorization’)

# El usuario es ‘admin’ y la contraseña es ‘ICastellbisbal’

# En Basic Auth, esto se envía codificado en Base64 como ‘admin:ICastellbisbal’

# Cadena Base64 resultante: YWRtaW46S thereICastellbisbal

password_correcta = “ICastellbisbal”

if auth_header is None or auth_header != password_correcta:

self.send_response(401)

self.send_header(‘WWW-Authenticate’, ‘Basic realm=”Acces Administrador”‘)

self.send_header(‘Content-type’, ‘text/html’)

self.end_headers()

self.wfile.write(b”No autoritzat”)

return

# — FIN SISTEMA DE CONTRASEÑA —

self.send_response(200)

self.send_header(“Content-type”, “text/html; charset=utf-8”)

self.end_headers()

conexion = sqlite3.connect(“usuarios.db”)

cursor = conexion.cursor()

cursor.execute(“SELECT nombre, apellido, instituto, email, hora, acompanantes FROM usuarios ORDER BY hora ASC”)

files = cursor.fetchall()

conexion.close()

total_inscrits = sum(f[5] for f in files)

tabla_html = “”.join([f”<tr><td>{f[0].capitalize()}</td><td>{f[1].capitalize()}</td><td>{f[2] or ‘-‘}</td><td>{f[3]}</td><td>{f[4]}</td><td>{f[5]}</td></tr>” for f in files])

html_admin = f”””<html><head><meta charset=’UTF-8′><title>Panell d’Administració</title>

<style>body{{font-family:’Segoe UI’,sans-serif;background:#0f172a;color:white;padding:40px;}}

table{{width:100%;border-collapse:collapse;background:#1e293b;border-radius:12px;overflow:hidden;}}

th,td{{padding:15px;text-align:left;border-bottom:1px solid #334155;}}

th{{background:#4f46e5;color:white;text-transform:uppercase;font-size:12px;letter-spacing:1px;}}

tr:hover{{background:#334155;}}</style></head>

<body><h1>Llistat de Registres</h1><p>Total persones inscrites: {total_inscrits}</p>

<table><thead><tr><th>Nom</th><th>Cognom</th><th>Institut</th><th>Email</th><th>Torn</th><th>Persones</th></tr></thead>

<tbody>{tabla_html}</tbody></table></body></html>”””

self.wfile.write(html_admin.encode(“utf-8”))

return

# Resto del código para la página principal…

self.send_response(200)

# … (aquí sigue el código del formulario que ya tenías)

self.send_response(200)

self.send_header(“Content-type”, “text/html; charset=utf-8”)

self.end_headers()

cupos = obtenir_estat_cupos()

grid_html = “”

for h, i in cupos.items():

clase = “disponible” if i[‘lliures’] > 0 else “esgotat”

disabled = “disabled” if i[‘lliures’] <= 0 else “”

grid_html += f”””

<label class=”radio-card {clase}”>

<input type=”radio” name=”hora” value=”{h}” required {disabled}>

<div class=”card-content”>

<span class=”franja-tag”>Franja {i[‘rang’]}h</span>

<span class=”hora-txt”>{h}</span>

<span class=”libres-txt”>{i[‘lliures’]} lliures</span>

</div>

</label>

“””

html = f”””

<!DOCTYPE html>

<html lang=”ca”>

<head>

<meta charset=”UTF-8″>

<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>

<title>Registre de l’Horari</title>

<link href=”https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600;800&display=swap” rel=”stylesheet”>

<style>

:root {{ –primary: #6366f1; –primary-hover: #4f46e5; –bg: #0f172a; }}

body, html {{ height: 100%; margin: 0; font-family: ‘Plus Jakarta Sans’, sans-serif; background: var(–bg); color: #1e293b; }}

.bg-image {{ position: fixed; width: 100%; height: 100%; z-index: -1; background: radial-gradient(circle at top right, #4f46e5, transparent), url(‘https://images.unsplash.com/photo-1451187580459-43490279c0fa?q=80&w=2072&auto=format&fit=crop’); background-size: cover; background-position: center; filter: brightness(0.6); }}

.content-wrapper {{ display: flex; justify-content: center; align-items: center; min-height: 100vh; padding: 20px; box-sizing: border-box; }}

.tarjeta {{ background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(12px); padding: 40px; border-radius: 24px; width: 100%; max-width: 550px; box-shadow: 0 25px 50px -12px rgba(0,0,0,0.5); border: 1px solid rgba(255,255,255,0.3); }}

h2 {{ text-align: center; font-weight: 800; font-size: 28px; margin-bottom: 8px; color: #0f172a; }}

p.subtitle {{ text-align: center; color: #64748b; margin-bottom: 30px; font-size: 14px; }}

.form-group {{ margin-bottom: 16px; }}

.form-label {{ display: block; font-weight: 600; font-size: 13px; margin-bottom: 6px; color: #334155; }}

input {{ width: 100%; padding: 12px 16px; border-radius: 12px; border: 1px solid #e2e8f0; background: white; box-sizing: border-box; transition: 0.2s; font-size: 15px; }}

input:focus {{ outline: none; border-color: var(–primary); box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1); }}

.grid-horas {{ display: grid; grid-template-columns: repeat(auto-fill, minmax(105px, 1fr)); gap: 12px; margin-top: 10px; }}

.radio-card {{ cursor: pointer; position: relative; }}

.radio-card input {{ position: absolute; opacity: 0; }}

.card-content {{ border: 2px solid #f1f5f9; border-radius: 16px; padding: 12px; text-align: center; background: white; transition: all 0.3s ease; }}

.franja-tag {{ font-size: 10px; color: #94a3b8; font-weight: 600; display: block; margin-bottom: 4px; }}

.hora-txt {{ font-weight: 800; font-size: 16px; color: #1e293b; display: block; }}

.libres-txt {{ font-size: 11px; color: #10b981; font-weight: 700; margin-top: 4px; display: block; }}

.radio-card:hover:not(.esgotat) .card-content {{ border-color: #cbd5e1; transform: translateY(-2px); }}

.radio-card input:checked + .card-content {{ border-color: var(–primary); background: #f5f3ff; box-shadow: 0 10px 15px -3px rgba(99, 102, 241, 0.2); }}

.esgotat {{ opacity: 0.5; cursor: not-allowed; filter: grayscale(1); }}

.btn {{ width: 100%; padding: 16px; background: var(–primary); color: white; border: none; border-radius: 16px; margin-top: 24px; cursor: pointer; font-weight: 700; font-size: 16px; transition: 0.3s; box-shadow: 0 10px 15px -3px rgba(79, 70, 229, 0.3); }}

.btn:hover {{ background: var(–primary-hover); transform: translateY(-1px); }}

.btn-alt {{ background: #f1f5f9; color: #475569; box-shadow: none; margin-top: 12px; }}

.resum-item {{ background: #f8fafc; padding: 14px; border-radius: 12px; margin-bottom: 10px; display: flex; justify-content: space-between; align-items: center; border: 1px solid #e2e8f0; }}

.resum-label {{ color: #64748b; font-size: 13px; font-weight: 600; }}

.resum-val {{ color: #0f172a; font-weight: 700; }}

.hidden {{ display: none; }}

</style>

</head>

<body>

<div class=”bg-image”></div>

<div class=”content-wrapper”>

<div class=”tarjeta” id=”box-form”>

<h2>Registre de l’Horari</h2>

<p class=”subtitle”>Reserva la teva plaça</p>

<form id=”mainForm” onsubmit=”mostrarResum(event)”>

<div style=”display: flex; gap: 15px;”>

<div class=”form-group” style=”flex:1;”>

<label class=”form-label”>Nom</label>

<input type=”text” id=”nom” placeholder=”Ex. Joan” required>

</div>

<div class=”form-group” style=”flex:1;”>

<label class=”form-label”>Cognom</label>

<input type=”text” id=”cog” placeholder=”Ex. Vila” required>

</div>

</div>

<div class=”form-group”>

<label class=”form-label”>Correu Electrònic</label>

<input type=”email” id=”email” placeholder=”correu@exemple.com” required>

</div>

<div class=”form-group”>

<label class=”form-label”>Institut / Centre Educatiu</label>

<input type=”text” id=”insti” placeholder=”Nom del teu centre (opcional)”>

</div>

<div class=”form-group”>

<label class=”form-label”>Nombre de persones (Màx. 4)</label>

<input type=”number” id=”gent” min=”1″ max=”4″ value=”1″ required>

</div>

<label class=”form-label”>Selecciona el teu torn</label>

<div class=”grid-horas”>{grid_html}</div>

<button type=”submit” class=”btn”>CONTINUAR</button>

</form>

</div>

<div class=”tarjeta hidden” id=”box-confirm”>

<h2>Verifica la reserva</h2>

<p class=”subtitle”>Comprova que les dades siguin correctes</p>

<div id=”resum-content” style=”margin-top: 20px;”></div>

<button onclick=”enviarDades()” class=”btn”>CONFIRMAR REGISTRE</button>

<button onclick=”tornarEnrera()” class=”btn btn-alt”>MODIFICAR DADES</button>

</div>

</div>

<script>

let dadesTemporals = {{}};

function mostrarResum(e) {{

e.preventDefault();

const horaSel = document.querySelector(‘input[name=”hora”]:checked’);

const numGent = parseInt(document.getElementById(‘gent’).value);

if(numGent > 4) {{ alert(“El màxim permès són 4 persones.”); return; }}

if(!horaSel) {{ alert(“Si us plau, selecciona un torn.”); return; }}

dadesTemporals = {{

nom: document.getElementById(‘nom’).value,

cog: document.getElementById(‘cog’).value,

email: document.getElementById(‘email’).value,

insti: document.getElementById(‘insti’).value || “No indicat”,

gent: numGent,

hora: horaSel.value

}};

document.getElementById(‘resum-content’).innerHTML = `

<div class=”resum-item”><span class=”resum-label”>Nom complet</span><span class=”resum-val”>${{dadesTemporals.nom}} ${{dadesTemporals.cog}}</span></div>

<div class=”resum-item”><span class=”resum-label”>Institut</span><span class=”resum-val”>${{dadesTemporals.insti}}</span></div>

<div class=”resum-item”><span class=”resum-label”>Assistents</span><span class=”resum-val”>${{dadesTemporals.gent}}</span></div>

<div class=”resum-item” style=”border: 2px solid var(–primary);”><span class=”resum-label”>Hora triada</span><span class=”resum-val” style=”color:var(–primary);”>${{dadesTemporals.hora}}h</span></div>

`;

document.getElementById(‘box-form’).classList.add(‘hidden’);

document.getElementById(‘box-confirm’).classList.remove(‘hidden’);

}}

function tornarEnrera() {{

document.getElementById(‘box-confirm’).classList.add(‘hidden’);

document.getElementById(‘box-form’).classList.remove(‘hidden’);

}}

async function enviarDades() {{

try {{

const r = await fetch(‘/’, {{method:’POST’, body:JSON.stringify(dadesTemporals)}});

const res = await r.json();

if(res.ok) {{

alert(‘Registre realitzat amb èxit! T’esperem.’);

location.reload();

}} else {{

alert(res.msg);

tornarEnrera();

}}

}} catch(e) {{

alert(“Error de connexió amb el servidor.”);

}}

}}

</script>

</body>

</html>

“””

self.wfile.write(html.encode(“utf-8”))

def do_POST(self):

content_length = int(self.headers[‘Content-Length’])

data = json.loads(self.rfile.read(content_length).decode(‘utf-8’))

# Validació de seguretat al servidor

if data[‘gent’] > 4:

resp = {“ok”: False, “msg”: “Error: El màxim de persones és 4.”}

else:

conexion = sqlite3.connect(“usuarios.db”)

cursor = conexion.cursor()

hora_pare = data[‘hora’].split(“:”)[0]

cursor.execute(“SELECT SUM(acompanantes) FROM usuarios WHERE hora LIKE ?”, (f”{hora_pare}:%”,))

actual = cursor.fetchone()[0] or 0

lliures = 20 – actual

if data[‘gent’] > lliures:

resp = {“ok”: False, “msg”: f”Ho sentim, només queden {max(0, lliures)} places lliures.”}

else:

try:

cursor.execute(“INSERT INTO usuarios (nombre, apellido, email, instituto, hora, acompanantes, fecha_registro) VALUES (?, ?, ?, ?, ?, ?, ?)”,

(data[‘nom’].lower(), data[‘cog’].lower(), data[‘email’], data[‘insti’], data[‘hora’], data[‘gent’], datetime.now().isoformat()))

conexion.commit()

resp = {“ok”: True}

except:

resp = {“ok”: False, “msg”: “Aquest nom i cognom ja estan registrats.”}

conexion.close()

self.send_response(200); self.send_header(‘Content-Type’, ‘application/json’); self.end_headers()

self.wfile.write(json.dumps(resp).encode(‘utf-8’))

if __name__ == “__main__”:

inicialitzar_db()

server = HTTPServer((“0.0.0.0”, 5000), ServidorSMX)

server.allow_reuse_address = True

print(“🚀 Servidor en català actiu a http://localhost:5000”)

server.serve_forever()

Basicament, son lineas per importar les llibreries de Mysql, també de tot el que es veu a la web: Nom, centre, hora… i pero últim, en que servidor anirá.