Lógica y automatización
Escribir el script Python que resume el pipeline comercial, ejecutarlo a mano y programarlo para todas las mañanas.
La app ya muestra datos y deja editarlos. Falta la parte que trabaja sola: un script que, todas las mañanas, mira el pipeline, cuenta lo que está abierto y registra los cierres previstos para la semana.
En esta etapa escribes ese script en Python, lo ejecutas a mano para ver el resultado, y le marcas una hora — todos los días a las 07:00.
Qué va a hacer el script
El atualizar_indicadores responde a tres preguntas, y las devuelve en
un resultado que queda en el historial:
| Pregunta | Qué devuelve |
|---|---|
| ¿Cuántas oportunidades están abiertas? | oportunidades_abertas |
| ¿Cuánto vale el pipeline? | valor_pipeline |
| ¿Cuántos cierres están previstos para los próximos 7 días? | fechos_proximos_7_dias |
Además del resultado, escribe logs — una línea por cierre de la semana — para que quien abra el historial entienda, sin cuentas, qué estaba marcado aquel día.
Crear el script
- Elige el panel Código en la barra lateral.
- En la fila Scripts, haz clic en el + (Nuevo script). Se abre el diálogo Nuevo script — «Elige el runtime y crea — el código se edita a continuación, con el contrato a la vista.»
- En Nombre, escribe
atualizar_indicadores. El nombre identifica el script en todas partes — en las programaciones, en los pasos de API, en las dependencias de otros scripts. - Runtime está fijo en Python.
- En Descripción, escribe
Recalcula los indicadores comerciales y avisa cuando hay cierres para esta semana. - En Tiempo límite, deja 1 minuto. Es el techo de la ejecución: pasado ese tiempo, la plataforma corta.
- Haz clic en Crear script. El editor se abre con el
main.pylisto.

Consejo
Un tiempo límite generoso no es amabilidad: si este script se usa como paso de una API, los clientes esperan ese tiempo en el peor caso. Un minuto llega y sobra para lo que vamos a hacer.
Escribir el main.py
El contrato de un script es corto: una función main(input) que devuelve
algo. Lo que devuelves queda en el historial de ejecuciones y, si el
script es llamado por una API, es su respuesta.
Escribe esto en el editor:
from datetime import date, timedelta
from api_manager import db, log
def main(input):
"""Recalcula los indicadores del panel comercial.
Corre todos los días a las 7h00 (programación "Indicadores diários") y
devuelve el resumen — el historial de ejecuciones queda con un registro
legible por día.
"""
crm = db("Dados CRM")
abertas = crm.query(
"select count(*) as n, coalesce(sum(valor), 0) as total "
"from oportunidades where fase not in ('fechada_ganha', 'fechada_perdida')"
)[0]
limite = (date.today() + timedelta(days=7)).isoformat()
fechos_semana = crm.query(
"select titulo, data_fecho from oportunidades "
"where fase not in ('fechada_ganha', 'fechada_perdida') "
"and data_fecho <= ? order by data_fecho",
[limite],
)
log("pipeline:", abertas["n"], "oportunidades /", abertas["total"], "EUR")
for op in fechos_semana:
log("fecho esta semana:", op["titulo"], "(", op["data_fecho"], ")")
return {
"oportunidades_abertas": abertas["n"],
"valor_pipeline": abertas["total"],
"fechos_proximos_7_dias": len(fechos_semana),
}

Cuatro cosas para retener de este código:
| Línea | Qué hace |
|---|---|
from api_manager import db, log |
El acceso de la plataforma: db abre bases de datos, log escribe en el historial. |
db("Dados CRM") |
La base de datos por el nombre interno del datasource — el mismo que registraste en la etapa del modelo. Cambia el nombre allí y esta línea deja de funcionar. |
crm.query(sql, [valores]) |
Consulta parametrizada. Los valores van siempre aparte de la query — nunca pegados al texto. |
return { … } |
El resultado de la ejecución. Queda en el historial y es lo que una API devolvería. |
El input que la función recibe trae los argumentos de la ejecución —
los de una API, los de una programación o los que escribas a mano a
continuación. Aquí no usamos ninguno.
Nota
No hay botón de guardar: el editor guarda solo. El interruptor Activo en la esquina superior derecha es otra cosa — un script inactivo sigue existiendo pero no corre, ni a mano ni por programación.
Ejecutar el script a mano
- En la parte superior del editor, haz clic en Ejecutar ahora.
- Se abre el diálogo — «Define los argumentos de esta ejecución (opcional). Los valores llegan al script como texto.» No necesitamos ninguno.
- Haz clic en Ejecutar.

El panel Resultado de la ejecución, abajo, se llena: el sello
Éxito con la duración, el valor devuelto en JSON y el bloque Logs
con las líneas que el log() escribió.

