V40-V43: band umur vote (BARU/VALID/KADALUARSA, scan simpan semua umur) → REVOTE = VALID langsung (V42) → tag mint 1-hari + badge VOTE ULANG SEGERA (V43) ; klaim recovery jendela lewat + parse respons dict rows (B7/B8) ; backend mysql test_mariadb ; oracle test_dashboard/distribute/get_voters/mariadb ; images web/scan/dist/claim rebuild + push

This commit is contained in:
proitlab committed 2026-08-07 12:07:34 +07:00
1 parent af9a0291b9
commit 81ec6bdfe7
17 files changed
+914 -148

No files matched your search

+51
View File
@@ -337,6 +337,57 @@ def update_claim_fee(claim_id, fee_status, fee_txid=None):
conn.close()
# ——————————————————————————————————————————————————————————————————————
# Kemunculan pertama pemilih (V41). Tabel append kecil — satu baris per owner
# (PK), mengingat KAPAN pertama kali muncul di snapshot. `voters` tetap
# snapshot murni (diganti tiap skan); first_seen bertahan di sini. Dipakai
# dashboard utk membedakan PEMILIH BARU (belum pernah terlihat) vs REVOTE
# (pemilih yang sudah pernah ada, revote/perpanjang).
# ——————————————————————————————————————————————————————————————————————
_FIRST_SEEN_COLS = 'owner VARCHAR(13) PRIMARY KEY, first_seen_at TEXT NOT NULL'
_FIRST_SEEN_DDL_SQLITE = (f'CREATE TABLE IF NOT EXISTS voter_first_seen '
f'({_FIRST_SEEN_COLS})')
_FIRST_SEEN_DDL_MYSQL = (f'CREATE TABLE IF NOT EXISTS voter_first_seen '
f'({_FIRST_SEEN_COLS})')
def ensure_first_seen_schema():
"""Buat tabel kemunculan pertama bila belum ada (idempoten, kedua backend)."""
conn = connect()
try:
cur = conn.cursor()
if DB_BACKEND == 'sqlite':
cur.execute(_FIRST_SEEN_DDL_SQLITE)
else:
cur.execute(_FIRST_SEEN_DDL_MYSQL)
conn.commit()
finally:
conn.close()
def record_first_seen(rows, scanned_at):
"""Catat kemunculan pertama tiap owner (idempoten, ⊥ overwrite).
INSERT OR IGNORE (sqlite) / INSERT IGNORE (mysql): hanya baris yang belum
ada yang masuk — `first_seen_at` selalu tanggal kemunculan pertama.
"""
ensure_first_seen_schema()
conn = connect()
try:
cur = conn.cursor()
if DB_BACKEND == 'sqlite':
sql = ('INSERT OR IGNORE INTO voter_first_seen '
'(owner, first_seen_at) VALUES (?, ?)')
else:
sql = ('INSERT IGNORE INTO voter_first_seen '
'(owner, first_seen_at) VALUES (%s, %s)')
cur.executemany(sql, [(r['owner'], scanned_at) for r in rows])
conn.commit()
finally:
conn.close()
def pending_claim_fee():
"""Klaim terbaru dengan fee belum terkirim (fee_amount > 0) → baris | None.