bgo-scheduler 1.9.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bgo_scheduler-1.9.5/LICENSE +21 -0
- bgo_scheduler-1.9.5/PKG-INFO +412 -0
- bgo_scheduler-1.9.5/README.md +395 -0
- bgo_scheduler-1.9.5/pyproject.toml +51 -0
- bgo_scheduler-1.9.5/setup.cfg +4 -0
- bgo_scheduler-1.9.5/src/bgo_scheduler/__init__.py +3 -0
- bgo_scheduler-1.9.5/src/bgo_scheduler/__main__.py +6 -0
- bgo_scheduler-1.9.5/src/bgo_scheduler/cli.py +89 -0
- bgo_scheduler-1.9.5/src/bgo_scheduler/config.py +314 -0
- bgo_scheduler-1.9.5/src/bgo_scheduler/cron.py +104 -0
- bgo_scheduler-1.9.5/src/bgo_scheduler/dashboard.html +1076 -0
- bgo_scheduler-1.9.5/src/bgo_scheduler/icons/err.ico +0 -0
- bgo_scheduler-1.9.5/src/bgo_scheduler/icons/ok.ico +0 -0
- bgo_scheduler-1.9.5/src/bgo_scheduler/icons/run.ico +0 -0
- bgo_scheduler-1.9.5/src/bgo_scheduler/loki_logger.py +66 -0
- bgo_scheduler-1.9.5/src/bgo_scheduler/scheduler_core.py +1378 -0
- bgo_scheduler-1.9.5/src/bgo_scheduler/tray.py +339 -0
- bgo_scheduler-1.9.5/src/bgo_scheduler/web_dashboard.py +251 -0
- bgo_scheduler-1.9.5/src/bgo_scheduler.egg-info/PKG-INFO +412 -0
- bgo_scheduler-1.9.5/src/bgo_scheduler.egg-info/SOURCES.txt +31 -0
- bgo_scheduler-1.9.5/src/bgo_scheduler.egg-info/dependency_links.txt +1 -0
- bgo_scheduler-1.9.5/src/bgo_scheduler.egg-info/entry_points.txt +5 -0
- bgo_scheduler-1.9.5/src/bgo_scheduler.egg-info/requires.txt +4 -0
- bgo_scheduler-1.9.5/src/bgo_scheduler.egg-info/top_level.txt +1 -0
- bgo_scheduler-1.9.5/tests/test_chaining.py +92 -0
- bgo_scheduler-1.9.5/tests/test_config.py +127 -0
- bgo_scheduler-1.9.5/tests/test_cron.py +45 -0
- bgo_scheduler-1.9.5/tests/test_dashboard_api.py +231 -0
- bgo_scheduler-1.9.5/tests/test_rules.py +71 -0
- bgo_scheduler-1.9.5/tests/test_schedule_edit.py +95 -0
- bgo_scheduler-1.9.5/tests/test_scheduler.py +301 -0
- bgo_scheduler-1.9.5/tests/test_settings.py +115 -0
- bgo_scheduler-1.9.5/tests/test_sleep_hours.py +157 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bruno Oliveira
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bgo-scheduler
|
|
3
|
+
Version: 1.9.5
|
|
4
|
+
Summary: Scheduler de apps com system tray, dashboard live e logs Grafana Loki
|
|
5
|
+
Author-email: Bruno Oliveira <bruno.goncalo.oliveira@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
8
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
15
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
# bgo_scheduler
|
|
19
|
+
|
|
20
|
+
Scheduler de apps para Windows com ícone de system tray, dashboard web live e
|
|
21
|
+
logs em formato Grafana Loki. Distribuído como wheel Python.
|
|
22
|
+
|
|
23
|
+
## Objetivos
|
|
24
|
+
|
|
25
|
+
- **Scheduler Python de zero dependências** — o runtime não instala nada além
|
|
26
|
+
da biblioteca padrão.
|
|
27
|
+
- **Avisos que tiram partido do Windows** — ícone de system tray (Win32 nativo)
|
|
28
|
+
e notificações (toasts) para o estado das execuções.
|
|
29
|
+
- **Sleep hours transversais e específicas por app** — períodos em que o
|
|
30
|
+
agendamento fica em pausa, definidos globalmente e/ou por aplicação.
|
|
31
|
+
- **Encadeamento de execuções** — uma app corre depois de outra terminar com
|
|
32
|
+
sucesso (ex.: `app02` corre após `app01`).
|
|
33
|
+
- **Visão agregada e por app** — uma vista geral de todas as execuções e o
|
|
34
|
+
detalhe/histórico de cada aplicação.
|
|
35
|
+
|
|
36
|
+
## Screenshots
|
|
37
|
+
|
|
38
|
+
**Visão geral de todas as apps** — indicadores no topo, lista de apps e o
|
|
39
|
+
histórico agregado com estado, RC, notificações e mensagens.
|
|
40
|
+
|
|
41
|
+