Consejo
La primera ejecución de un script Python es siempre la más lenta — el entorno se prepara en ese momento. Las siguientes corren en milisegundos.
El historial de ejecuciones
El resultado en el editor es solo el de la sesión actual. El historial completo está en el botón Ejecuciones, junto al Ejecutar ahora:

Cada fila dice Inicio, Origen (Manual, cuando fuiste tú; Programación, cuando fue la hora marcada), Estado y Duración, y el enlace Detalles abre la ejecución: los argumentos, el resultado devuelto y los logs de aquella corrida en concreto. Por aquí se entiende, tres semanas después, qué vio el script la mañana en que nadie estaba mirando.
Programar para todas las mañanas
Un script que solo corre cuando alguien pulsa el botón no es automatización. Es hora de marcarle una hora.
- En el árbol del panel Código, expande el nodo del script
atualizar_indicadores. Aparecen tres secciones: Archivos, Dependencias y Programaciones. - Pasa el ratón sobre Programaciones y haz clic en el + (Nueva programación).
- Dale el nombre
Indicadores diáriosy crea. El editor de la programación se abre en una pestaña. - En Frecuencia, elige En momentos concretos — «A una hora del día, en los días que elijas.»
- En Repite, elige Todos los días (en los días elegidos).
- En A la hora, escribe
07:00. - En Días, deja los siete seleccionados (o haz clic en Laborables si el fin de semana no interesa).
- En Zona horaria, elige
Europe/Lisbon. Es la zona la que decide qué son «las siete de la mañana» — sin ella, la hora correcta cambia con el cambio de hora. - En Si queda sin ejecutar, elige Ignorar.
- Confirma que el interruptor Activo está encendido y haz clic en Guardar.

Las tres frecuencias
| Frecuencia | Cuándo usarla |
|---|---|
| Repetir | Cada N minutos u horas, sin parar. Sincronizaciones, sondeos. |
| En momentos concretos | A una hora del día, en los días elegidos. Es nuestro caso — y el más común. |
| Ventana de vigilancia | Sondea cada N minutos entre dos horas y para cuando el trabajo del día esté hecho. Para esperar un archivo que llega «por la mañana, a horas variables». |
Quien prefiera escribir la expresión de programación a mano tiene el enlace Escribir la expresión a mano bajo las tres opciones.
Qué hacer con lo que quedó sin ejecutar
El servidor estuvo caído a las siete de la mañana. Cuando vuelva, ¿qué pasa con la ocurrencia fallida? Es lo que decide Si queda sin ejecutar:
| Opción | Qué hace |
|---|---|
| Ignorar | No recupera. Queda registrado que se perdió. |
| Solo la más reciente | Recupera la última que quedó por hacer; las anteriores quedan registradas como perdidas. |
| Todas | Recupera todas las que quedaron por hacer, en el orden en que estaban marcadas. |
Para un resumen diario, Ignorar es lo correcto: ejecutar el resumen del martes el jueves no le sirve a nadie. Para una facturación mensual, Todas tiene todo el sentido.
El panel de ejecuciones de la programación
A la derecha del editor queda el panel Ejecuciones, con las horas previstas y qué pasó con cada una: Hecha, Por hacer, Perdida o Saltada.
Atención
Si este panel avisa de que «el programador no está corriendo en esta instalación — nada será ejecutado», las horas marcadas quedan a la espera y nada corre. El programador se activa en la instalación, no en la app — habla con quien administra la plataforma.
¿Y después?
El script queda listo para más que la hora marcada: puede ser un paso de
una API (para que el resumen se calcule a petición), puede llamar a otros
scripts como dependencia, y puede instalar packages de Python que
necesite. El capítulo Scripts recorre
todo eso, y el SDK de los scripts documenta el
api_manager — base de datos, archivos, secrets, notificaciones y
llamadas HTTP.
¿Por qué no…?
- ¿Por qué el script falla con «datasource no encontrado»? El nombre
en
db("…")tiene que ser exactamente el Nombre interno del datasource, mayúsculas incluidas. - ¿Por qué no veo el botón Ejecutar ahora? El script está inactivo — enciende el interruptor de arriba.
- ¿Por qué la ejecución se cortó a medias? Chocó con el Tiempo límite. O el script tarda de verdad, y aumentas el límite, o está haciendo trabajo de más para el sitio desde donde se llama.
- ¿Por qué la programación nunca corrió? Mira, por orden: ¿la programación está Activa? ¿El script está Activo? ¿El programador está corriendo en esta instalación? ¿Y la Zona horaria es la que crees que es?
El CRM está completo. Falta ponerlo en marcha: publicar y usar.