swiftcase-ai / app.py
Benny-Tang's picture
Update app.py
a8d585d verified
import gradio as gr
import sqlite3
import os
import datetime
DB_FILE = "cases.db"
# -----------------------------
# Database setup
# -----------------------------
def init_db():
conn = sqlite3.connect(DB_FILE)
c = conn.cursor()
# Cases table
c.execute('''CREATE TABLE IF NOT EXISTS summons_cases (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
address TEXT,
ticket_number TEXT UNIQUE,
vehicle_number TEXT,
road_name TEXT,
town_city TEXT,
state TEXT,
offence_en TEXT,
offence_bm TEXT,
offence_cn TEXT,
base_fine REAL,
status TEXT,
created_at TEXT,
updated_at TEXT
)''')
# Appeals table
c.execute('''CREATE TABLE IF NOT EXISTS appeals (
appeal_id INTEGER PRIMARY KEY AUTOINCREMENT,
ticket_number TEXT,
appeal_text TEXT,
appeal_to TEXT,
decision TEXT,
final_fine REAL,
created_at TEXT,
updated_at TEXT
)''')
conn.commit()
conn.close()
def seed_mock_data():
"""Seed Ali, Ah Kow, Muthu if DB empty"""
conn = sqlite3.connect(DB_FILE)
c = conn.cursor()
c.execute("SELECT COUNT(*) FROM summons_cases")
if c.fetchone()[0] == 0:
now = datetime.datetime.now().isoformat()
cases = [
("Ali", "12, Jalan Kenanga 3, Taman Seri, 75450 Melaka", "K250901234", "VDK8821",
"Jalan Lembongan", "Melaka", "Melaka Tengah",
"Not obeying correct lane usage",
"Tidak mematuhi lorong yang betul",
"未按规定车道行驶",
300.00, "ISSUED", now, now),
("Ah Kow", "88, Jalan Merdeka, 75000 Melaka", "K250901235", "MCX8899",
"Jalan Merdeka", "Melaka", "Melaka Tengah",
"Speeding over 120km/h in city zone",
"Memandu melebihi 120km/j di kawasan bandar",
"在市区超速驾驶超过120公里/小时",
250.00, "ISSUED", now, now),
("Muthu", "5, Jalan Bunga Raya, 11000 Pulau Pinang", "K250901236", "PGX5521",
"Jalan Bunga Raya", "George Town", "Pulau Pinang",
"Parking in a no-parking zone",
"Letak kenderaan di kawasan larangan",
"在禁止停车区停车",
100.00, "ISSUED", now, now)
]
c.executemany('''INSERT INTO summons_cases
(name, address, ticket_number, vehicle_number, road_name, town_city, state,
offence_en, offence_bm, offence_cn, base_fine, status, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''', cases)
conn.commit()
conn.close()
# -----------------------------
# DB Helper
# -----------------------------
def find_case(ticket_number, name, vehicle_number):
conn = sqlite3.connect(DB_FILE)
c = conn.cursor()
c.execute("""SELECT * FROM summons_cases
WHERE ticket_number=? OR name=? OR vehicle_number=?""",
(ticket_number, name, vehicle_number))
row = c.fetchone()
conn.close()
return row
# -----------------------------
# Citizen Portal
# -----------------------------
def create_case(name, address, ticket_number, vehicle_number, road_name, town_city, state,
offence_en, offence_bm, offence_cn, base_fine):
now = datetime.datetime.now().isoformat()
conn = sqlite3.connect(DB_FILE)
c = conn.cursor()
try:
c.execute('''INSERT INTO summons_cases
(name, address, ticket_number, vehicle_number, road_name, town_city, state,
offence_en, offence_bm, offence_cn, base_fine, status, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''',
(name, address, ticket_number, vehicle_number, road_name, town_city, state,
offence_en, offence_bm, offence_cn, base_fine, "ISSUED", now, now))
conn.commit()
return "✅ Case created successfully / Kes berjaya dicipta / 案件创建成功"
except sqlite3.IntegrityError:
return "❌ Case already exists / Kes sudah wujud / 案件已存在"
finally:
conn.close()
def view_case(ticket_number, name, vehicle_number):
row = find_case(ticket_number, name, vehicle_number)
if not row:
return "❌ Case not found / Kes tidak dijumpai / 未找到案件"
return (
f"Case / Kes / 案件 — Ticket: {row[3]}\n"
f"Name / Nama / 姓名: {row[1]}\n"
f"Address / Alamat / 地址: {row[2]}\n\n"
f"Vehicle No. / No. Kenderaan / 车牌号码: {row[4]}\n"
f"Road / Jalan / 道路: {row[5]}\n"
f"Town/City / Bandar / 城市: {row[6]}\n"
f"State / Negeri / 州: {row[7]}\n\n"
f"Offence (EN): {row[8]}\n"
f"Kesalahan (BM): {row[9]}\n"
f"违法 (中文): {row[10]}\n\n"
f"Base Fine / Denda Asal / 原始罚款: RM {row[11]:.2f}\n"
f"Status / Status / 状态: {row[12]}\n\n"
f"Created / Dicipta / 创建: {row[13]}\n"
f"Updated / Dikemaskini / 更新: {row[14]}"
)
def appeal_to_pdrm(ticket_number, appeal_text):
now = datetime.datetime.now().isoformat()
conn = sqlite3.connect(DB_FILE)
c = conn.cursor()
c.execute('''INSERT INTO appeals (ticket_number, appeal_text, appeal_to, decision, final_fine, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?)''',
(ticket_number, appeal_text, "PDRM", None, None, now, now))
conn.commit()
conn.close()
return "📨 Appeal submitted to PDRM / Rayuan dihantar ke PDRM / 上诉已提交给皇家警察"
def appeal_to_magistrate(ticket_number, appeal_text):
now = datetime.datetime.now().isoformat()
conn = sqlite3.connect(DB_FILE)
c = conn.cursor()
c.execute('''INSERT INTO appeals (ticket_number, appeal_text, appeal_to, decision, final_fine, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?)''',
(ticket_number, appeal_text, "MAGISTRATE", None, None, now, now))
conn.commit()
conn.close()
return "📨 Appeal submitted to Magistrate / Rayuan dihantar ke Majistret / 上诉已提交给地方法官"
def view_appeal_status(ticket_number):
conn = sqlite3.connect(DB_FILE)
c = conn.cursor()
c.execute("SELECT decision, final_fine FROM appeals WHERE ticket_number=? ORDER BY appeal_id DESC LIMIT 1", (ticket_number,))
row = c.fetchone()
decision, final_fine = None, None
# Predefined outcomes
if ticket_number == "K250901234": # Ali
decision, final_fine = "APPROVED_REDUCED", 150.00
elif ticket_number == "K250901235": # Ah Kow
decision, final_fine = "REJECTED", 250.00
elif ticket_number == "K250901236": # Muthu
decision, final_fine = "MAINTAINED", 100.00
if not row or not row[0]:
c.execute("UPDATE appeals SET decision=?, final_fine=? WHERE ticket_number=?",
(decision, final_fine, ticket_number))
conn.commit()
else:
decision, final_fine = row
conn.close()
decision_map = {
"APPROVED_REDUCED": "APPROVED_REDUCED / DILULUSKAN & DIKURANGKAN / 批准并减免",
"REJECTED": "REJECTED / DITOLAK / 驳回",
"MAINTAINED": "MAINTAINED / DIKEKALKAN / 维持原判"
}
decision_str = decision_map.get(decision, decision)
fine_str = "Pending / Menunggu / 待定" if final_fine is None else f"RM {final_fine:.2f}"
return (
f"Appeal Status / Status Rayuan / 上诉状态 — Ticket: {ticket_number}\n"
f"Decision / Keputusan / 决定: {decision_str}\n"
f"Final Fine / Denda Akhir / 最终罚款: {fine_str}"
)
def pay_fine(ticket_number):
return f"💳 Payment successful / Bayaran berjaya / 支付成功 — Ticket: {ticket_number}. Receipt issued / Resit dikeluarkan / 收据已发出."
# -----------------------------
# Gradio UI
# -----------------------------
def citizen_portal():
with gr.Tab("Citizen Portal / Portal Warganegara / 公民门户"):
with gr.Tab("Create Case / Cipta Kes / 创建案件"):
name = gr.Textbox(label="Name / Nama / 姓名")
address = gr.Textbox(label="Address / Alamat / 地址")
ticket_number = gr.Textbox(label="Ticket Number / Nombor Saman / 传票号码")
vehicle_number = gr.Textbox(label="Vehicle No. / No. Kenderaan / 车牌号码")
road_name = gr.Textbox(label="Road / Jalan / 道路")
town_city = gr.Textbox(label="Town/City / Bandar / 城市")
state = gr.Textbox(label="State / Negeri / 州")
offence_en = gr.Textbox(label="Offence (EN)")
offence_bm = gr.Textbox(label="Kesalahan (BM)")
offence_cn = gr.Textbox(label="违法 (中文)")
base_fine = gr.Number(label="Base Fine / Denda Asal / 原始罚款", value=300.00)
create_btn = gr.Button("Create / Cipta / 创建")
create_output = gr.Textbox(label="Result / Keputusan / 结果")
create_btn.click(create_case, [name, address, ticket_number, vehicle_number,
road_name, town_city, state,
offence_en, offence_bm, offence_cn, base_fine],
create_output)
with gr.Tab("View Case / Lihat Kes / 查看案件"):
vc_ticket = gr.Textbox(label="Ticket Number / Nombor Saman / 传票号码")
vc_name = gr.Textbox(label="Name / Nama / 姓名")
vc_vehicle = gr.Textbox(label="Vehicle No. / No. Kenderaan / 车牌号码")
view_btn = gr.Button("View / Lihat / 查看")
view_output = gr.Textbox(label="Case Details / Butiran Kes / 案件详情", lines=15)
view_btn.click(view_case, [vc_ticket, vc_name, vc_vehicle], view_output)
with gr.Tab("Submit Appeal / Hantar Rayuan / 提交上诉"):
sa_ticket = gr.Textbox(label="Ticket Number / Nombor Saman / 传票号码")
sa_text = gr.Textbox(label="Appeal Text / Teks Rayuan / 上诉内容")
sa_pdrm_btn = gr.Button("Appeal to PDRM / Rayuan ke PDRM / 向皇家警察上诉")
sa_mag_btn = gr.Button("Appeal to Magistrate / Rayuan ke Majistret / 向地方法官上诉")
sa_output = gr.Textbox(label="Result / Keputusan / 结果")
sa_pdrm_btn.click(appeal_to_pdrm, [sa_ticket, sa_text], sa_output)
sa_mag_btn.click(appeal_to_magistrate, [sa_ticket, sa_text], sa_output)
with gr.Tab("View Appeal Status / Lihat Status Rayuan / 查看上诉状态"):
vas_ticket = gr.Textbox(label="Ticket Number / Nombor Saman / 传票号码")
vas_btn = gr.Button("Check / Semak / 检查")
vas_output = gr.Textbox(label="Appeal Status / Status Rayuan / 上诉状态", lines=5)
vas_btn.click(view_appeal_status, [vas_ticket], vas_output)
with gr.Tab("Pay Fine / Bayar Denda / 缴纳罚款"):
pf_ticket = gr.Textbox(label="Ticket Number / Nombor Saman / 传票号码")
pf_btn = gr.Button("Pay / Bayar / 支付")
pf_output = gr.Textbox(label="Result / Keputusan / 结果")
pf_btn.click(pay_fine, [pf_ticket], pf_output)
# -----------------------------
# Launch
# -----------------------------
init_db()
seed_mock_data()
with gr.Blocks() as demo:
gr.Markdown("## 🚦 SwiftCase.ai — Smart Traffic Justice Platform")
citizen_portal()
if __name__ == "__main__":
demo.launch()