@danhachuel/pandanews 0.2.2 → 0.3.0
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.
- package/CHANGELOG.md +9 -0
- package/README.md +5 -4
- package/backend/app/main.py +20 -6
- package/backend/app/routers/automacao.py +47 -0
- package/backend/app/routers/blogs.py +10 -0
- package/backend/app/routers/configuracoes.py +12 -0
- package/backend/app/routers/imagens.py +49 -0
- package/backend/app/routers/noticias.py +46 -0
- package/backend/core/database.py +31 -0
- package/backend/core/image_gen.py +82 -4
- package/backend/core/pipeline.py +212 -4
- package/backend/core/scheduler.py +80 -0
- package/frontend/dist/assets/index-BcgBzdzo.js +70 -0
- package/frontend/dist/assets/index-gsZzhCX8.css +1 -0
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/index-BaPdaYIz.css +0 -1
- package/frontend/dist/assets/index-D0e-vr7T.js +0 -70
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Scheduler do PandaNews (APScheduler).
|
|
2
|
+
|
|
3
|
+
Um job por blog ativo, com intervalo = frequencia_horas do blog. O estado
|
|
4
|
+
global (configuracoes.scheduler_ativo) liga/desliga tudo. Os jobs chamam o
|
|
5
|
+
pipeline e registram logs; erros não derrubam o scheduler.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from apscheduler.schedulers.background import BackgroundScheduler
|
|
10
|
+
from apscheduler.triggers.interval import IntervalTrigger
|
|
11
|
+
from sqlalchemy import select
|
|
12
|
+
|
|
13
|
+
from backend.core.database import Blog, Log, SessionLocal
|
|
14
|
+
|
|
15
|
+
_scheduler: BackgroundScheduler | None = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def esta_ativo() -> bool:
|
|
19
|
+
return _scheduler is not None and _scheduler.running
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def iniciar() -> None:
|
|
23
|
+
global _scheduler
|
|
24
|
+
if esta_ativo():
|
|
25
|
+
return
|
|
26
|
+
_scheduler = BackgroundScheduler(timezone="UTC", daemon=True)
|
|
27
|
+
_scheduler.start()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def parar() -> None:
|
|
31
|
+
global _scheduler
|
|
32
|
+
if _scheduler is not None:
|
|
33
|
+
_scheduler.shutdown(wait=False)
|
|
34
|
+
_scheduler = None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def recarregar() -> None:
|
|
38
|
+
"""Sincroniza os jobs com os blogs ativos (após CRUD ou toggle)."""
|
|
39
|
+
if _scheduler is None:
|
|
40
|
+
return
|
|
41
|
+
db = SessionLocal()
|
|
42
|
+
try:
|
|
43
|
+
ativos = db.scalars(select(Blog).where(Blog.status == "ativo")).all()
|
|
44
|
+
_scheduler.remove_all_jobs()
|
|
45
|
+
for blog in ativos:
|
|
46
|
+
_scheduler.add_job(
|
|
47
|
+
_executar,
|
|
48
|
+
IntervalTrigger(hours=max(1, int(blog.frequencia_horas or 6))),
|
|
49
|
+
id=f"blog_{blog.id}",
|
|
50
|
+
args=[blog.id],
|
|
51
|
+
replace_existing=True,
|
|
52
|
+
)
|
|
53
|
+
finally:
|
|
54
|
+
db.close()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def jobs() -> list[dict]:
|
|
58
|
+
if _scheduler is None:
|
|
59
|
+
return []
|
|
60
|
+
resultado = []
|
|
61
|
+
for job in _scheduler.get_jobs():
|
|
62
|
+
resultado.append(
|
|
63
|
+
{
|
|
64
|
+
"blog_id": job.args[0] if job.args else None,
|
|
65
|
+
"proxima_execucao": job.next_run_time.isoformat() if job.next_run_time else None,
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
return resultado
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _executar(blog_id: int) -> None:
|
|
72
|
+
from backend.core import pipeline
|
|
73
|
+
|
|
74
|
+
db = SessionLocal()
|
|
75
|
+
try:
|
|
76
|
+
db.add(Log(blog_id=blog_id, etapa="pipeline", status="sucesso", mensagem="Execução agendada iniciada"))
|
|
77
|
+
db.commit()
|
|
78
|
+
finally:
|
|
79
|
+
db.close()
|
|
80
|
+
pipeline.gerar_noticia(blog_id)
|