DEV Community

Cover image for Kiosk Manager: My Python Class capstone project (No External Libraries Needed)
Joan
Joan

Posted on

Kiosk Manager: My Python Class capstone project (No External Libraries Needed)

For a capstone project at LuxDev, we were given a challenge: build a complete inventory and sales management tool for a small kiosk using only basic Python, just what we learned in the course. That means only functions, dictionaries, lists, tuples, sets, and simple file input/output.
The result is Kiosk Manager: a command-line app that lets a shop owner track stock, record sales, make reports, search for products, and save everything between sessions.
Here’s how it works, and what I learned along the way.

The Constraint

The main rule: handle every risky situation before it goes wrong. That means things like converting user input to a number or looking up a key in a dictionary had to be checked first, not fixed after an error. For example, every number you enter in Kiosk Manager is checked with .isdigit() before anything else:

raw_amount = input(f"How many {name} to sell? ").strip()

if not raw_amount.isdigit():
    print("Please enter a valid whole number for quantity.")
    return

amount = int(raw_amount)
Enter fullscreen mode Exit fullscreen mode

It’s a simple habit, but it’s used everywhere: restocking, selling, even the main menu. That way, a typo won’t crash the program; you’ll just see a clear message and can try again.

Data Structures

The whole app runs on just three things:

  • A dictionary of dictionaries for inventory:
stock = {
    'Bread': {'price': 65, 'quantity': 20},
    'Milk': {'price': 120, 'quantity': 15},
}
Enter fullscreen mode Exit fullscreen mode
  • A list of tuples for the sales log, where each sale is (product, quantity, total):
sales_log = [('Bread', 2, 130), ('Milk', 1, 120)]
Enter fullscreen mode Exit fullscreen mode
  • A set keeps track of which products were sold in a session. Sets ignore duplicates, so you can call sold_today.add(name) every time and just use len(sold_today) to instantly see how many unique products were sold; no extra logic needed

Core Features

Viewing and Searching Stock

Both view_stock() and search_products() print inventory in aligned columns using Python’s f-string padding:

print(f"{name:<15}{details['price']:>13}{details['quantity']:>12}")
Enter fullscreen mode Exit fullscreen mode

<15 means left-align in 15 spaces; >13 means right-align in 13.
Lining up the numbers this way keeps everything neat and much easier to read in the command line.
Searching is case-insensitive, so typing “bre” will match “Bread”; you don’t need to know the exact name or use the right case.

Restocking and Adding Products

restock_product()branches on whether the product already exists:

If it does, it only asks for a quantity to add.
If it doesn’t, it’s treated as a new product and asks for both a price and a starting quantity.

Every input is checked with .isdigit() before converting, so if someone enters something invalid, the restock just cancels with a friendly message, no crashing.

Selling, With a Stock Check First

sell_product()is the one function where getting the order of operations right really matters:

if amount > available:
    print(f"Not enough stock. Only {available} {name} left.")
    return

stock[name]['quantity'] -= amount
total = stock[name]['price'] * amount
sales_log.append((name, amount, total))
sold_today.add(name)
Enter fullscreen mode Exit fullscreen mode

Stock is always checked before making any changes. If someone asks for more than what’s available, the function stops right away; nothing changes. This way, the inventory can never go negative.

Reporting

sales_report() loops through sales_log once, doing three things in the same pass:

  • Prints each sale as a table row
  • Accumulates total_revenue
  • Builds a quantity_by_product dictionary using .get(name, 0) + qty, a classic Python trick for counting items in a dictionary without checking whether the key exists first.

The best-seller is found with:

best_product = max(quantity_by_product, key=quantity_by_product.get)
Enter fullscreen mode Exit fullscreen mode

max() on a dictionary looks at keys, but if you give it key=quantity_by_product.get, it finds the key with the biggest value. So it tells you which product sold the most, not just the biggest number.

Persistence Without a Database
Data survives between runs using two plain text files:

  • inventory.txt — overwritten ("w" mode) on every exit, since it should only ever reflect the current state

  • sales_history.txt — appended to ("a" mode) on every exit, so sales history accumulates across sessions instead of being lost

Both files just use plain CSV-style lines like (Bread,65,20). When loading, .split(",") splits each line, and everything gets converted back to numbers since reading from a text file always gives you strings.

if not os.path.exists(INVENTORY_FILE):
    return { 'Bread': {'price': 65, 'quantity': 20}, ... }  # sensible defaults
Enter fullscreen mode Exit fullscreen mode

If there’s no save file yet (like on the first run or after a fresh install), the app just loads a simple default inventory so you can get started right away.


Program Structure

The file is laid out top to bottom: all functions come first, then the main program (the welcome message and the menu loop) at the end. Since Python reads code from top to bottom, this just makes sure every function is ready before it gets used. The menu runs in a simple while True loop, and only stops when you pick “Exit”; then everything is saved before quitting.

Takeaways
Validating input before doing anything- check, then convert, then change turned out to be a really useful habit, no matter what error-handling tools you have. And building everything out of just three basic data structures (dict, list of tuples, set) was a great reminder that you can go a long way with just the Python basics.


This is part of my ongoing series documenting the LuxDev Data Science, Data Analysis, and AI programme.

Top comments (0)