diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..4ae3ba7
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,5 @@
+FROM python:3.11-slim
+WORKDIR /app
+RUN pip install --no-cache-dir flask
+EXPOSE 8080
+CMD ["python", "app.py"]
diff --git a/app.py b/app.py
new file mode 100644
index 0000000..3f2f61f
--- /dev/null
+++ b/app.py
@@ -0,0 +1,212 @@
+import os
+import configparser
+from flask import Flask, request, render_template_string, redirect, url_for
+
+app = Flask(__name__)
+DATA_DIR = "/data"
+
+def get_poll_files():
+ if not os.path.exists(DATA_DIR):
+ return []
+ return [f for f in os.listdir(DATA_DIR) if f.endswith('.encuesta')]
+
+def load_poll(filename):
+ filepath = os.path.join(DATA_DIR, filename)
+ if not os.path.exists(filepath):
+ return None
+
+ config = configparser.ConfigParser()
+ config.read(filepath, encoding='utf-8')
+
+ poll_id = filename.replace('.encuesta', '')
+ titulo = config.get('encuesta', 'titulo', fallback=poll_id)
+ pregunta = config.get('encuesta', 'pregunta', fallback='')
+
+ # Leer el estado de la encuesta (por defecto True si no existe el campo)
+ activa_raw = config.get('encuesta', 'activa', fallback='true').lower()
+ activa = activa_raw in ['true', '1', 'yes', 'si']
+
+ options = []
+ votes = []
+ if 'opciones' in config and 'votos' in config:
+ for key in config['opciones']:
+ options.append(config['opciones'][key])
+ votes.append(int(config['votos'].get(key, '0')))
+
+ return {
+ "id": poll_id,
+ "filename": filename,
+ "titulo": titulo,
+ "pregunta": pregunta,
+ "activa": activa,
+ "options": options,
+ "votes": votes
+ }
+
+def save_vote(filename, option_index):
+ filepath = os.path.join(DATA_DIR, filename)
+ config = configparser.ConfigParser()
+ config.read(filepath, encoding='utf-8')
+
+ # Seguridad: verificar si sigue activa antes de guardar
+ activa_raw = config.get('encuesta', 'activa', fallback='true').lower()
+ if activa_raw not in ['true', '1', 'yes', 'si']:
+ return False
+
+ key = str(option_index + 1)
+ current_votes = int(config.get('votos', key, fallback='0'))
+ config.set('votos', key, str(current_votes + 1))
+
+ with open(filepath, 'w', encoding='utf-8') as f:
+ config.write(f)
+ return True
+
+HTML_LAYOUT = """
+
+
+
+
+
+ Encuestas
+
+
+
+
+ {{ content | safe }}
+
+
+
+"""
+
+@app.route('/')
+def home():
+ files = get_poll_files()
+ active_polls = []
+ closed_polls = []
+
+ for f in files:
+ poll = load_poll(f)
+ if poll:
+ if poll["activa"]:
+ active_polls.append(poll)
+ else:
+ closed_polls.append(poll)
+
+ content = "SoloConLinux: Panel de Encuestas
"
+
+ # BLOQUE: Encuestas Activas
+ content += 'Encuestas Activas
'
+ if not active_polls:
+ content += "No hay encuestas activas en este momento.
"
+ else:
+ for poll in active_polls:
+ content += f'''
+
+ '''
+
+ # BLOQUE: Encuestas Finalizadas
+ content += 'Encuestas Finalizadas
'
+ if not closed_polls:
+ content += "No hay encuestas finalizadas.
"
+ else:
+ for poll in closed_polls:
+ content += f'''
+
+
FINALIZADA
+
{poll["titulo"]}
+
{poll["pregunta"]}
+
Ver Resultados
+
+ '''
+
+ return render_template_string(HTML_LAYOUT, content=content)
+
+@app.route('/poll/')
+def show_poll(poll_id):
+ filename = f"{poll_id}.encuesta"
+ poll = load_poll(filename)
+ if not poll:
+ return "Encuesta no encontrada", 404
+
+ # Si la encuesta NO está activa, forzar siempre la vista de resultados
+ voted = (request.args.get('voted') == '1') or (not poll["activa"])
+
+ content = f'{poll["pregunta"]}
'
+
+ if not poll["activa"]:
+ content += '⚠️ Esta encuesta ha finalizado. Solo se pueden consultar los resultados.
'
+
+ if voted:
+ total = sum(poll["votes"])
+ content += "Resultados:
"
+ bar_class = "bar" if poll["activa"] else "bar bar-closed"
+
+ for i, opt in enumerate(poll["options"]):
+ count = poll["votes"][i]
+ percent = round((count / total * 100), 1) if total > 0 else 0
+ content += f'''
+
+
{opt}
+
+
+
{count} votos ({percent}%)
+
+
+ '''
+ content += f'Total de votos: {total}
'
+ content += f'Volver al listado'
+ else:
+ content += f''
+
+ return render_template_string(HTML_LAYOUT, content=content)
+
+@app.route('/vote/', methods=['POST'])
+def vote(poll_id):
+ filename = f"{poll_id}.encuesta"
+ poll = load_poll(filename)
+
+ # Si la encuesta está desactivada, ignorar el intento de voto
+ if not poll or not poll["activa"]:
+ return redirect(url_for('show_poll', poll_id=poll_id))
+
+ idx = int(request.form.get('option'))
+ save_vote(filename, idx)
+ return redirect(url_for('show_poll', poll_id=poll_id, voted=1))
+
+if __name__ == '__main__':
+ app.run(host='0.0.0.0', port=8080)
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..496523c
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+docker build -t encuestas .
diff --git a/encuestas/01-Publicaciones_Soloconlinux.encuesta b/encuestas/01-Publicaciones_Soloconlinux.encuesta
new file mode 100644
index 0000000..957f738
--- /dev/null
+++ b/encuestas/01-Publicaciones_Soloconlinux.encuesta
@@ -0,0 +1,17 @@
+[encuesta]
+titulo = Publicaciones en SoloConLinux
+pregunta = ¿Qué opináis sobre crear este tipo de publicación en SoloConLinux?
+activa = true
+
+[opciones]
+1 = Es algo interesante
+2 = Interesante pero mejor 2 ediciones anuales
+3 = No tiene sentido hoy en día
+4 = Prefiero directamente el Blog de soloconlinux.org.es
+
+[votos]
+1 = 3
+2 = 1
+3 = 0
+4 = 1
+
diff --git a/encuestas/02-Eleccion servidor.encuesta b/encuestas/02-Eleccion servidor.encuesta
new file mode 100644
index 0000000..4674956
--- /dev/null
+++ b/encuestas/02-Eleccion servidor.encuesta
@@ -0,0 +1,19 @@
+[encuesta]
+titulo = Elección de distribución en servidores
+pregunta = ¿Qué distribución de Linux prefieres utilizar para desplegar servicios en producción?
+activa = false
+
+[opciones]
+1 = Debian (Estable y puro)
+2 = Ubuntu Server (Soporte extendido y ecosistema)
+3 = AlmaLinux / Rocky Linux (Entorno empresarial tipo RHEL)
+4 = Alpine Linux (Imágenes ultra ligeras para contenedores)
+5 = Arch Linux (Para estar siempre a la última)
+
+[votos]
+1 = 1324
+2 = 340
+3 = 564
+4 = 340
+5 = 450
+
diff --git a/plantilla.encuesta b/plantilla.encuesta
new file mode 100644
index 0000000..ac7c1fc
--- /dev/null
+++ b/plantilla.encuesta
@@ -0,0 +1,20 @@
+[encuesta]
+titulo = Titulo de la Encuesta
+pregunta = ¿Qué bla bla bla bla prefieres?
+# activa = true|false
+activa = true
+
+[opciones]
+# n = texto de la opcion
+1 = Texto de la opcion 1
+2 = Texto de la opcion 2
+3 = Texto de la opcion 3
+4 = Texto de la opcion 4
+
+[votos]
+# inicializar con valor 0
+1 = 0
+2 = 0
+3 = 0
+4 = 0
+
diff --git a/run.sh b/run.sh
new file mode 100755
index 0000000..0930e07
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+docker rm -f encuestas 2> /dev/null
+
+docker run -d \
+ --name encuestas \
+ --restart always \
+ -p 8080:8080 \
+ -v ${PWD}/app.py:/app/app.py \
+ -v ${PWD}/encuestas:/data \
+ encuestas
+
+echo "Abra el Gestor de Encuestas en: http://localhost:8080"
diff --git a/stop.sh b/stop.sh
new file mode 100755
index 0000000..882109a
--- /dev/null
+++ b/stop.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+echo "Deteniendo contenedor de Encuestas..."
+docker rm -f encuestas 2> /dev/null
+echo "¡Detenido!"
+echo