add edoxid calendar sync dag

Sync jadwal sidang eDOXID ke Google Calendar "Jadwal Sidang" tiap 5 menit
(read-only ke doxid2022), plus reminder email H-1/1-jam via Gmail API
karena reminder Calendar bawaan tidak ikut ke kalender pribadi attendee.
This commit is contained in:
Power BI Dev
2026-07-27 21:59:27 +07:00
parent c6edee7aeb
commit 33f4666bad
9 changed files with 658 additions and 0 deletions
+93
View File
@@ -0,0 +1,93 @@
from datetime import date, datetime
import pymysql
import pymysql.cursors
from scripts.edoxid_calendar_config import DbConfig
class Database:
"""Koneksi READ-ONLY ke doxid2022 (DB aplikasi eDOXID) pakai user
`calendar_sync_ro` yang cuma punya grant SELECT ke submissions/verificators/users.
Tidak pernah menulis apa pun ke DB ini."""
def __init__(self, config: DbConfig):
self._conn = pymysql.connect(
host=config.host,
port=config.port,
user=config.username,
password=config.password,
database=config.database,
cursorclass=pymysql.cursors.DictCursor,
autocommit=True,
)
def close(self) -> None:
self._conn.close()
def get_scheduled_submissions(self, min_date: str = "today") -> list[dict]:
"""Submission yang sudah punya jadwal sidang (schedule IS NOT NULL),
dibatasi >= min_date supaya tidak menyapu semua histori lama.
min_date: 'today' atau string 'YYYY-MM-DD'.
"""
cutoff = date.today() if min_date == "today" else datetime.strptime(min_date, "%Y-%m-%d").date()
with self._conn.cursor() as cur:
cur.execute(
"""
SELECT s.id, s.schedule, s.room, s.title, s.type_id,
u.name AS student_name, u.email AS student_email, u.unique_id AS student_nim
FROM submissions s
JOIN users u ON u.id = s.student_id
WHERE s.schedule IS NOT NULL
AND s.schedule >= %s
""",
(cutoff,),
)
submissions = cur.fetchall()
if not submissions:
return []
ids = [s["id"] for s in submissions]
placeholders = ",".join(["%s"] * len(ids))
# Diurutkan v.id supaya "Pembimbing 1/2" & "Penguji 1/2" konsisten
# dengan urutan input aslinya.
with self._conn.cursor() as cur:
cur.execute(
f"""
SELECT v.submission_id, v.type, u.name, u.email
FROM verificators v
JOIN users u ON u.id = v.user_id
WHERE v.submission_id IN ({placeholders})
AND v.type IN ('Pembimbing', 'Penguji')
AND u.email IS NOT NULL AND u.email != ''
ORDER BY v.id
""",
ids,
)
verificator_rows = cur.fetchall()
verificators_by_submission: dict[int, list[dict]] = {}
for row in verificator_rows:
verificators_by_submission.setdefault(row["submission_id"], []).append(
{"type": row["type"], "name": row["name"], "email": row["email"]}
)
for submission in submissions:
submission["verificators"] = verificators_by_submission.get(submission["id"], [])
return submissions
def get_ids_with_active_schedule(self) -> set[int]:
"""ID semua submission yang MASIH punya schedule (tanpa filter tanggal).
Dipakai KHUSUS untuk deteksi orphan/cancelled — supaya sidang yang
tanggalnya sudah lewat (tapi memang belum dibatalkan) tidak salah
dianggap "cancelled" dan event Calendar-nya ikut terhapus, gara-gara
filter min_date di get_scheduled_submissions() menyisihkannya.
"""
with self._conn.cursor() as cur:
cur.execute("SELECT id FROM submissions WHERE schedule IS NOT NULL")
return {row["id"] for row in cur.fetchall()}