login_system.py

 avatar
unknown
plain_text
a month ago
17 kB
3
Indexable
#!/usr/bin/env python3
import tkinter as tk
from tkinter import ttk, messagebox, simpledialog
import mysql.connector
from datetime import datetime
import os
import hashlib
import re
from parkmanager import ParkingManagerApp

# Database Configuration
DB_CONFIG = {
    "host": "localhost",
    "user": "root",
    "password": "",
    "database": "parkfinder",
}

# --- Configuration for Locked Company ---
def load_locked_company():
    """Load the locked company from file; default to 'olpcc' if not found."""
    if os.path.exists("locked_company.txt"):
        try:
            with open("locked_company.txt", "r") as f:
                company = f.read().strip()
                if company:
                    return company
        except Exception as e:
            print("Error reading locked_company.txt:", e)
    return "OLPCC"

def save_locked_company(company):
    """Save the locked company to a file for persistence."""
    try:
        with open("locked_company.txt", "w") as f:
            f.write(company)
        print(f"Successfully saved company: {company} to locked_company.txt")
        return True
    except Exception as e:
        print("Error writing locked_company.txt:", e)
        return False

def hash_password(password):
    """Create a SHA-256 hash of the password."""
    return hashlib.sha256(password.encode()).hexdigest()

class LoginWindow(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Parking Management System - Login")
        self.geometry("400x500")
        self.resizable(False, False)
        
        # Center the window
        self.update_idletasks()
        width = self.winfo_width()
        height = self.winfo_height()
        x = (self.winfo_screenwidth() // 2) - (width // 2)
        y = (self.winfo_screenheight() // 2) - (height // 2)
        self.geometry(f'+{x}+{y}')
        
        self.create_widgets()
        
        # Load the locked company
        self.locked_company = load_locked_company()
        self.update_company_display()
        
        # Fetch companies for the dropdown
        self.load_companies()

    def create_widgets(self):
        # Main frame
        main_frame = ttk.Frame(self, padding=20)
        main_frame.pack(fill="both", expand=True)
        
        # Title
        title_label = ttk.Label(main_frame, text="Parking Management System", font=("Arial", 16, "bold"))
        title_label.pack(pady=(0, 20))
        
        # Login frame
        login_frame = ttk.LabelFrame(main_frame, text="Staff Login", padding=10)
        login_frame.pack(fill="both", expand=True, padx=10, pady=10)
        
        # Username
        ttk.Label(login_frame, text="Username:").pack(anchor="w", pady=(10, 5))
        self.username_entry = ttk.Entry(login_frame, width=30)
        self.username_entry.pack(fill="x", pady=(0, 10))
        
        # Password
        ttk.Label(login_frame, text="Password:").pack(anchor="w", pady=(5, 5))
        self.password_entry = ttk.Entry(login_frame, width=30, show="*")
        self.password_entry.pack(fill="x", pady=(0, 10))
        
        # Company selection
        ttk.Label(login_frame, text="Company:").pack(anchor="w", pady=(5, 5))
        self.company_frame = ttk.Frame(login_frame)
        self.company_frame.pack(fill="x", pady=(0, 10))
        
        self.company_label = ttk.Label(self.company_frame, text="Loading companies...", font=("Arial", 10))
        self.company_label.pack(side="left", fill="x", expand=True)
        
        self.change_company_btn = ttk.Button(self.company_frame, text="Change", command=self.select_company)
        self.change_company_btn.pack(side="right")
        
        # Login button
        self.login_button = ttk.Button(login_frame, text="Login", command=self.login)
        self.login_button.pack(pady=20)
        
        # Register link
        register_frame = ttk.Frame(main_frame)
        register_frame.pack(fill="x", pady=10)
        register_label = ttk.Label(register_frame, text="Don't have an account?")
        register_label.pack(side="left")
        register_link = ttk.Label(register_frame, text="Register", foreground="blue", cursor="hand2")
        register_link.pack(side="left", padx=5)
        register_link.bind("<Button-1>", self.show_register)
        
        # Status bar
        self.status_var = tk.StringVar()
        self.status_var.set("Ready")
        status_bar = ttk.Label(self, textvariable=self.status_var, relief="sunken", anchor="w")
        status_bar.pack(side="bottom", fill="x")

    def update_company_display(self):
        """Update the company label with the current locked company"""
        if hasattr(self, 'company_label'):
            self.company_label.config(text=f"Current: {self.locked_company}")

    def load_companies(self):
        """Load companies from the database for the dropdown"""
        try:
            conn = mysql.connector.connect(**DB_CONFIG)
            cursor = conn.cursor()
            cursor.execute("SELECT id, company_name FROM parking_lots")
            self.companies = [(row[0], row[1]) for row in cursor.fetchall()]
            conn.close()
            
            if not self.companies:
                self.status_var.set("Warning: No companies found in database")
            else:
                self.update_company_display()
                
        except mysql.connector.Error as err:
            self.status_var.set(f"Error: {err}")
            self.companies = []

    def select_company(self):
        """Open a dialog to select a company"""
        if not hasattr(self, 'companies') or not self.companies:
            messagebox.showerror("Error", "No companies available to select")
            return
            
        # Create a new window with a dropdown to select a company
        top = tk.Toplevel(self)
        top.title("Select Company")
        top.geometry("300x200")
        top.resizable(False, False)
        
        # Center the window
        top.update_idletasks()
        width = top.winfo_width()
        height = top.winfo_height()
        x = (top.winfo_screenwidth() // 2) - (width // 2)
        y = (top.winfo_screenheight() // 2) - (height // 2)
        top.geometry(f'+{x}+{y}')
        
        ttk.Label(top, text="Select a company:", font=("Arial", 12)).pack(pady=(20, 10), padx=10)
        
        # Create StringVar with the current company as default
        company_var = tk.StringVar()
        
        # Find the current company name in the list
        current_company_name = None
        for company_id, company_name in self.companies:
            if company_name == self.locked_company:
                current_company_name = company_name
                break
        
        if current_company_name:
            company_var.set(current_company_name)
        else:
            company_var.set(self.companies[0][1])  # Default to first company
        
        # Create the dropdown with company names only
        company_names = [name for _, name in self.companies]
        combobox = ttk.Combobox(top, textvariable=company_var, values=company_names, state="readonly", width=25)
        combobox.pack(pady=10, padx=10)
        
        def on_confirm():
            selected_company_name = company_var.get()
            if selected_company_name:
                # Update the locked company
                self.locked_company = selected_company_name
                # Save to file
                save_locked_company(selected_company_name)
                # Update the label
                self.update_company_display()
                messagebox.showinfo("Success", f"Company set to {selected_company_name}")
                top.destroy()
        
        button_frame = ttk.Frame(top)
        button_frame.pack(pady=20)
        confirm_button = ttk.Button(button_frame, text="Confirm", command=on_confirm)
        confirm_button.pack(side="left", padx=5)
        cancel_button = ttk.Button(button_frame, text="Cancel", command=top.destroy)
        cancel_button.pack(side="left", padx=5)
        
        # Make the window modal
        top.transient(self)
        top.grab_set()
        self.wait_window(top)

    def login(self):
        username = self.username_entry.get().strip()
        password = self.password_entry.get()
        
        if not username or not password:
            messagebox.showerror("Login Error", "Please enter both username and password")
            return
        
        try:
            conn = mysql.connector.connect(**DB_CONFIG)
            cursor = conn.cursor(dictionary=True)
            
            # Check if user exists and password matches
            hashed_password = hash_password(password)
            cursor.execute(
                "SELECT * FROM staff WHERE username = %s AND password = %s",
                (username, hashed_password)
            )
            staff = cursor.fetchone()
            
            if not staff:
                messagebox.showerror("Login Error", "Invalid username or password")
                conn.close()
                return
            
            # Record login time in staffrecords
            now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            cursor.execute(
                "INSERT INTO staffrecords (staff_id, time_in, company) VALUES (%s, %s, %s)",
                (staff['id'], now, self.locked_company)
            )
            conn.commit()
            
            # Get the record ID for logout
            record_id = cursor.lastrowid
            
            conn.close()
            
            # Hide login window
            self.withdraw()
            
            # Create user info dictionary
            user_info = {
                'id': staff['id'],
                'name': staff['name'],
                'username': staff['username'],
                'role': staff['role']
            }
            
            # Launch main application
            app = ParkingManagerApp(user=user_info, record_id=record_id)
            
            # Set logout callback to show login window again
            app.set_logout_callback(self.on_logout)
            
            # Pass the locked company to the main app
            app.locked_company = self.locked_company
            
            app.mainloop()
            
        except mysql.connector.Error as err:
            messagebox.showerror("Database Error", f"Error: {err}")
    
    def on_logout(self):
        """Callback function when user logs out of main app"""
        # Clear login fields
        self.username_entry.delete(0, tk.END)
        self.password_entry.delete(0, tk.END)
        
        # Show login window again
        self.deiconify()
        self.username_entry.focus()

    def show_register(self, event=None):
        self.withdraw()  # Hide login window
        register_window = RegisterWindow(self)
        register_window.mainloop()


class RegisterWindow(tk.Toplevel):
    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent
        self.title("Parking Management System - Register")
        self.geometry("400x500")
        self.resizable(False, False)
        
        # Center the window
        self.update_idletasks()
        width = self.winfo_width()
        height = self.winfo_height()
        x = (self.winfo_screenwidth() // 2) - (width // 2)
        y = (self.winfo_screenheight() // 2) - (height // 2)
        self.geometry(f'+{x}+{y}')
        
        self.protocol("WM_DELETE_WINDOW", self.on_close)
        self.create_widgets()

    def create_widgets(self):
        # Main frame
        main_frame = ttk.Frame(self, padding=20)
        main_frame.pack(fill="both", expand=True)
        
        # Title
        title_label = ttk.Label(main_frame, text="Staff Registration", font=("Arial", 16, "bold"))
        title_label.pack(pady=(0, 20))
        
        # Register frame
        register_frame = ttk.LabelFrame(main_frame, text="Create Account", padding=10)
        register_frame.pack(fill="both", expand=True, padx=10, pady=10)
        
        # Full Name
        ttk.Label(register_frame, text="Full Name:").pack(anchor="w", pady=(10, 5))
        self.name_entry = ttk.Entry(register_frame, width=30)
        self.name_entry.pack(fill="x", pady=(0, 10))
        
        # Username
        ttk.Label(register_frame, text="Username:").pack(anchor="w", pady=(5, 5))
        self.username_entry = ttk.Entry(register_frame, width=30)
        self.username_entry.pack(fill="x", pady=(0, 10))
        
        # Password
        ttk.Label(register_frame, text="Password:").pack(anchor="w", pady=(5, 5))
        self.password_entry = ttk.Entry(register_frame, width=30, show="*")
        self.password_entry.pack(fill="x", pady=(0, 10))
        
        # Confirm Password
        ttk.Label(register_frame, text="Confirm Password:").pack(anchor="w", pady=(5, 5))
        self.confirm_password_entry = ttk.Entry(register_frame, width=30, show="*")
        self.confirm_password_entry.pack(fill="x", pady=(0, 10))
        
        # Role selection
        ttk.Label(register_frame, text="Role:").pack(anchor="w", pady=(5, 5))
        self.role_var = tk.StringVar(value="attendant")
        role_frame = ttk.Frame(register_frame)
        role_frame.pack(fill="x", pady=(0, 10))
        ttk.Radiobutton(role_frame, text="Attendant", variable=self.role_var, value="attendant").pack(side="left", padx=(0, 10))
        ttk.Radiobutton(role_frame, text="Admin", variable=self.role_var, value="admin").pack(side="left")
        
        # Register button
        self.register_button = ttk.Button(register_frame, text="Register", command=self.register)
        self.register_button.pack(pady=20)
        
        # Login link
        login_frame = ttk.Frame(main_frame)
        login_frame.pack(fill="x", pady=10)
        login_label = ttk.Label(login_frame, text="Already have an account?")
        login_label.pack(side="left")
        login_link = ttk.Label(login_frame, text="Login", foreground="blue", cursor="hand2")
        login_link.pack(side="left", padx=5)
        login_link.bind("<Button-1>", self.show_login)
        
        # Status bar
        self.status_var = tk.StringVar()
        self.status_var.set("Ready")
        status_bar = ttk.Label(self, textvariable=self.status_var, relief="sunken", anchor="w")
        status_bar.pack(side="bottom", fill="x")

    def register(self):
        name = self.name_entry.get().strip()
        username = self.username_entry.get().strip()
        password = self.password_entry.get()
        confirm_password = self.confirm_password_entry.get()
        role = self.role_var.get()
        
        # Validate inputs
        if not name or not username or not password:
            messagebox.showerror("Registration Error", "Please fill in all fields")
            return
        
        if password != confirm_password:
            messagebox.showerror("Registration Error", "Passwords do not match")
            return
        
        if len(password) < 6:
            messagebox.showerror("Registration Error", "Password must be at least 6 characters")
            return
        
        if not re.match(r'^[a-zA-Z0-9_]+$', username):
            messagebox.showerror("Registration Error", "Username can only contain letters, numbers, and underscores")
            return
        
        try:
            conn = mysql.connector.connect(**DB_CONFIG)
            cursor = conn.cursor()
            
            # Check if username already exists
            cursor.execute("SELECT id FROM staff WHERE username = %s", (username,))
            if cursor.fetchone():
                messagebox.showerror("Registration Error", "Username already exists")
                conn.close()
                return
            
            # Hash the password
            hashed_password = hash_password(password)
            
            # Insert new staff
            cursor.execute(
                "INSERT INTO staff (name, username, password, role) VALUES (%s, %s, %s, %s)",
                (name, username, hashed_password, role)
            )
            conn.commit()
            conn.close()
            
            messagebox.showinfo("Registration Successful", "Your account has been created. You can now login.")
            self.show_login()
            
        except mysql.connector.Error as err:
            messagebox.showerror("Database Error", f"Error: {err}")

    def show_login(self, event=None):
        self.destroy()
        self.parent.deiconify()  # Show login window

    def on_close(self):
        self.parent.deiconify()  # Show login window when register window is closed
        self.destroy()


if __name__ == "__main__":
    login_app = LoginWindow()
    login_app.mainloop()
Editor is loading...
Leave a Comment