|
|
|
|
@ -17,6 +17,7 @@ MENU_CSV = "menu.csv"
|
|
|
|
|
PROMO_CSV = "promo.csv"
|
|
|
|
|
TRANSAKSI_CSV = "transaksi.csv"
|
|
|
|
|
DETAIL_TRANSAKSI_CSV = "detail_transaksi.csv"
|
|
|
|
|
FAVORITE_CSV = "favorite.csv"
|
|
|
|
|
IMG_PREVIEW_SIZE = (120, 80)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -61,6 +62,7 @@ def init_db_csv():
|
|
|
|
|
ensure_file(PROMO_CSV, ["code", "type", "value", "min_total"])
|
|
|
|
|
ensure_file(TRANSAKSI_CSV, ["id", "user_id", "nomor_meja", "total", "status", "promo_code", "subtotal", "item_discount", "promo_discount", "tanggal"])
|
|
|
|
|
ensure_file(DETAIL_TRANSAKSI_CSV, ["id", "transaksi_id", "menu_id", "qty", "harga_satuan", "subtotal_item"])
|
|
|
|
|
ensure_file(FAVORITE_CSV, ["user_id", "menu_id", "order_count", "last_ordered"])
|
|
|
|
|
|
|
|
|
|
seed_defaults()
|
|
|
|
|
|
|
|
|
|
@ -423,6 +425,9 @@ def transaksi_add(user_id, nomor_meja, cart_items, promo_code=None):
|
|
|
|
|
success, msg = menu_decrease_stock(item['menu_id'], qty)
|
|
|
|
|
if not success:
|
|
|
|
|
return False, f"Gagal mengurangi stok menu ID {item['menu_id']}: {msg}"
|
|
|
|
|
|
|
|
|
|
# Update favorite count
|
|
|
|
|
favorite_update(user_id, item['menu_id'])
|
|
|
|
|
|
|
|
|
|
write_all(DETAIL_TRANSAKSI_CSV, ["id", "transaksi_id", "menu_id", "qty", "harga_satuan", "subtotal_item"], detail_rows)
|
|
|
|
|
|
|
|
|
|
@ -700,6 +705,7 @@ class App:
|
|
|
|
|
self.tab_promo = ttk.Frame(main)
|
|
|
|
|
self.tab_order = ttk.Frame(main)
|
|
|
|
|
self.tab_waiter = ttk.Frame(main)
|
|
|
|
|
self.tab_favorite = ttk.Frame(main)
|
|
|
|
|
|
|
|
|
|
# Tab untuk semua role
|
|
|
|
|
main.add(self.tab_menu_view, text="Menu - View")
|
|
|
|
|
@ -708,6 +714,9 @@ class App:
|
|
|
|
|
if self.session['role'] in ['pembeli', 'admin', 'user']:
|
|
|
|
|
main.add(self.tab_order, text="Order Menu")
|
|
|
|
|
|
|
|
|
|
if self.session['role'] in ['pembeli', 'admin', 'user']:
|
|
|
|
|
main.add(self.tab_favorite, text="Favorit Saya")
|
|
|
|
|
|
|
|
|
|
# Tab khusus waiter
|
|
|
|
|
if self.session['role'] in ['waiter', 'admin']:
|
|
|
|
|
main.add(self.tab_waiter, text="Waiter - Pesanan")
|
|
|
|
|
@ -723,6 +732,8 @@ class App:
|
|
|
|
|
|
|
|
|
|
if self.session['role'] in ['pembeli', 'admin', 'user']:
|
|
|
|
|
self.build_order_tab(self.tab_order)
|
|
|
|
|
if self.session['role'] in ['pembeli', 'admin', 'user']:
|
|
|
|
|
self.build_favorite_tab(self.tab_favorite)
|
|
|
|
|
|
|
|
|
|
if self.session['role'] in ['waiter', 'admin']:
|
|
|
|
|
self.build_waiter_tab(self.tab_waiter)
|
|
|
|
|
@ -1305,7 +1316,7 @@ class App:
|
|
|
|
|
if col >= 2:
|
|
|
|
|
col = 0
|
|
|
|
|
row += 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def reset_order_search(self):
|
|
|
|
|
self.order_search_var.set("")
|
|
|
|
|
self.reload_order_menu_cards()
|
|
|
|
|
@ -1350,6 +1361,286 @@ class App:
|
|
|
|
|
self.reload_order_menu_cards()
|
|
|
|
|
self.update_cart_display()
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Wilayah dikuasai Favorite
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def build_favorite_tab(self, parent):
|
|
|
|
|
"""Tab untuk melihat menu favorit dan history pesanan"""
|
|
|
|
|
for w in parent.winfo_children():
|
|
|
|
|
w.destroy()
|
|
|
|
|
|
|
|
|
|
# Header
|
|
|
|
|
header = ttk.Frame(parent)
|
|
|
|
|
header.pack(fill='x', padx=10, pady=8)
|
|
|
|
|
ttk.Label(header, text="🌟 Menu Favorit & History", font=("Arial", 14, "bold")).pack(side='left')
|
|
|
|
|
ttk.Button(header, text="🔄 Refresh", command=self.reload_favorite_tab).pack(side='right', padx=6)
|
|
|
|
|
|
|
|
|
|
# Split 2 panel: kiri = favorit, kanan = history
|
|
|
|
|
left = ttk.LabelFrame(parent, text="⭐ Menu Favorit Saya (Top 5)", padding=10)
|
|
|
|
|
left.pack(side='left', fill='both', expand=True, padx=10, pady=6)
|
|
|
|
|
|
|
|
|
|
right = ttk.LabelFrame(parent, text="📜 History Pesanan Terakhir", padding=10)
|
|
|
|
|
right.pack(side='right', fill='both', expand=True, padx=10, pady=6)
|
|
|
|
|
|
|
|
|
|
# === PANEL KIRI: Menu Favorit ===
|
|
|
|
|
|
|
|
|
|
# Treeview favorit
|
|
|
|
|
fav_cols = ("Rank", "Menu", "Kategori", "Harga", "Dipesan", "Terakhir")
|
|
|
|
|
self.favorite_tree = ttk.Treeview(left, columns=fav_cols, show='headings', height=12)
|
|
|
|
|
|
|
|
|
|
self.favorite_tree.heading("Rank", text="#")
|
|
|
|
|
self.favorite_tree.heading("Menu", text="Menu")
|
|
|
|
|
self.favorite_tree.heading("Kategori", text="Kategori")
|
|
|
|
|
self.favorite_tree.heading("Harga", text="Harga")
|
|
|
|
|
self.favorite_tree.heading("Dipesan", text="Dipesan")
|
|
|
|
|
self.favorite_tree.heading("Terakhir", text="Terakhir")
|
|
|
|
|
|
|
|
|
|
self.favorite_tree.column("Rank", width=40)
|
|
|
|
|
self.favorite_tree.column("Menu", width=150)
|
|
|
|
|
self.favorite_tree.column("Kategori", width=90)
|
|
|
|
|
self.favorite_tree.column("Harga", width=80)
|
|
|
|
|
self.favorite_tree.column("Dipesan", width=70)
|
|
|
|
|
self.favorite_tree.column("Terakhir", width=140)
|
|
|
|
|
|
|
|
|
|
self.favorite_tree.pack(fill='both', expand=True, pady=6)
|
|
|
|
|
|
|
|
|
|
# Tombol quick order
|
|
|
|
|
fav_btn_frame = ttk.Frame(left)
|
|
|
|
|
fav_btn_frame.pack(pady=6)
|
|
|
|
|
ttk.Label(fav_btn_frame, text="Quick Order:").pack(side='left', padx=6)
|
|
|
|
|
ttk.Button(fav_btn_frame, text="🛒 Pesan Lagi", command=self.quick_order_favorite).pack(side='left', padx=3)
|
|
|
|
|
|
|
|
|
|
# === PANEL KANAN: History Transaksi ===
|
|
|
|
|
|
|
|
|
|
# Treeview history
|
|
|
|
|
hist_cols = ("ID", "Tanggal", "Meja", "Total", "Status")
|
|
|
|
|
self.history_tree = ttk.Treeview(right, columns=hist_cols, show='headings', height=12)
|
|
|
|
|
|
|
|
|
|
self.history_tree.heading("ID", text="ID")
|
|
|
|
|
self.history_tree.heading("Tanggal", text="Tanggal")
|
|
|
|
|
self.history_tree.heading("Meja", text="Meja")
|
|
|
|
|
self.history_tree.heading("Total", text="Total")
|
|
|
|
|
self.history_tree.heading("Status", text="Status")
|
|
|
|
|
|
|
|
|
|
self.history_tree.column("ID", width=40)
|
|
|
|
|
self.history_tree.column("Tanggal", width=140)
|
|
|
|
|
self.history_tree.column("Meja", width=60)
|
|
|
|
|
self.history_tree.column("Total", width=100)
|
|
|
|
|
self.history_tree.column("Status", width=90)
|
|
|
|
|
|
|
|
|
|
self.history_tree.pack(fill='both', expand=True, pady=6)
|
|
|
|
|
|
|
|
|
|
# Bind event untuk lihat detail
|
|
|
|
|
self.history_tree.bind("<<TreeviewSelect>>", self.on_history_select)
|
|
|
|
|
|
|
|
|
|
# Detail history
|
|
|
|
|
detail_frame = ttk.Frame(right)
|
|
|
|
|
detail_frame.pack(fill='x', pady=6)
|
|
|
|
|
|
|
|
|
|
self.history_detail_text = tk.Text(detail_frame, height=8, font=("Courier New", 8), wrap='word')
|
|
|
|
|
hist_scroll = ttk.Scrollbar(detail_frame, orient='vertical', command=self.history_detail_text.yview)
|
|
|
|
|
self.history_detail_text.configure(yscrollcommand=hist_scroll.set)
|
|
|
|
|
|
|
|
|
|
self.history_detail_text.pack(side='left', fill='both', expand=True)
|
|
|
|
|
hist_scroll.pack(side='right', fill='y')
|
|
|
|
|
|
|
|
|
|
# Tombol history action
|
|
|
|
|
hist_btn_frame = ttk.Frame(right)
|
|
|
|
|
hist_btn_frame.pack(pady=6)
|
|
|
|
|
ttk.Button(hist_btn_frame, text="🔁 Pesan Ulang", command=self.reorder_from_history).pack(side='left', padx=3)
|
|
|
|
|
|
|
|
|
|
# Load data
|
|
|
|
|
self.reload_favorite_tab()
|
|
|
|
|
|
|
|
|
|
def reload_favorite_tab(self):
|
|
|
|
|
"""Load data favorit dan history"""
|
|
|
|
|
# Clear trees
|
|
|
|
|
for r in self.favorite_tree.get_children():
|
|
|
|
|
self.favorite_tree.delete(r)
|
|
|
|
|
for r in self.history_tree.get_children():
|
|
|
|
|
self.history_tree.delete(r)
|
|
|
|
|
|
|
|
|
|
# Load favorit
|
|
|
|
|
favorites = favorite_list(self.session['id'], limit=5)
|
|
|
|
|
rank = 1
|
|
|
|
|
for fav in favorites:
|
|
|
|
|
menu_id, count, last_ordered = fav
|
|
|
|
|
menu_data = menu_get(menu_id)
|
|
|
|
|
if not menu_data:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
_, nama, kategori, harga, stok, foto, tersedia, item_disc = menu_data
|
|
|
|
|
|
|
|
|
|
self.favorite_tree.insert("", tk.END, values=(
|
|
|
|
|
rank,
|
|
|
|
|
nama,
|
|
|
|
|
kategori,
|
|
|
|
|
f"Rp {harga:,.0f}",
|
|
|
|
|
f"{count}x",
|
|
|
|
|
last_ordered
|
|
|
|
|
))
|
|
|
|
|
rank += 1
|
|
|
|
|
|
|
|
|
|
# Load history transaksi
|
|
|
|
|
history = transaksi_list(user_id=self.session['id'])
|
|
|
|
|
for h in history:
|
|
|
|
|
tid, uid, meja, total, status, promo_code, tanggal = h
|
|
|
|
|
self.history_tree.insert("", tk.END, values=(
|
|
|
|
|
tid,
|
|
|
|
|
tanggal,
|
|
|
|
|
meja,
|
|
|
|
|
f"Rp {total:,.0f}",
|
|
|
|
|
status
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
def on_history_select(self, event):
|
|
|
|
|
"""Tampilkan detail history saat dipilih"""
|
|
|
|
|
sel = self.history_tree.selection()
|
|
|
|
|
if not sel:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
item = self.history_tree.item(sel)['values']
|
|
|
|
|
transaksi_id = item[0]
|
|
|
|
|
|
|
|
|
|
# Get detail transaksi
|
|
|
|
|
transaksi_data = transaksi_get(transaksi_id)
|
|
|
|
|
if not transaksi_data:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
tid, uid, meja, total, status, promo_code, subtotal, item_disc, promo_disc, tanggal = transaksi_data
|
|
|
|
|
detail_items = detail_transaksi_list(transaksi_id)
|
|
|
|
|
|
|
|
|
|
# Format detail
|
|
|
|
|
detail_text = f"TRANSAKSI #{tid} - {status.upper()}\n"
|
|
|
|
|
detail_text += f"{'='*40}\n"
|
|
|
|
|
detail_text += f"Tanggal: {tanggal}\n"
|
|
|
|
|
detail_text += f"Meja: {meja}\n\n"
|
|
|
|
|
detail_text += f"Item Pesanan:\n"
|
|
|
|
|
detail_text += f"{'-'*40}\n"
|
|
|
|
|
|
|
|
|
|
for detail in detail_items:
|
|
|
|
|
did, mid, qty, harga, subtotal_item = detail
|
|
|
|
|
menu_data = menu_get(mid)
|
|
|
|
|
if menu_data:
|
|
|
|
|
_, nama, kategori, _, _, _, _, _ = menu_data
|
|
|
|
|
detail_text += f"• {nama}\n"
|
|
|
|
|
detail_text += f" {qty} x Rp {harga:,.0f} = Rp {subtotal_item:,.0f}\n"
|
|
|
|
|
|
|
|
|
|
detail_text += f"{'-'*40}\n"
|
|
|
|
|
detail_text += f"Subtotal: Rp {subtotal:,.0f}\n"
|
|
|
|
|
detail_text += f"Diskon: Rp {item_disc + promo_disc:,.0f}\n"
|
|
|
|
|
detail_text += f"TOTAL: Rp {total:,.0f}\n"
|
|
|
|
|
|
|
|
|
|
self.history_detail_text.delete('1.0', tk.END)
|
|
|
|
|
self.history_detail_text.insert('1.0', detail_text)
|
|
|
|
|
|
|
|
|
|
def quick_order_favorite(self):
|
|
|
|
|
"""Pesan ulang dari menu favorit yang dipilih"""
|
|
|
|
|
sel = self.favorite_tree.selection()
|
|
|
|
|
if not sel:
|
|
|
|
|
messagebox.showwarning("Pilih Menu", "Pilih menu favorit terlebih dahulu")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
item = self.favorite_tree.item(sel)['values']
|
|
|
|
|
menu_name = item[1]
|
|
|
|
|
|
|
|
|
|
# Cari menu_id dari nama
|
|
|
|
|
all_menus = menu_list()
|
|
|
|
|
menu_id = None
|
|
|
|
|
for m in all_menus:
|
|
|
|
|
if m[1] == menu_name:
|
|
|
|
|
menu_id = m[0]
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
if not menu_id:
|
|
|
|
|
messagebox.showerror("Error", "Menu tidak ditemukan")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Tambah ke cart (qty 1)
|
|
|
|
|
menu_data = menu_get(menu_id)
|
|
|
|
|
if not menu_data:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
_, nama, kategori, harga, stok, foto, tersedia, item_disc = menu_data
|
|
|
|
|
|
|
|
|
|
if stok < 1:
|
|
|
|
|
messagebox.showwarning("Stok Habis", f"Stok {nama} habis")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Cek apakah sudah ada di cart
|
|
|
|
|
found = False
|
|
|
|
|
for cart_item in self.cart_items:
|
|
|
|
|
if cart_item['menu_id'] == menu_id:
|
|
|
|
|
if cart_item['qty'] < stok:
|
|
|
|
|
cart_item['qty'] += 1
|
|
|
|
|
found = True
|
|
|
|
|
else:
|
|
|
|
|
messagebox.showwarning("Stok Habis", f"Stok {nama} hanya {stok}")
|
|
|
|
|
return
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
if not found:
|
|
|
|
|
self.cart_items.append({'menu_id': menu_id, 'qty': 1})
|
|
|
|
|
|
|
|
|
|
messagebox.showinfo("Ditambahkan", f"{nama} ditambahkan ke keranjang!\n\nSilakan ke tab 'Order Menu' untuk checkout.")
|
|
|
|
|
|
|
|
|
|
def reorder_from_history(self):
|
|
|
|
|
"""Pesan ulang semua item dari history yang dipilih"""
|
|
|
|
|
sel = self.history_tree.selection()
|
|
|
|
|
if not sel:
|
|
|
|
|
messagebox.showwarning("Pilih History", "Pilih history pesanan terlebih dahulu")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
item = self.history_tree.item(sel)['values']
|
|
|
|
|
transaksi_id = item[0]
|
|
|
|
|
|
|
|
|
|
# Get detail items
|
|
|
|
|
detail_items = detail_transaksi_list(transaksi_id)
|
|
|
|
|
|
|
|
|
|
if not detail_items:
|
|
|
|
|
messagebox.showerror("Error", "Tidak ada detail pesanan")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Tambah semua item ke cart
|
|
|
|
|
added_count = 0
|
|
|
|
|
for detail in detail_items:
|
|
|
|
|
did, mid, qty, harga, subtotal_item = detail
|
|
|
|
|
|
|
|
|
|
# Cek stok
|
|
|
|
|
menu_data = menu_get(mid)
|
|
|
|
|
if not menu_data:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
_, nama, kategori, harga_now, stok, foto, tersedia, item_disc = menu_data
|
|
|
|
|
|
|
|
|
|
if stok < qty:
|
|
|
|
|
messagebox.showwarning("Stok Kurang", f"Stok {nama} hanya {stok}, pesanan asli {qty}")
|
|
|
|
|
qty = stok
|
|
|
|
|
|
|
|
|
|
if qty <= 0:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
# Tambah ke cart
|
|
|
|
|
found = False
|
|
|
|
|
for cart_item in self.cart_items:
|
|
|
|
|
if cart_item['menu_id'] == mid:
|
|
|
|
|
new_qty = cart_item['qty'] + qty
|
|
|
|
|
if new_qty <= stok:
|
|
|
|
|
cart_item['qty'] = new_qty
|
|
|
|
|
found = True
|
|
|
|
|
added_count += 1
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
if not found:
|
|
|
|
|
self.cart_items.append({'menu_id': mid, 'qty': qty})
|
|
|
|
|
added_count += 1
|
|
|
|
|
|
|
|
|
|
if added_count > 0:
|
|
|
|
|
messagebox.showinfo("Berhasil", f"{added_count} item ditambahkan ke keranjang!\n\nSilakan ke tab 'Order Menu' untuk checkout.")
|
|
|
|
|
else:
|
|
|
|
|
messagebox.showwarning("Gagal", "Tidak ada item yang bisa ditambahkan (stok habis)")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def update_cart_display(self):
|
|
|
|
|
"""Update tampilan keranjang dan hitung total"""
|
|
|
|
|
@ -1706,6 +1997,102 @@ class App:
|
|
|
|
|
else:
|
|
|
|
|
messagebox.showerror("❌ Gagal", "Gagal mengubah status pesanan")
|
|
|
|
|
|
|
|
|
|
# Wilayah dikuasai Favorite
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def favorite_update(user_id, menu_id):
|
|
|
|
|
"""Update atau tambah favorite count untuk user tertentu"""
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
rows = read_all(FAVORITE_CSV)
|
|
|
|
|
found = False
|
|
|
|
|
|
|
|
|
|
for r in rows:
|
|
|
|
|
if r.get("user_id") == str(user_id) and r.get("menu_id") == str(menu_id):
|
|
|
|
|
# Update count
|
|
|
|
|
try:
|
|
|
|
|
count = int(r.get("order_count") or 0)
|
|
|
|
|
except:
|
|
|
|
|
count = 0
|
|
|
|
|
r["order_count"] = str(count + 1)
|
|
|
|
|
r["last_ordered"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
|
found = True
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
if not found:
|
|
|
|
|
# Tambah baru
|
|
|
|
|
rows.append({
|
|
|
|
|
"user_id": str(user_id),
|
|
|
|
|
"menu_id": str(menu_id),
|
|
|
|
|
"order_count": "1",
|
|
|
|
|
"last_ordered": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
write_all(FAVORITE_CSV, ["user_id", "menu_id", "order_count", "last_ordered"], rows)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def favorite_list(user_id, limit=5):
|
|
|
|
|
"""Ambil menu favorit user, sorted by order_count descending"""
|
|
|
|
|
rows = read_all(FAVORITE_CSV)
|
|
|
|
|
out = []
|
|
|
|
|
|
|
|
|
|
for r in rows:
|
|
|
|
|
if r.get("user_id") == str(user_id):
|
|
|
|
|
try:
|
|
|
|
|
mid = int(r.get("menu_id") or 0)
|
|
|
|
|
except:
|
|
|
|
|
mid = r.get("menu_id")
|
|
|
|
|
try:
|
|
|
|
|
count = int(r.get("order_count") or 0)
|
|
|
|
|
except:
|
|
|
|
|
count = 0
|
|
|
|
|
|
|
|
|
|
last_ordered = r.get("last_ordered")
|
|
|
|
|
out.append((mid, count, last_ordered))
|
|
|
|
|
|
|
|
|
|
# Sort by count descending
|
|
|
|
|
out.sort(key=lambda x: x[1], reverse=True)
|
|
|
|
|
|
|
|
|
|
# Limit results
|
|
|
|
|
if limit:
|
|
|
|
|
out = out[:limit]
|
|
|
|
|
|
|
|
|
|
return out
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def favorite_all(limit=10):
|
|
|
|
|
"""Ambil menu paling populer dari semua user"""
|
|
|
|
|
rows = read_all(FAVORITE_CSV)
|
|
|
|
|
menu_counts = {}
|
|
|
|
|
|
|
|
|
|
for r in rows:
|
|
|
|
|
menu_id = r.get("menu_id")
|
|
|
|
|
try:
|
|
|
|
|
count = int(r.get("order_count") or 0)
|
|
|
|
|
except:
|
|
|
|
|
count = 0
|
|
|
|
|
|
|
|
|
|
if menu_id in menu_counts:
|
|
|
|
|
menu_counts[menu_id] += count
|
|
|
|
|
else:
|
|
|
|
|
menu_counts[menu_id] = count
|
|
|
|
|
|
|
|
|
|
# Convert to list dan sort
|
|
|
|
|
out = []
|
|
|
|
|
for menu_id, total_count in menu_counts.items():
|
|
|
|
|
try:
|
|
|
|
|
mid = int(menu_id)
|
|
|
|
|
except:
|
|
|
|
|
mid = menu_id
|
|
|
|
|
out.append((mid, total_count))
|
|
|
|
|
|
|
|
|
|
out.sort(key=lambda x: x[1], reverse=True)
|
|
|
|
|
|
|
|
|
|
if limit:
|
|
|
|
|
out = out[:limit]
|
|
|
|
|
|
|
|
|
|
return out
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|