Skip to content

Commit cac92c6

Browse files
authored
Create Real-Time-Bidding-System.py
1 parent 24e9d4c commit cac92c6

1 file changed

Lines changed: 303 additions & 0 deletions

File tree

Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
import threading
2+
import tkinter as tk
3+
from tkinter import messagebox
4+
from dataclasses import dataclass, field
5+
from typing import List, Dict, Optional
6+
import time
7+
import random
8+
9+
import ttkbootstrap as tb
10+
from ttkbootstrap.constants import *
11+
from ttkbootstrap.widgets.scrolled import ScrolledText
12+
13+
# ---------------- CONFIG ---------------- #
14+
RESULTS_PER_PAGE = 6
15+
BID_REFRESH_INTERVAL = 2 # seconds
16+
17+
# ---------------- GLOBAL STATE ---------------- #
18+
all_auctions: List["AuctionItem"] = []
19+
current_page = 1
20+
bidding_active_ids: Dict[int, bool] = {} # Track auto-bidding per item
21+
22+
# ---------------- DATA STRUCTURE ---------------- #
23+
@dataclass
24+
class AuctionItem:
25+
item_id: int
26+
name: str
27+
description: str
28+
starting_bid: float
29+
current_bid: float = 0.0
30+
highest_bidder: Optional[str] = None
31+
bids: List[Dict[str, float]] = field(default_factory=list)
32+
33+
# ---------------- AUCTION LOGIC ---------------- #
34+
def place_bid(item: AuctionItem, bidder: str, amount: float):
35+
if amount <= item.current_bid:
36+
messagebox.showwarning("Invalid Bid", f"Your bid must be higher than current bid (${item.current_bid:.2f})")
37+
return False
38+
item.current_bid = round(amount, 2)
39+
item.highest_bidder = bidder
40+
item.bids.append({"bidder": bidder, "amount": item.current_bid})
41+
return True
42+
43+
def simulate_bid_for_item(item: AuctionItem):
44+
"""Simulate auto-bidding for a specific item ID."""
45+
while bidding_active_ids.get(item.item_id, False):
46+
time.sleep(BID_REFRESH_INTERVAL)
47+
bid_increment = round(random.uniform(1, 20), 2)
48+
bidder = random.choice(["Alice", "Bob", "Charlie", "Dave"])
49+
new_bid = item.current_bid + bid_increment
50+
place_bid(item, bidder, new_bid)
51+
app.after(0, display_page)
52+
53+
def start_all_bidding():
54+
"""Start auto-bidding for all items."""
55+
for item in all_auctions:
56+
if not bidding_active_ids[item.item_id]:
57+
bidding_active_ids[item.item_id] = True
58+
threading.Thread(target=simulate_bid_for_item, args=(item,), daemon=True).start()
59+
60+
def stop_all_bidding():
61+
"""Stop auto-bidding for all items."""
62+
for item in all_auctions:
63+
bidding_active_ids[item.item_id] = False
64+
65+
# ---------------- UI HELPERS ---------------- #
66+
def display_page():
67+
text.configure(state="normal")
68+
text.delete("1.0", "end")
69+
70+
start = (current_page - 1) * RESULTS_PER_PAGE
71+
end = min(start + RESULTS_PER_PAGE, len(all_auctions))
72+
page_results = all_auctions[start:end]
73+
74+
if not page_results:
75+
text.insert("end", "No auctions available.\n")
76+
text.configure(state="disabled")
77+
update_pagination()
78+
return
79+
80+
for item in page_results:
81+
idx = item.item_id # Global numbering
82+
text.insert("end", f"{idx}. {item.name}\n", f"title_{idx}")
83+
text.tag_config(f"title_{idx}", foreground="#1a0dab", font=("Segoe UI", 14, "bold"))
84+
85+
text.insert("end", f" Starting Bid: ${item.starting_bid:.2f}\n", f"start_{idx}")
86+
text.tag_config(f"start_{idx}", foreground="#006621", font=("Segoe UI", 10))
87+
88+
text.insert("end", f" Current Bid: ${item.current_bid:.2f} | Highest Bidder: {item.highest_bidder or 'None'}\n", f"current_{idx}")
89+
text.tag_config(f"current_{idx}", foreground="#D2691E", font=("Segoe UI", 10, "italic"))
90+
91+
text.insert("end", f" Description: {item.description}\n\n")
92+
text.configure(state="disabled")
93+
update_pagination()
94+
95+
def next_page():
96+
global current_page
97+
if current_page * RESULTS_PER_PAGE < len(all_auctions):
98+
current_page += 1
99+
display_page()
100+
101+
def prev_page():
102+
global current_page
103+
if current_page > 1:
104+
current_page -= 1
105+
display_page()
106+
107+
def update_pagination():
108+
total_pages = max(1, (len(all_auctions) - 1) // RESULTS_PER_PAGE + 1)
109+
page_label.config(text=f"Page {current_page} of {total_pages}")
110+
prev_btn.config(state=DISABLED if current_page == 1 else NORMAL)
111+
next_btn.config(state=DISABLED if current_page == total_pages else NORMAL)
112+
113+
# Spinbox max = number of items on current page
114+
start = (current_page - 1) * RESULTS_PER_PAGE
115+
end = min(start + RESULTS_PER_PAGE, len(all_auctions))
116+
item_index_spin.config(to=max(1, end - start))
117+
118+
# ---------------- AUCTION SETUP ---------------- #
119+
def add_auction_item():
120+
name = item_name_entry.get().strip()
121+
desc = item_desc_entry.get().strip()
122+
try:
123+
start_bid = float(item_start_bid_entry.get().strip())
124+
except ValueError:
125+
messagebox.showwarning("Invalid Input", "Starting bid must be a number.")
126+
return
127+
if not name or name == "Item Name":
128+
messagebox.showwarning("Input Required", "Enter an item name.")
129+
return
130+
131+
item = AuctionItem(
132+
item_id=len(all_auctions)+1, # Global numbering
133+
name=name,
134+
description=desc,
135+
starting_bid=start_bid,
136+
current_bid=start_bid
137+
)
138+
all_auctions.append(item)
139+
bidding_active_ids[item.item_id] = False # Default no auto-bidding
140+
display_page()
141+
# Reset entries
142+
reset_entry(item_name_entry, "Item Name")
143+
reset_entry(item_desc_entry, "Item Description")
144+
reset_entry(item_start_bid_entry, "Starting Bid")
145+
146+
def manual_bid():
147+
try:
148+
bid_amount = float(bid_amount_entry.get().strip())
149+
except ValueError:
150+
messagebox.showwarning("Invalid Input", "Bid amount must be a number.")
151+
return
152+
153+
bidder = bidder_name_entry.get().strip()
154+
if not bidder or bidder == "Your Name":
155+
messagebox.showwarning("Input Required", "Enter your name.")
156+
return
157+
158+
# Convert page-local index to global index
159+
try:
160+
page_local_idx = int(item_index_spin.get()) - 1
161+
global_idx = (current_page - 1) * RESULTS_PER_PAGE + page_local_idx
162+
except ValueError:
163+
messagebox.showwarning("Invalid Input", "Item index must be a number.")
164+
return
165+
166+
if global_idx < 0 or global_idx >= len(all_auctions):
167+
messagebox.showwarning("Invalid Selection", "Select a valid item index.")
168+
return
169+
170+
success = place_bid(all_auctions[global_idx], bidder, bid_amount)
171+
if success:
172+
display_page()
173+
reset_entry(bid_amount_entry, "Bid Amount")
174+
175+
# ---------------- AUTO-BIDDING BY ITEM ---------------- #
176+
def start_bidding_for_item():
177+
try:
178+
bid_item_id = int(auto_bid_id_entry.get().strip())
179+
except ValueError:
180+
messagebox.showwarning("Invalid Input", "Item ID must be a number.")
181+
return
182+
183+
# Find the item
184+
item = next((i for i in all_auctions if i.item_id == bid_item_id), None)
185+
if not item:
186+
messagebox.showwarning("Invalid ID", "No item with this ID exists.")
187+
return
188+
189+
if not bidding_active_ids[item.item_id]:
190+
bidding_active_ids[item.item_id] = True
191+
threading.Thread(target=simulate_bid_for_item, args=(item,), daemon=True).start()
192+
193+
def stop_bidding_for_item():
194+
try:
195+
bid_item_id = int(auto_bid_id_entry.get().strip())
196+
except ValueError:
197+
messagebox.showwarning("Invalid Input", "Item ID must be a number.")
198+
return
199+
200+
if bid_item_id in bidding_active_ids:
201+
bidding_active_ids[bid_item_id] = False
202+
203+
# ---------------- PLACEHOLDER UTIL ---------------- #
204+
def add_placeholder(entry: tb.Entry, placeholder_text: str):
205+
entry.insert(0, placeholder_text)
206+
entry.config(foreground="grey")
207+
208+
def on_focus_in(event):
209+
if entry.get() == placeholder_text:
210+
entry.delete(0, "end")
211+
entry.config(foreground="black")
212+
213+
def on_focus_out(event):
214+
if not entry.get():
215+
entry.insert(0, placeholder_text)
216+
entry.config(foreground="grey")
217+
218+
entry.bind("<FocusIn>", on_focus_in)
219+
entry.bind("<FocusOut>", on_focus_out)
220+
221+
def reset_entry(entry: tb.Entry, placeholder_text: str):
222+
entry.delete(0, "end")
223+
entry.insert(0, placeholder_text)
224+
entry.config(foreground="grey")
225+
226+
# ---------------- UI SETUP ---------------- #
227+
app = tb.Window(title="Real-Time Bidding System", themename="flatly", size=(1000, 720), resizable=(True, True))
228+
229+
# Top frame - Add auction items
230+
top = tb.Frame(app, padding=15)
231+
top.pack(fill=tk.X)
232+
tb.Label(top, text="Add Auction Item", font=("Segoe UI", 16, "bold")).pack(anchor=tk.W)
233+
234+
item_name_entry = tb.Entry(top, font=("Segoe UI", 12))
235+
item_name_entry.pack(fill=tk.X, pady=2)
236+
add_placeholder(item_name_entry, "Item Name")
237+
238+
item_desc_entry = tb.Entry(top, font=("Segoe UI", 12))
239+
item_desc_entry.pack(fill=tk.X, pady=2)
240+
add_placeholder(item_desc_entry, "Item Description")
241+
242+
item_start_bid_entry = tb.Entry(top, font=("Segoe UI", 12))
243+
item_start_bid_entry.pack(fill=tk.X, pady=2)
244+
add_placeholder(item_start_bid_entry, "Starting Bid")
245+
246+
tb.Button(top, text="Add Item", bootstyle="success", command=add_auction_item).pack(anchor=tk.E, pady=5)
247+
248+
# Results frame
249+
result_frame = tb.Frame(app, padding=(15, 5))
250+
result_frame.pack(fill=tk.BOTH, expand=True)
251+
result_box = ScrolledText(result_frame, autohide=True)
252+
result_box.pack(fill=tk.BOTH, expand=True)
253+
text = result_box.text
254+
text.configure(state="disabled", wrap="word")
255+
256+
# Manual bid frame
257+
bid_frame = tb.Frame(app, padding=10)
258+
bid_frame.pack(fill=tk.X)
259+
260+
tb.Label(bid_frame, text="Bidder Name:").pack(side=tk.LEFT)
261+
bidder_name_entry = tb.Entry(bid_frame, width=12)
262+
bidder_name_entry.pack(side=tk.LEFT, padx=5)
263+
add_placeholder(bidder_name_entry, "Your Name")
264+
265+
tb.Label(bid_frame, text="Item Index:").pack(side=tk.LEFT)
266+
item_index_spin = tb.Spinbox(bid_frame, from_=1, to=RESULTS_PER_PAGE, width=5)
267+
item_index_spin.pack(side=tk.LEFT, padx=5)
268+
269+
tb.Label(bid_frame, text="Bid Amount:").pack(side=tk.LEFT)
270+
bid_amount_entry = tb.Entry(bid_frame, width=10)
271+
bid_amount_entry.pack(side=tk.LEFT, padx=5)
272+
add_placeholder(bid_amount_entry, "Bid Amount")
273+
274+
tb.Button(bid_frame, text="Place Bid", bootstyle="primary", command=manual_bid).pack(side=tk.LEFT, padx=5)
275+
276+
# Auto-bidding control
277+
control_frame = tb.Frame(app, padding=10)
278+
control_frame.pack(fill=tk.X)
279+
280+
tb.Label(control_frame, text="Item ID:").pack(side=tk.LEFT)
281+
auto_bid_id_entry = tb.Entry(control_frame, width=5)
282+
auto_bid_id_entry.pack(side=tk.LEFT, padx=5)
283+
add_placeholder(auto_bid_id_entry, "ID")
284+
285+
tb.Button(control_frame, text="Start Item Bidding", bootstyle="success", command=start_bidding_for_item).pack(side=tk.LEFT, padx=5)
286+
tb.Button(control_frame, text="Stop Item Bidding", bootstyle="danger", command=stop_bidding_for_item).pack(side=tk.LEFT, padx=5)
287+
288+
# Start/Stop All Bidding buttons
289+
tb.Button(control_frame, text="Start All Bidding", bootstyle="success", command=start_all_bidding).pack(side=tk.LEFT, padx=5)
290+
tb.Button(control_frame, text="Stop All Bidding", bootstyle="danger", command=stop_all_bidding).pack(side=tk.LEFT, padx=5)
291+
292+
# Pagination
293+
nav = tb.Frame(app, padding=10)
294+
nav.pack(fill=tk.X)
295+
prev_btn = tb.Button(nav, text="← Prev", bootstyle="secondary", command=prev_page)
296+
prev_btn.pack(side=tk.LEFT)
297+
page_label = tb.Label(nav, text="Page 1", font=("Segoe UI", 10))
298+
page_label.pack(side=tk.LEFT, padx=10)
299+
next_btn = tb.Button(nav, text="Next →", bootstyle="secondary", command=next_page)
300+
next_btn.pack(side=tk.LEFT)
301+
302+
# Start UI
303+
app.mainloop()

0 commit comments

Comments
 (0)