|
|
42
|
+
|
|
43
|
+
**Detalhe de uma app** — abas Monitorização / Configuração, histórico com
|
|
44
|
+
duração e origem, e "Executar agora".
|
|
45
|
+
|
|
46
|
+

|
|
47
|
+
|
|
48
|
+
**Definições globais** e **sleep hours transversais**, editáveis no dashboard:
|
|
49
|
+
|
|
50
|
+
<p>
|
|
51
|
+
<img src="docs/images/definicoes-globais.png" alt="Modal de definições globais (host, port, raízes de apps, links)" width="440">
|
|
52
|
+
<img src="docs/images/sleep-hours.png" alt="Modal de sleep hours transversais" width="360">
|
|
53
|
+
</p>
|
|
54
|
+
|
|
55
|
+
## Instalação
|
|
56
|
+
|
|
57
|
+
```bat
|
|
58
|
+
pip install dist\bgo_scheduler-1.9.5-py3-none-any.whl
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Sem dependências de runtime**: o ícone de tray é nativo do Windows (Win32
|
|
62
|
+
via `ctypes`, carrega ficheiros `.ico`) e o dashboard usa só a stdlib.
|
|
63
|
+
|
|
64
|
+
Fica com dois comandos:
|
|
65
|
+
|
|
66
|
+
- `bgo-scheduler` — com consola (bom para debug); aceita `--headless` para
|
|
67
|
+
correr sem ícone de tray (só scheduler + dashboard).
|
|
68
|
+
- `bgo-scheduler-tray` — sem janela de consola; é este que deves usar no
|
|
69
|
+
arranque do Windows (atalho na pasta `shell:startup`).
|
|
70
|
+
|
|
71
|
+
Para reconstruir o wheel: `python -m pip wheel . --no-deps -w dist`.
|
|
72
|
+
Em desenvolvimento (sem instalar): `python systray_icon.py`.
|
|
73
|
+
|
|
74
|
+
## Estrutura de pastas e ficheiros
|
|
75
|
+
|
|
76
|
+
Há **duas zonas distintas**: a pasta de *configuração/dados* do scheduler e a(s)
|
|
77
|
+
pasta(s) de *apps*. Não têm de ser a mesma (embora no primeiro arranque
|
|
78
|
+
coincidam — ver "Configuração").
|
|
79
|
+
|
|
80
|
+
### Pasta de configuração/dados
|
|
81
|
+
|
|
82
|
+
Por omissão `%APPDATA%\bgo_scheduler` (ou onde apontar o `--config` /
|
|
83
|
+
`BGO_SCHEDULER_CONFIG`). É aqui que o scheduler guarda tudo o que produz:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
%APPDATA%\bgo_scheduler\
|
|
87
|
+
├── scheduler.ini ← configuração GLOBAL do scheduler
|
|
88
|
+
├── notification_rules.json ← regras de notificação e mensagens
|
|
89
|
+
├── logs\ ← logs JSON-lines (formato Grafana Loki)
|
|
90
|
+
│ ├── scheduler.log ← log do próprio scheduler
|
|
91
|
+
│ ├── hello1.log ← um ficheiro por app
|
|
92
|
+
│ └── hello2.log
|
|
93
|
+
└── history\ ← histórico de execuções (persiste entre reinícios)
|
|
94
|
+
├── hello1.jsonl ← uma linha JSON por execução
|
|
95
|
+
└── hello2.jsonl
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
| Ficheiro / pasta | Para que serve |
|
|
99
|
+
|---|---|
|
|
100
|
+
| `scheduler.ini` | Configuração global: onde estão as apps (`[Apps] roots`), o dashboard (`host`/`port`), `max_parallel`, sleep hours transversais, atalhos `[Links]`. Parte é editável no dashboard (⚙ Definições), o resto à mão. |
|
|
101
|
+
| `notification_rules.json` | Padrões que disparam **notificações de erro** e **mensagens de sucesso/warning**. Gerido pelo dashboard (aba Configuração de cada app) — normalmente não se edita à mão. |
|
|
102
|
+
| `logs\<app>.log` | Um ficheiro por app; cada linha é um objeto JSON (`ts`, `level`, `app`, `event`, `msg`) pronto para o Grafana Loki. Escrito **à medida que a app corre**. Rotação automática 5 MB × 3. |
|
|
103
|
+
| `logs\scheduler.log` | Log do próprio scheduler: arranque, rescan, avisos de configuração, encadeamentos. |
|
|
104
|
+
| `history\<app>.jsonl` | Uma linha JSON por execução (início, fim, duração, código de saída, estado, origem, excerto do output, notificações). Carregado no arranque para o dashboard mostrar o histórico logo após reiniciar. Compactado acima de 2 MB. |
|
|
105
|
+
|
|
106
|
+
A pasta `logs\` pode ser movida com `[Logs] dir` no INI (ex.: para uma pasta que
|
|
107
|
+
o Promtail/Alloy já vigie); a `history\` fica sempre ao lado do INI.
|
|
108
|
+
|
|
109
|
+
### Pasta de apps
|
|
110
|
+
|
|
111
|
+
Cada **sub-pasta** de uma raiz (`[Apps] roots`) é uma app. O nome da app é o
|
|
112
|
+
nome da pasta (é o que aparece no tray e no dashboard). A pasta tem de conter
|
|
113
|
+
`main.py` (preferido) ou `main.bat`, e opcionalmente um `schedule.ini`.
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
C:\Users\asus\Desktop\bgo_apps\ ← uma raiz (em [Apps] roots)
|
|
117
|
+
├── hello1\ ← app "hello1"
|
|
118
|
+
│ ├── main.py ← o que é executado
|
|
119
|
+
│ └── schedule.ini ← periodicidade/opções (opcional)
|
|
120
|
+
├── relatorios\ ← app "relatorios"
|
|
121
|
+
│ ├── main.py
|
|
122
|
+
│ ├── schedule.ini
|
|
123
|
+
│ └── .venv\ ← venv próprio (opcional; ver python_exe)
|
|
124
|
+
└── backup\ ← app "backup"
|
|
125
|
+
└── main.bat ← app sem Python: corre o .bat (com logs à mesma)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
- A app corre com a **própria pasta como diretório de trabalho** (caminhos
|
|
129
|
+
relativos dentro do `main.py` são resolvidos a partir daí).
|
|
130
|
+
- Sem `schedule.ini`, a app fica ativa e corre **a cada 60 min**.
|
|
131
|
+
- **Código de saída**: `0` = ok; `≠ 0` marca a execução como "erro" e (por
|
|
132
|
+
omissão) dispara notificação.
|
|
133
|
+
|
|
134
|
+
Exemplo de `hello1\main.py`:
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
import sys
|
|
138
|
+
|
|
139
|
+
print("a processar…") # vai para logs/hello1.log (event=stdout)
|
|
140
|
+
# ... trabalho ...
|
|
141
|
+
print("42 registos processados") # pode virar "mensagem de sucesso" no dashboard
|
|
142
|
+
sys.exit(0) # 0 = ok; != 0 = erro
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Exemplos de `schedule.ini` (todas as opções também são editáveis no dashboard):
|
|
146
|
+
|
|
147
|
+
```ini
|
|
148
|
+
; de 30 em 30 minutos
|
|
149
|
+
[Schedule]
|
|
150
|
+
interval_minutes = 30
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
```ini
|
|
154
|
+
; dias úteis às 09:00, aborta se passar de 10 min
|
|
155
|
+
[Schedule]
|
|
156
|
+
cron = 0 9 * * 1-5
|
|
157
|
+
timeout_minutes = 10
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
```ini
|
|
161
|
+
; interpretador próprio (venv da app) e nunca dorme (ignora sleep hours)
|
|
162
|
+
[Schedule]
|
|
163
|
+
interval_minutes = 60
|
|
164
|
+
python_exe = .venv\Scripts\python.exe
|
|
165
|
+
ignore_sleep_hours = true
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
```ini
|
|
169
|
+
; encadeamento: corre quando a app "extrair" termina com sucesso
|
|
170
|
+
[Schedule]
|
|
171
|
+
run_after = extrair
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
O detalhe completo de cada opção está mais abaixo, em
|
|
175
|
+
[schedule.ini (por app)](#scheduleini-por-app-opcional).
|
|
176
|
+
|
|
177
|
+
## Configuração
|
|
178
|
+
|
|
179
|
+
O `scheduler.ini` é procurado por esta ordem:
|
|
180
|
+
|
|
181
|
+
1. `--config C:\caminho\scheduler.ini`
|
|
182
|
+
2. variável de ambiente `BGO_SCHEDULER_CONFIG`
|
|
183
|
+
3. `%APPDATA%\bgo_scheduler\scheduler.ini` — criado automaticamente no
|
|
184
|
+
primeiro arranque
|
|
185
|
+
|
|
186
|
+
**No primeiro arranque** (sem `--config` e sem INI existente), o
|
|
187
|
+
`scheduler.ini` é criado em `%APPDATA%\bgo_scheduler` com `roots` a apontar
|
|
188
|
+
para essa mesma pasta. Ou seja, funciona logo: basta criares sub-pastas com as
|
|
189
|
+
apps dentro de `%APPDATA%\bgo_scheduler`. Podes depois trocar os `roots` no INI
|
|
190
|
+
(ou no dashboard) para outras pastas.
|
|
191
|
+
|
|
192
|
+
Ao lado do INI vivem o `notification_rules.json` (editado no dashboard) e a
|
|
193
|
+
pasta `logs\` (alterável em `[Logs] dir`).
|
|
194
|
+
|
|
195
|
+
### Onde estão as apps
|
|
196
|
+
|
|
197
|
+
Na secção `[Apps]` do INI indicas **uma ou mais pastas, uma por linha**:
|
|
198
|
+
|
|
199
|
+
```ini
|
|
200
|
+
[Apps]
|
|
201
|
+
roots =
|
|
202
|
+
C:\Users\asus\Desktop\bgo_apps
|
|
203
|
+
D:\outros_jobs
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Cada sub-pasta de cada raiz com `main.py` (ou, em alternativa, `main.bat`) é
|
|
207
|
+
uma app. Se duas raízes tiverem apps com o mesmo nome, a primeira ganha e
|
|
208
|
+
fica um aviso no log do scheduler. Para testes pontuais:
|
|
209
|
+
`bgo-scheduler --apps-root C:\outra\pasta` (repetível; ignora as roots do INI
|
|
210
|
+
nessa execução).
|
|
211
|
+
|
|
212
|
+
### scheduler.ini completo
|
|
213
|
+
|
|
214
|
+
- `[Dashboard]` — `host`, `port` (por omissão 8765), `open_on_start`.
|
|
215
|
+
- `[Apps]` — `roots` (ver acima) e `exclude` (pastas a ignorar).
|
|
216
|
+
- `[Execution]` — `max_parallel` (0 = sem limite): número máximo de apps a
|
|
217
|
+
executar em simultâneo. Quando o limite é atingido, as restantes ficam
|
|
218
|
+
**em fila** e arrancam à medida que abrem lugares (evita picos quando muitas
|
|
219
|
+
apps partilham o mesmo horário).
|
|
220
|
+
- `[Logs]` — `dir` (vazio = `logs\` ao lado do INI).
|
|
221
|
+
- `[Links]` — cada `Nome = URL` vira item de menu no tray e link no topo do
|
|
222
|
+
dashboard (Hello1, Hello2, …).
|
|
223
|
+
- `[SleepHours]` — período diário em que as apps **não** são executadas
|
|
224
|
+
automaticamente (ver abaixo).
|
|
225
|
+
|
|
226
|
+
### Sleep hours (pausa do agendamento)
|
|
227
|
+
|
|
228
|
+
Período em que as execuções agendadas e cron ficam em pausa (a app retoma
|
|
229
|
+
quando o período termina). Suporta janelas que atravessam a meia-noite
|
|
230
|
+
(ex.: `22:00`–`07:00`). A **execução manual** continua sempre disponível.
|
|
231
|
+
|
|
232
|
+
**Tudo editável no dashboard** — não é preciso mexer nos ficheiros à mão:
|
|
233
|
+
|
|
234
|
+
- **Transversal** (a todas as apps): clica no indicador "sleep hours" no
|
|
235
|
+
topo do dashboard. Guarda em `scheduler.ini [SleepHours]`.
|
|
236
|
+
```ini
|
|
237
|
+
[SleepHours]
|
|
238
|
+
enabled = true
|
|
239
|
+
start = 22:00
|
|
240
|
+
end = 07:00
|
|
241
|
+
```
|
|
242
|
+
- **Por app** (no detalhe da app, secção "Sleep hours desta app"), com três
|
|
243
|
+
modos, guardados no `schedule.ini` da app:
|
|
244
|
+
- **Herdar** a transversal (por omissão);
|
|
245
|
+
- **Ignorar** — app crítica, nunca dorme (`ignore_sleep_hours = true`);
|
|
246
|
+
- **Horário próprio** — janela só desta app (`sleep_hours = 23:00-06:00`).
|
|
247
|
+
|
|
248
|
+
As alterações aplicam-se de imediato, sem reiniciar. O dashboard mostra um
|
|
249
|
+
indicador no topo e marca cada app em pausa.
|
|
250
|
+
|
|
251
|
+
### schedule.ini (por app, opcional)
|
|
252
|
+
|
|
253
|
+
```ini
|
|
254
|
+
[Schedule]
|
|
255
|
+
enabled = true
|
|
256
|
+
|
|
257
|
+
; agendamento por intervalo:
|
|
258
|
+
interval_minutes = 60
|
|
259
|
+
; OU agendamento cron (tem prioridade sobre interval_minutes):
|
|
260
|
+
; cron = 0 9 * * 1-5 <- dias úteis às 09:00 (hora local)
|
|
261
|
+
|
|
262
|
+
; aborta a execução ao fim de N minutos (0 = sem limite)
|
|
263
|
+
timeout_minutes = 0
|
|
264
|
+
|
|
265
|
+
; interpretador Python próprio (venv da app); caminho absoluto
|
|
266
|
+
; ou relativo à pasta da app
|
|
267
|
+
; python_exe = .venv\Scripts\python.exe
|
|
268
|
+
|
|
269
|
+
; ignorar o período de sleep hours (ver scheduler.ini) para esta app
|
|
270
|
+
; ignore_sleep_hours = false
|
|
271
|
+
|
|
272
|
+
; encadeamento: corre quando a(s) app(s) a montante terminam com sucesso,
|
|
273
|
+
; em vez de por intervalo/cron (vários nomes separados por vírgula)
|
|
274
|
+
; run_after = extrair, transformar
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Sem `schedule.ini`: ativa, a cada 60 minutos. Em modo intervalo, conta a
|
|
278
|
+
partir do fim da execução anterior e há uma execução no arranque do
|
|
279
|
+
scheduler; em modo cron respeita-se apenas o horário definido. A app corre
|
|
280
|
+
com a própria pasta como diretório de trabalho e execuções sobrepostas são
|
|
281
|
+
ignoradas (fica registado no log).
|
|
282
|
+
|
|
283
|
+
**Todo o agendamento é editável no dashboard** (detalhe da app → secção
|
|
284
|
+
"Agendamento"): modo intervalo/cron, timeout e o encadeamento — sem editar
|
|
285
|
+
ficheiros nem reiniciar.
|
|
286
|
+
|
|
287
|
+
### Encadeamento de apps (pipelines)
|
|
288
|
+
|
|
289
|
+
Com `run_after`, uma app deixa de correr por tempo e passa a correr quando a(s)
|
|
290
|
+
app(s) indicada(s) **terminam com sucesso** — útil para pipelines de dados
|
|
291
|
+
(ex.: `extrair` → `transformar` → `carregar`). As execuções mostram a origem
|
|
292
|
+
"dependência (…)" no histórico e no log (evento `chain_trigger`). O scheduler
|
|
293
|
+
deteta e quebra ciclos (com aviso) e ignora referências a apps inexistentes.
|
|
294
|
+
A execução manual continua sempre disponível.
|
|
295
|
+
|
|
296
|
+
Sintaxe cron (5 campos): `minuto hora dia-do-mês mês dia-da-semana`, com
|
|
297
|
+
`*`, listas `1,2,3`, intervalos `1-5` e passos `*/15`. Dia da semana: 0 e 7
|
|
298
|
+
= domingo. Exemplos: `*/30 * * * *` (de 30 em 30 min), `0 9 * * 1-5` (dias
|
|
299
|
+
úteis às 09:00), `0 7 1 * *` (dia 1 de cada mês às 07:00).
|
|
300
|
+
|
|
301
|
+
Ligar/desligar o agendamento no dashboard **fica gravado** no `schedule.ini`
|
|
302
|
+
da app (comentários preservados).
|
|
303
|
+
|
|
304
|
+
## Dashboard (http://127.0.0.1:8765/)
|
|
305
|
+
|
|
306
|
+
- Homepage com a **visão geral das execuções** de todas as apps (app,
|
|
307
|
+
início/fim, RC, estado, notificações e mensagens destacadas).
|
|
308
|
+
- O detalhe de cada app separa **consulta de edição** em duas abas:
|
|
309
|
+
- **Monitorização** (por omissão): estado, histórico com output e log viewer.
|
|
310
|
+
- **Configuração**: agendamento, sleep hours e regras de notificação/mensagens,
|
|
311
|
+
com **um único "Guardar configuração"** (só grava as secções alteradas) e
|
|
312
|
+
aviso de "alterações por gravar" / confirmação ao sair.
|
|
313
|
+
- Ações rápidas sempre no topo do detalhe: **Executar agora** e ligar/desligar
|
|
314
|
+
o agendamento (gravado no schedule.ini). **↻ Redetetar apps** sem reiniciar.
|
|
315
|
+
- **⚙ Definições** (topo): edita `host`/`port`/`open_on_start`/`max_parallel`,
|
|
316
|
+
as raízes de apps (`[Apps] roots`) e os `[Links]` no `scheduler.ini`. As
|
|
317
|
+
**raízes** (redeteta as apps ao guardar), os links e o `open_on_start`
|
|
318
|
+
aplicam-se ao vivo; `host`/`port`/`max_parallel` ficam gravados mas só depois
|
|
319
|
+
de reiniciar (o dashboard avisa).
|
|
320
|
+
- **Notificações de erro**: por omissão notifica (toast do Windows) quando o
|
|
321
|
+
código de saída ≠ 0; podes acrescentar padrões (texto ou regex) por app ou
|
|
322
|
+
globais que disparam notificação quando aparecem no output.
|
|
323
|
+
- **Mensagens de sucesso/warning**: padrões que destacam linhas do output na
|
|
324
|
+
homepage e no histórico (sem toast).
|
|
325
|
+
- Menu do tray: cada app tem "Abrir dashboard" e "Executar agora"; item
|
|
326
|
+
"Redetetar apps"; duplo-clique no ícone abre o dashboard geral.
|
|
327
|
+
|
|
328
|
+
## Logs
|
|
329
|
+
|
|
330
|
+
Cada linha de `logs\<app>.log` é um objeto JSON (mesmo para apps `main.bat`,
|
|
331
|
+
porque é o scheduler que captura o stdout/stderr):
|
|
332
|
+
|
|
333
|
+
```json
|
|
334
|
+
{"ts": "2026-07-08T16:38:34.229Z", "level": "error", "app": "app_fail",
|
|
335
|
+
"event": "stderr", "msg": "ERRO: falha ao ligar à base de dados"}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
As linhas de `stdout`/`stderr` são escritas **à medida que a app corre**
|
|
339
|
+
(logs live no Loki), não só no fim da execução.
|
|
340
|
+
|
|
341
|
+
Eventos: `run_start`, `stdout`, `stderr`, `run_end` (com `status`,
|
|
342
|
+
`returncode`, `duration_s`), `run_timeout`, `run_skipped`, `run_queued`,
|
|
343
|
+
`notify`. Rotação automática: 5 MB × 3 backups. O log do próprio scheduler é
|
|
344
|
+
`logs\scheduler.log`.
|
|
345
|
+
|
|
346
|
+
Exemplo de scrape no Promtail (ou equivalente no Grafana Alloy):
|
|
347
|
+
|
|
348
|
+
```yaml
|
|
349
|
+
scrape_configs:
|
|
350
|
+
- job_name: bgo_scheduler
|
|
351
|
+
static_configs:
|
|
352
|
+
- targets: [localhost]
|
|
353
|
+
labels:
|
|
354
|
+
job: bgo_scheduler
|
|
355
|
+
__path__: C:\Users\asus\AppData\Roaming\bgo_scheduler\logs\*.log
|
|
356
|
+
pipeline_stages:
|
|
357
|
+
- json:
|
|
358
|
+
expressions: { ts: ts, level: level, app: app, event: event }
|
|
359
|
+
- labels: { app: "", level: "" }
|
|
360
|
+
- timestamp: { source: ts, format: RFC3339 }
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## Estrutura do projeto
|
|
364
|
+
|
|
365
|
+
```
|
|
366
|
+
bgo_scheduler\
|
|
367
|
+
├── pyproject.toml ← metadata do package + entry points
|
|
368
|
+
├── systray_icon.py ← arranque em modo dev (sem instalar)
|
|
369
|
+
└── src\bgo_scheduler\
|
|
370
|
+
├── cli.py ← bgo-scheduler / bgo-scheduler-tray
|
|
371
|
+
├── config.py ← resolução e leitura do scheduler.ini
|
|
372
|
+
├── scheduler_core.py ← deteção de apps, agendamento, execução, regras
|
|
373
|
+
├── loki_logger.py ← JSON-lines para o Loki
|
|
374
|
+
├── web_dashboard.py ← API + servidor do dashboard (stdlib)
|
|
375
|
+
├── cron.py ← parser de expressões cron
|
|
376
|
+
├── tray.py ← ícone de system tray (Win32 nativo via ctypes)
|
|
377
|
+
├── dashboard.html ← página do dashboard (package data)
|
|
378
|
+
└── icons\ ← ok/run/err .ico do tray (package data)
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
## Avisos de configuração
|
|
382
|
+
|
|
383
|
+
Erros no `scheduler.ini` ou num `schedule.ini` (porto inválido, cron mal
|
|
384
|
+
formada, `python_exe` inexistente, raiz de apps em falta, etc.) já não são
|
|
385
|
+
silenciosos: aparecem num banner no topo do dashboard e por app (⚠), além de
|
|
386
|
+
irem para `logs\scheduler.log` (evento `config_warning`).
|
|
387
|
+
|
|
388
|
+
## Desenvolvimento
|
|
389
|
+
|
|
390
|
+
```bat
|
|
391
|
+
pip install -e ".[dev]" :: instala pytest + ruff
|
|
392
|
+
ruff check .
|
|
393
|
+
pytest -q
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
A suite (`tests\`, ~70 testes) cobre cron, config/sleep hours/avisos, regras,
|
|
397
|
+
execução, streaming de logs, timeout, concorrência, histórico persistente,
|
|
398
|
+
rescan, toggle e a API HTTP do dashboard. O CI (GitHub Actions,
|
|
399
|
+
`.github\workflows\ci.yml`) corre ruff + pytest em Windows (Python 3.9 e 3.12)
|
|
400
|
+
a cada push/PR.
|
|
401
|
+
|
|
402
|
+
## To Do
|
|
403
|
+
|
|
404
|
+
- [ ] Tornar a aplicação multi-língua (i18n do dashboard e das mensagens).
|
|
405
|
+
- [ ] Tornar a aplicação cross-platform (Linux/macOS): abstrair o tray e as
|
|
406
|
+
notificações, hoje específicos do Windows.
|
|
407
|
+
|
|
408
|
+
## Licença
|
|
409
|
+
|
|
410
|
+
MIT — ver [LICENSE](LICENSE). Copyright © 2026 Bruno Oliveira. Podes usar,
|
|
411
|
+
copiar, modificar e distribuir livremente, **desde que mantenhas o aviso de
|
|
412
|
+
copyright e a licença**; o software é fornecido "tal como está", sem garantias.
|