jadwal distribusi mingguan + banner BERITA jadwal berikutnya di dashboard (V37/V38, T43/T44)
This commit is contained in:
1 parent
b1e6e93796
commit
2f1f19c435
12 files changed
+344
-37
No files matched your search
+59
-20
@@ -1,14 +1,18 @@
|
||||
"""Jadwal distribusi harian: jalankan distribute.py pada jam DISTRIBUTE_HOUR.
|
||||
"""Jadwal distribusi mingguan: jalankan distribute.py pada hari `DISTRIBUTE_WEEKDAY`
|
||||
jam `DISTRIBUTE_HOUR`, plus satu one-off `DISTRIBUTE_FIRST_RUN` (YYYY-MM-DD).
|
||||
|
||||
Menunggu hingga batas `DISTRIBUTE_HOUR` berikutnya (waktu lokal via `TZ`)
|
||||
lalu memanggil `distribute.main()`. Loop tak pernah keluar — service dijaga
|
||||
hidup oleh restart_policy swarm. Gagal distribusi dicatat lalu lanjut ke hari
|
||||
berikutnya (⊥ membunuh loop). Tanpa `VEX_BP_PRIVATE_KEY` run nyata raise
|
||||
RuntimeError yang jelas — ditangkap, dicetak, dicoba lagi besok.
|
||||
Contoh produksi: `DISTRIBUTE_FIRST_RUN=2026-08-17` (Senin) lalu `DISTRIBUTE_WEEKDAY=sat`
|
||||
→ putaran pertama Senin 17 Agt 10:00, berikutnya tiap Sabtu 10:00.
|
||||
|
||||
Menunggu hingga batas terjadwal berikutnya (waktu lokal via `TZ`) lalu memanggil
|
||||
`distribute.main()`. Loop tak pernah keluar — service dijaga hidup oleh
|
||||
restart_policy swarm. Gagal distribusi dicatat lalu lanjut ke jadwal berikutnya
|
||||
(⊥ membunuh loop). Tanpa `VEX_BP_PRIVATE_KEY` run nyata raise RuntimeError yang
|
||||
jelas — ditangkap, dicetak, dicoba lagi di jadwal berikutnya.
|
||||
|
||||
Berbeda dengan scan: ⊥ ada "distribusi-awal" saat start (SCAN_RUN_ON_START) —
|
||||
membayar di container start berisiko memakai snapshot basi; distribusi hanya
|
||||
berjalan pada jam terjadwal.
|
||||
berjalan pada jadwal.
|
||||
|
||||
Scheduler helper (`next_boundary`, `_wait_db`) sengaja diduplikasi dari
|
||||
`scan_loop.py` (bukan di-import) karena kedua image Docker terpisah:
|
||||
@@ -22,19 +26,50 @@ from datetime import datetime, timedelta
|
||||
import db
|
||||
import distribute
|
||||
|
||||
WEEKDAYS = {
|
||||
'mon': 0, 'tue': 1, 'wed': 2, 'thu': 3,
|
||||
'fri': 4, 'sat': 5, 'sun': 6,
|
||||
}
|
||||
|
||||
def next_boundary(now, hours):
|
||||
"""datetime berikutnya pada jam-jam `hours` yang lebih besar dari `now`.
|
||||
|
||||
Sama dengan `scan_loop.next_boundary` (mirror agar image dist mandiri).
|
||||
"""
|
||||
for h in hours:
|
||||
cand = now.replace(hour=h, minute=0, second=0, microsecond=0)
|
||||
if cand > now:
|
||||
return cand
|
||||
# semua jam hari ini sudah lewat → besok jam pertama
|
||||
nxt = now + timedelta(days=1)
|
||||
return nxt.replace(hour=hours[0], minute=0, second=0, microsecond=0)
|
||||
def parse_weekday(value):
|
||||
"""Nama hari (`sat`) atau angka 0-6 → weekday Python (Senin=0)."""
|
||||
v = str(value).strip().lower()
|
||||
if v in WEEKDAYS:
|
||||
return WEEKDAYS[v]
|
||||
if v.isdigit() and 0 <= int(v) <= 6:
|
||||
return int(v)
|
||||
raise ValueError(f'DISTRIBUTE_WEEKDAY tak valid: {value!r}')
|
||||
|
||||
|
||||
def parse_first_run(value):
|
||||
"""`YYYY-MM-DD` → date; kosong → None (⊥ one-off)."""
|
||||
if not value:
|
||||
return None
|
||||
return datetime.strptime(str(value).strip(), '%Y-%m-%d').date()
|
||||
|
||||
|
||||
def next_boundary(now, hour, weekday, first_run=None):
|
||||
"""datetime berikutnya utk distribusi.
|
||||
|
||||
Mingguan: `weekday` jam `hour`. Bila `first_run` diset, distribusi ⊥ jalan
|
||||
sebelum tanggal itu — Sabtu (atau hari mingguan) yang jatuh sebelum launch
|
||||
dilewati; event = {one-off `first_run` (jika masih depan)} ∪ {mingguan ≥
|
||||
tanggal `first_run`}; pilih yang terdekat > `now`."""
|
||||
days_ahead = (weekday - now.weekday()) % 7
|
||||
weekly = (now.replace(hour=hour, minute=0, second=0, microsecond=0)
|
||||
+ timedelta(days=days_ahead))
|
||||
if weekly <= now:
|
||||
weekly += timedelta(days=7)
|
||||
if first_run is None:
|
||||
return weekly
|
||||
start = datetime(first_run.year, first_run.month, first_run.day, hour=hour)
|
||||
candidates = []
|
||||
if start >= now:
|
||||
candidates.append(start)
|
||||
if weekly >= start:
|
||||
candidates.append(weekly)
|
||||
return min(candidates)
|
||||
|
||||
|
||||
def _wait_db(timeout=120):
|
||||
@@ -61,11 +96,15 @@ def main():
|
||||
hour = int(os.getenv('DISTRIBUTE_HOUR', '10'))
|
||||
if not 0 <= hour <= 23:
|
||||
raise ValueError(f'DISTRIBUTE_HOUR tak valid: {hour!r}')
|
||||
print(f'Jadwal distribusi: jam {hour} (waktu lokal)')
|
||||
weekday = parse_weekday(os.getenv('DISTRIBUTE_WEEKDAY', 'sat'))
|
||||
first_run = parse_first_run(os.getenv('DISTRIBUTE_FIRST_RUN', ''))
|
||||
jadwal = (f'jam {hour} tiap hari {list(WEEKDAYS)[weekday]} (waktu lokal)'
|
||||
+ (f', one-off {first_run.isoformat()}' if first_run else ''))
|
||||
print(f'Jadwal distribusi: {jadwal}')
|
||||
_wait_db()
|
||||
while True:
|
||||
now = datetime.now()
|
||||
target = next_boundary(now, [hour])
|
||||
target = next_boundary(now, hour, weekday, first_run)
|
||||
wait = (target - now).total_seconds()
|
||||
print(f'{now.isoformat(timespec="seconds")} tidur {wait / 3600:.2f} jam '
|
||||
f'sampai {target.isoformat(timespec="seconds")}', flush=True)
|
||||
|
||||
Reference in new issue
Block a user