Files
andaquenosabesna/make_guest_form.py
T
AlexandClaude Sonnet 5.5 da8cc01fad Ficha privada de invitados y condiciones de participación
- /invitados.html (noindex) con enlace personal ?k=<código>; las respuestas
  llegan por correo desde contacto@ en lugar de Google Forms.
- server.py: /api/invitados/check y /api/invitados (códigos en INVITE_TOKENS).
- terminos.html: sección 5 con las condiciones de participación.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSyR5Zw5eGj6oPTRVjvP1b
2026-10-20 23:50:53 +02:00

89 lines
4.4 KiB
Python

"""Genera public/invitados.html a partir de guest_form.json (copia de las preguntas del formulario de Google)."""
import html, json
esc = html.escape
F = json.load(open('guest_form.json', encoding='utf-8'))
secciones, actual, nq = [], None, 0
consent = None
for it in F['items']:
t = it['type']
if t == 8:
actual = {'titulo': it['title'], 'nota': it['desc'], 'preguntas': []}
secciones.append(actual)
elif t in (0, 1):
nq += 1
actual['preguntas'].append({'id': f'q{nq:02d}', 'titulo': it['title'], 'desc': it['desc'],
'req': it['required'], 'largo': t == 1})
elif t == 4:
consent = {'titulo': it['title'], 'opcion': it['options'][0], 'req': it['required']}
consent_text = secciones[-1]['nota']
secciones.pop() # la última sección solo contiene las condiciones
def pregunta(q):
mark = ' <span class="req" title="Obligatoria">*</span>' if q['req'] else ''
campo = (f'<textarea name="{q["id"]}" rows="4" maxlength="3000"{" required" if q["req"] else ""}></textarea>'
if q['largo'] else
f'<input name="{q["id"]}" type="text" maxlength="300"{" required" if q["req"] else ""}>')
d = f'<small>{esc(q["desc"])}</small>' if q['desc'] else ''
return f' <label>{esc(q["titulo"])}{mark}{d}\n {campo}\n </label>'
bloques = []
for s in secciones:
nota = f'\n <p class="note">{esc(s["nota"])}</p>' if s['nota'] else ''
bloques.append(f' <fieldset class="sec">\n <legend>{esc(s["titulo"])}</legend>{nota}\n' +
'\n'.join(pregunta(q) for q in s['preguntas']) + '\n </fieldset>')
page = f'''<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow, noarchive">
<title>Ficha del invitado · Anda Que No Sabes Ná</title>
<meta name="theme-color" content="#06080d">
<link rel="icon" href="img/avatar.jpg">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header class="top">
<a class="brand" href="/" aria-label="Anda Que No Sabes Ná, inicio"><img src="img/avatar.jpg" alt="" width="40" height="40"><span>Anda que no sabes ná</span></a>
</header>
<main class="legal contact guest">
<h1>Ficha del <em>invitado</em></h1>
<p id="hola" class="lead-c" hidden></p>
<p id="malo" class="status err" hidden>Este enlace no es válido o ha caducado. Escríbenos a <a href="mailto:andaquenosabesnada@gmail.com">andaquenosabesnada@gmail.com</a> y te mandamos uno nuevo.</p>
<p id="intro" class="lead-c" hidden>{esc(F['description'])}</p>
<form id="g" class="form" novalidate hidden data-ok="¡Gracias! Hemos recibido tu ficha. Te escribiremos pronto con los siguientes pasos.">
<label>Tu correo electrónico (opcional, para poder responderte)
<input name="email" type="email" autocomplete="email" maxlength="200">
</label>
{chr(10).join(bloques)}
<fieldset class="sec">
<legend>Antes de enviar</legend>
<p class="note">Este podcast se realiza de forma altruista y colaborativa. Antes de enviar, lee las <a href="terminos.html#participacion" target="_blank" rel="noopener">condiciones de participación</a>.</p>
<div class="trap" aria-hidden="true"><label>No rellenes este campo <input name="web" type="text" tabindex="-1" autocomplete="off"></label></div>
<label class="check">
<input name="consent" type="checkbox" required>
<span>He leído y acepto las <a href="terminos.html#participacion" target="_blank" rel="noopener">condiciones de participación</a> descritas. <span class="req">*</span></span>
</label>
<label class="check">
<input name="privacy" type="checkbox" required>
<span>He leído la <a href="privacidad.html" target="_blank" rel="noopener">política de privacidad</a> y acepto que usen estos datos para preparar el episodio. <span class="req">*</span></span>
</label>
</fieldset>
<button class="btn primary" type="submit">Enviar mi ficha</button>
<p id="st" class="status" role="status" aria-live="polite"></p>
<p class="note">Tus respuestas se guardan en este navegador mientras escribes, por si cierras la página. Se borran al enviarlas.</p>
</form>
</main>
<script src="invitados.js"></script>
</body>
</html>
'''
open('public/invitados.html', 'w', encoding='utf-8').write(page)
print('invitados.html:', len(page), 'bytes;', nq, 'preguntas;', len(secciones), 'secciones')