193 lines
6.4 KiB
Python
193 lines
6.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
redistribute_periode.py
|
|
-----------------------
|
|
Memisahkan ulasan 2025 yang salah masuk ke periode Mei 2026,
|
|
lalu memindahkan ke periode yang benar berdasarkan kolom `tanggal`.
|
|
|
|
Jalankan dari root project Laravel:
|
|
python scripts/redistribute_periode.py
|
|
|
|
Atau dengan --dry-run untuk preview tanpa mengubah data:
|
|
python scripts/redistribute_periode.py --dry-run
|
|
"""
|
|
|
|
import sys
|
|
import argparse
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
|
|
# ── path setup ──────────────────────────────────────────────────────────────
|
|
BASE_DIR = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
|
|
# re-use helper dari scraping_pipeline
|
|
from scraping_pipeline import (
|
|
db_config,
|
|
make_connection,
|
|
prepare_sql,
|
|
execute,
|
|
is_sqlite_connection,
|
|
)
|
|
|
|
NAMA_BULAN = [
|
|
"Januari", "Februari", "Maret", "April", "Mei", "Juni",
|
|
"Juli", "Agustus", "September", "Oktober", "November", "Desember",
|
|
]
|
|
|
|
|
|
def log(level, msg):
|
|
print(f"[{level}] {msg}", flush=True)
|
|
|
|
|
|
def parse_args():
|
|
p = argparse.ArgumentParser()
|
|
p.add_argument(
|
|
"--dry-run",
|
|
action="store_true",
|
|
help="Preview perubahan tanpa benar-benar mengubah database.",
|
|
)
|
|
p.add_argument(
|
|
"--tahun",
|
|
type=int,
|
|
default=2025,
|
|
help="Tahun ulasan yang akan dipisah. Default: 2025.",
|
|
)
|
|
return p.parse_args()
|
|
|
|
|
|
def get_or_create_periode(cursor, conn, bulan, tahun, dry_run):
|
|
"""Ambil id periode yang ada, atau buat baru jika belum ada."""
|
|
execute(
|
|
cursor, conn,
|
|
"SELECT id FROM periode_analisis WHERE bulan = %s AND tahun = %s LIMIT 1",
|
|
(bulan, tahun),
|
|
)
|
|
row = cursor.fetchone()
|
|
if row:
|
|
return row[0]
|
|
|
|
nama = f"{NAMA_BULAN[bulan - 1]} {tahun}"
|
|
if dry_run:
|
|
log("DRY-RUN", f"Akan buat periode baru: {nama}")
|
|
return None # belum ada id nyata
|
|
|
|
execute(
|
|
cursor, conn,
|
|
"""
|
|
INSERT INTO periode_analisis (nama, bulan, tahun, created_at, updated_at)
|
|
VALUES (%s, %s, %s, NOW(), NOW())
|
|
""",
|
|
(nama, bulan, tahun),
|
|
)
|
|
conn.commit()
|
|
new_id = cursor.lastrowid
|
|
log("OK", f"Periode baru dibuat: {nama} (id={new_id})")
|
|
return new_id
|
|
|
|
|
|
def main():
|
|
args = parse_args()
|
|
dry_run = args.dry_run
|
|
tahun = args.tahun
|
|
|
|
if dry_run:
|
|
log("INFO", "Mode DRY-RUN — tidak ada perubahan yang disimpan.")
|
|
|
|
config = db_config()
|
|
conn = make_connection(config)
|
|
cursor = conn.cursor()
|
|
|
|
# ── 1. Ambil semua ulasan tahun target yang periode-nya salah ───────────
|
|
# "Salah" = bulan/tahun di kolom tanggal tidak cocok dengan periode_analisis
|
|
if is_sqlite_connection(conn):
|
|
sql_fetch = """
|
|
SELECT u.id, u.tanggal, u.wisata, u.periode_id,
|
|
p.bulan AS p_bulan, p.tahun AS p_tahun
|
|
FROM ulasan u
|
|
JOIN periode_analisis p ON u.periode_id = p.id
|
|
WHERE strftime('%Y', u.tanggal) = ?
|
|
AND (
|
|
CAST(strftime('%m', u.tanggal) AS INTEGER) != p.bulan
|
|
OR CAST(strftime('%Y', u.tanggal) AS INTEGER) != p.tahun
|
|
)
|
|
"""
|
|
cursor.execute(sql_fetch, (str(tahun),))
|
|
else:
|
|
sql_fetch = """
|
|
SELECT u.id, u.tanggal, u.wisata, u.periode_id,
|
|
p.bulan AS p_bulan, p.tahun AS p_tahun
|
|
FROM ulasan u
|
|
JOIN periode_analisis p ON u.periode_id = p.id
|
|
WHERE YEAR(u.tanggal) = %s
|
|
AND (
|
|
MONTH(u.tanggal) != p.bulan
|
|
OR YEAR(u.tanggal) != p.tahun
|
|
)
|
|
"""
|
|
cursor.execute(sql_fetch, (tahun,))
|
|
|
|
rows = cursor.fetchall()
|
|
log("INFO", f"Ditemukan {len(rows)} ulasan {tahun} dengan periode tidak sesuai tanggal.")
|
|
|
|
if not rows:
|
|
log("OK", "Tidak ada data yang perlu dipindahkan.")
|
|
conn.close()
|
|
return
|
|
|
|
# ── 2. Kelompokkan per bulan ─────────────────────────────────────────────
|
|
from collections import defaultdict
|
|
grouped = defaultdict(list) # (bulan, tahun) → [id, ...]
|
|
|
|
for row in rows:
|
|
ulasan_id, tanggal, wisata, periode_id, p_bulan, p_tahun = row
|
|
try:
|
|
if isinstance(tanggal, str):
|
|
dt = datetime.strptime(tanggal[:10], "%Y-%m-%d")
|
|
else:
|
|
dt = tanggal
|
|
grouped[(dt.month, dt.year)].append(ulasan_id)
|
|
except Exception as e:
|
|
log("WARNING", f"Skip id={ulasan_id} tanggal='{tanggal}': {e}")
|
|
|
|
# ── 3. Preview ───────────────────────────────────────────────────────────
|
|
log("INFO", "Rencana pemindahan:")
|
|
for (bulan, thn), ids in sorted(grouped.items()):
|
|
nama = f"{NAMA_BULAN[bulan - 1]} {thn}"
|
|
log("INFO", f" → {nama}: {len(ids)} ulasan")
|
|
|
|
if dry_run:
|
|
log("DRY-RUN", "Selesai preview. Jalankan tanpa --dry-run untuk terapkan perubahan.")
|
|
conn.close()
|
|
return
|
|
|
|
# ── 4. Terapkan pemindahan ───────────────────────────────────────────────
|
|
total_moved = 0
|
|
for (bulan, thn), ids in sorted(grouped.items()):
|
|
periode_id = get_or_create_periode(cursor, conn, bulan, thn, dry_run=False)
|
|
if periode_id is None:
|
|
continue
|
|
|
|
# Update per batch
|
|
placeholders = ",".join(["%s"] * len(ids))
|
|
if is_sqlite_connection(conn):
|
|
placeholders = ",".join(["?"] * len(ids))
|
|
|
|
cursor.execute(
|
|
f"UPDATE ulasan SET periode_id = %s, updated_at = NOW() WHERE id IN ({placeholders})".replace(
|
|
"%s", "?" if is_sqlite_connection(conn) else "%s"
|
|
),
|
|
[periode_id] + ids,
|
|
)
|
|
conn.commit()
|
|
|
|
nama = f"{NAMA_BULAN[bulan - 1]} {thn}"
|
|
log("OK", f"{nama}: {len(ids)} ulasan dipindahkan ke periode_id={periode_id}")
|
|
total_moved += len(ids)
|
|
|
|
log("OK", f"Selesai. Total {total_moved} ulasan berhasil dipindahkan ke periode yang benar.")
|
|
conn.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |