From 11824ae5a10330caa9381d864c71a206612ca145 Mon Sep 17 00:00:00 2001
From: Terence <terence2.frayne@live.uwe.ac.uk>
Date: Wed, 19 Feb 2025 11:10:29 +0000
Subject: [PATCH] Updated login system, GUI, and added database export

---
 GUI.py                                      |  90 +++++-
 Horizon_cinema Database.sql                 | 232 ++++++++++++++++
 README.md                                   |  22 +-
 __pycache__/main.cpython-312.pyc            | Bin 5680 -> 6324 bytes
 main.py                                     |  78 +++---
 src/__pycache__/__init__.cpython-312.pyc    | Bin 549 -> 512 bytes
 src/__pycache__/adminUser.cpython-312.pyc   | Bin 860 -> 823 bytes
 src/__pycache__/bookingObj.cpython-312.pyc  | Bin 855 -> 818 bytes
 src/__pycache__/cinemaObj.cpython-312.pyc   | Bin 2756 -> 2719 bytes
 src/__pycache__/constants.cpython-312.pyc   | Bin 603 -> 601 bytes
 src/__pycache__/dbfunc.cpython-312.pyc      | Bin 7724 -> 8557 bytes
 src/__pycache__/managerUser.cpython-312.pyc | Bin 1031 -> 994 bytes
 src/__pycache__/reportObj.cpython-312.pyc   | Bin 738 -> 701 bytes
 src/__pycache__/screenObj.cpython-312.pyc   | Bin 3580 -> 3543 bytes
 src/__pycache__/staffUser.cpython-312.pyc   | Bin 5434 -> 5463 bytes
 src/constants.py                            |   8 +-
 src/dbfunc.py                               | 288 ++++++++++----------
 17 files changed, 528 insertions(+), 190 deletions(-)
 create mode 100644 Horizon_cinema Database.sql

diff --git a/GUI.py b/GUI.py
index 8a45610..24a0776 100644
--- a/GUI.py
+++ b/GUI.py
@@ -1,5 +1,16 @@
 from tkinter import Tk, Frame, Label, Entry, Button, messagebox
 from main import *
+from src.staffUser import StaffUser
+from src.adminUser import AdminUser
+from src.managerUser import ManagerUser
+from main import loginAttempt
+from tkinter import Listbox
+from src.dbfunc import fetch_movies
+
+import sys
+sys.path.append("src")  # Ensure Python finds 'src' modules
+from main import *
+
 
 # Initialize main window
 window = Tk()
@@ -49,21 +60,59 @@ def apply_theme():
     theme_toggle_button.configure(bg=current_theme["button_bg"], fg=current_theme["button_fg"])
 
 # Handle login action
+
+# Function - open dashboards based on user role
+def open_dashboard(user):
+    """Opens different GUI windows based on user role"""
+    
+    if user is None:
+        messagebox.showerror("Error", "Invalid login attempt.")
+        return
+
+    dashboard = Tk()
+    dashboard.title("Dashboard")
+    dashboard.geometry("500x400")
+
+    # Display Welcome Message Using `user.username`
+    welcome_label = Label(dashboard, text=f"Welcome, {user['username']}!", font=("Arial", 16))
+    welcome_label.pack(pady=20)
+
+    if isinstance(user, StaffUser):
+        Label(dashboard, text="Staff Dashboard").pack()
+        Button(dashboard, text="View Listings").pack()
+        Button(dashboard, text="Create Booking").pack()
+
+    elif isinstance(user, AdminUser):
+        Label(dashboard, text="Admin Dashboard").pack()
+        Button(dashboard, text="Manage Listings").pack()
+        Button(dashboard, text="View Reports").pack()
+
+    elif isinstance(user, ManagerUser):
+        Label(dashboard, text="Manager Dashboard").pack()
+        Button(dashboard, text="Expand Business").pack()
+        Button(dashboard, text="Generate Revenue Reports").pack()
+
+    Button(dashboard, text="Logout", command=dashboard.quit).pack(pady=20)
+
+    dashboard.mainloop()
+
 def gui_login():
     username = username_entry.get()
     password = password_entry.get()
-    # check form not empty
+    
     if not username or not password:
         messagebox.showwarning("Warning", "Please fill in both fields.")
-        # escape function
-    else: # remove else and message box when login function is finished.
-        messagebox.showinfo("Login", "Login successful!")
-    # function call "loginAttempt()" from main.py
-    user = None
-    while user is None:
-        user = loginAttempt(username=username, password=password)
-        if user:
-            break
+        return
+
+    # Call loginAttempt() from main.py
+    user = loginAttempt(username, password)  
+
+    if user:
+        messagebox.showinfo("Login", f"Login successful! Welcome {user['username']}.")
+        window.destroy()  # Close login window
+        open_dashboard(user)  # Open appropriate dashboard based on user role
+    else:
+        messagebox.showerror("Error", "Incorrect username or password!")
 
 # Make rows and columns in the main window expand to center the form frame
 window.grid_rowconfigure(0, weight=1)
@@ -103,4 +152,23 @@ apply_theme()
 
 # app will only launch if run directly from this file.
 if __name__ == "__main__":
-    window.mainloop()
\ No newline at end of file
+    window.mainloop()
+    
+def fetch_movies():
+    conn = get_connection()
+    if conn:
+        cursor = conn.cursor(dictionary=True)
+        cursor.execute("SELECT title, genre, age_rating FROM Films")
+        movies = cursor.fetchall()
+        conn.close()
+        return movies
+    return []
+
+# Create a listbox widget
+movie_listbox = Listbox(dashboard)
+movie_listbox.pack()
+
+# Fetch movies from database and display
+movies = fetch_movies()
+for movie in movies:
+    movie_listbox.insert("end", f"{movie['title']} ({movie['genre']}, Rated: {movie['age_rating']})")
\ No newline at end of file
diff --git a/Horizon_cinema Database.sql b/Horizon_cinema Database.sql
new file mode 100644
index 0000000..c8832d0
--- /dev/null
+++ b/Horizon_cinema Database.sql	
@@ -0,0 +1,232 @@
+-- MySQL dump 10.13  Distrib 8.0.19, for Win64 (x86_64)
+--
+-- Host: localhost    Database: horizon_cinema
+-- ------------------------------------------------------
+-- Server version	8.0.19
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!50503 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `bookings`
+--
+
+DROP TABLE IF EXISTS `bookings`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `bookings` (
+  `booking_id` int NOT NULL AUTO_INCREMENT,
+  `user_id` int DEFAULT NULL,
+  `showing_id` int DEFAULT NULL,
+  `seat_number` varchar(10) NOT NULL,
+  `total_price` decimal(6,2) NOT NULL,
+  `booking_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`booking_id`),
+  KEY `user_id` (`user_id`),
+  KEY `showing_id` (`showing_id`),
+  CONSTRAINT `bookings_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE,
+  CONSTRAINT `bookings_ibfk_2` FOREIGN KEY (`showing_id`) REFERENCES `showings` (`showing_id`) ON DELETE CASCADE
+) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `bookings`
+--
+
+LOCK TABLES `bookings` WRITE;
+/*!40000 ALTER TABLE `bookings` DISABLE KEYS */;
+/*!40000 ALTER TABLE `bookings` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `cancellations`
+--
+
+DROP TABLE IF EXISTS `cancellations`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `cancellations` (
+  `cancellation_id` int NOT NULL AUTO_INCREMENT,
+  `booking_id` int DEFAULT NULL,
+  `cancelled_by` int DEFAULT NULL,
+  `cancellation_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
+  `refund_amount` decimal(6,2) DEFAULT NULL,
+  PRIMARY KEY (`cancellation_id`),
+  KEY `booking_id` (`booking_id`),
+  KEY `cancelled_by` (`cancelled_by`),
+  CONSTRAINT `cancellations_ibfk_1` FOREIGN KEY (`booking_id`) REFERENCES `bookings` (`booking_id`) ON DELETE CASCADE,
+  CONSTRAINT `cancellations_ibfk_2` FOREIGN KEY (`cancelled_by`) REFERENCES `users` (`user_id`) ON DELETE SET NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `cancellations`
+--
+
+LOCK TABLES `cancellations` WRITE;
+/*!40000 ALTER TABLE `cancellations` DISABLE KEYS */;
+/*!40000 ALTER TABLE `cancellations` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `cinemas`
+--
+
+DROP TABLE IF EXISTS `cinemas`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `cinemas` (
+  `cinema_id` int NOT NULL AUTO_INCREMENT,
+  `city` varchar(50) NOT NULL,
+  `location` varchar(255) NOT NULL,
+  PRIMARY KEY (`cinema_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `cinemas`
+--
+
+LOCK TABLES `cinemas` WRITE;
+/*!40000 ALTER TABLE `cinemas` DISABLE KEYS */;
+INSERT INTO `cinemas` VALUES (1,'Birmingham','Downtown Birmingham'),(2,'London','West End'),(3,'Birmingham','Downtown Birmingham'),(4,'London','West End'),(5,'Birmingham','Downtown Birmingham'),(6,'London','West End'),(7,'Birmingham','Downtown Birmingham'),(8,'London','West End'),(9,'Birmingham','Downtown Birmingham'),(10,'London','West End');
+/*!40000 ALTER TABLE `cinemas` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `films`
+--
+
+DROP TABLE IF EXISTS `films`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `films` (
+  `film_id` int NOT NULL AUTO_INCREMENT,
+  `title` varchar(100) NOT NULL,
+  `genre` varchar(50) DEFAULT NULL,
+  `age_rating` varchar(10) DEFAULT NULL,
+  `duration` int NOT NULL,
+  `description` text,
+  PRIMARY KEY (`film_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `films`
+--
+
+LOCK TABLES `films` WRITE;
+/*!40000 ALTER TABLE `films` DISABLE KEYS */;
+INSERT INTO `films` VALUES (1,'Interstellar','Sci-Fi','PG-13',169,'A team of explorers travel through a wormhole in space.'),(2,'Inception','Action','PG-13',148,'A thief who enters people’s dreams.'),(3,'Interstellar','Sci-Fi','PG-13',169,'A team of explorers travel through a wormhole in space.'),(4,'Inception','Action','PG-13',148,'A thief who enters people’s dreams.'),(5,'Interstellar','Sci-Fi','PG-13',169,'A team of explorers travel through a wormhole in space.'),(6,'Inception','Action','PG-13',148,'A thief who enters people’s dreams.'),(7,'Interstellar','Sci-Fi','PG-13',169,'A team of explorers travel through a wormhole in space.'),(8,'Inception','Action','PG-13',148,'A thief who enters people’s dreams.'),(9,'Interstellar','Sci-Fi','PG-13',169,'A team of explorers travel through a wormhole in space.'),(10,'Inception','Action','PG-13',148,'A thief who enters people’s dreams.');
+/*!40000 ALTER TABLE `films` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `screens`
+--
+
+DROP TABLE IF EXISTS `screens`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `screens` (
+  `screen_id` int NOT NULL AUTO_INCREMENT,
+  `cinema_id` int DEFAULT NULL,
+  `screen_number` int NOT NULL,
+  `seating_capacity` int DEFAULT NULL,
+  PRIMARY KEY (`screen_id`),
+  KEY `cinema_id` (`cinema_id`),
+  CONSTRAINT `screens_ibfk_1` FOREIGN KEY (`cinema_id`) REFERENCES `cinemas` (`cinema_id`) ON DELETE CASCADE,
+  CONSTRAINT `screens_chk_1` CHECK ((`seating_capacity` between 50 and 120))
+) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `screens`
+--
+
+LOCK TABLES `screens` WRITE;
+/*!40000 ALTER TABLE `screens` DISABLE KEYS */;
+INSERT INTO `screens` VALUES (1,1,1,100),(2,1,2,120),(3,2,1,80),(4,1,1,100),(5,1,2,120),(6,2,1,80),(7,1,1,100),(8,1,2,120),(9,2,1,80),(10,1,1,100),(11,1,2,120),(12,2,1,80),(13,1,1,100),(14,1,2,120),(15,2,1,80);
+/*!40000 ALTER TABLE `screens` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `showings`
+--
+
+DROP TABLE IF EXISTS `showings`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `showings` (
+  `showing_id` int NOT NULL AUTO_INCREMENT,
+  `film_id` int DEFAULT NULL,
+  `screen_id` int DEFAULT NULL,
+  `show_time` datetime NOT NULL,
+  `price` decimal(5,2) NOT NULL,
+  PRIMARY KEY (`showing_id`),
+  KEY `film_id` (`film_id`),
+  KEY `screen_id` (`screen_id`),
+  CONSTRAINT `showings_ibfk_1` FOREIGN KEY (`film_id`) REFERENCES `films` (`film_id`) ON DELETE CASCADE,
+  CONSTRAINT `showings_ibfk_2` FOREIGN KEY (`screen_id`) REFERENCES `screens` (`screen_id`) ON DELETE CASCADE
+) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `showings`
+--
+
+LOCK TABLES `showings` WRITE;
+/*!40000 ALTER TABLE `showings` DISABLE KEYS */;
+INSERT INTO `showings` VALUES (1,1,1,'2025-05-10 18:00:00',10.00),(2,2,2,'2025-05-10 21:00:00',12.00),(3,1,1,'2025-05-10 18:00:00',10.00),(4,2,2,'2025-05-10 21:00:00',12.00),(5,1,1,'2025-05-10 18:00:00',10.00),(6,2,2,'2025-05-10 21:00:00',12.00),(7,1,1,'2025-05-10 18:00:00',10.00),(8,2,2,'2025-05-10 21:00:00',12.00),(9,1,1,'2025-05-10 18:00:00',10.00),(10,2,2,'2025-05-10 21:00:00',12.00);
+/*!40000 ALTER TABLE `showings` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `users`
+--
+
+DROP TABLE IF EXISTS `users`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `users` (
+  `user_id` int NOT NULL AUTO_INCREMENT,
+  `username` varchar(50) NOT NULL,
+  `password_hash` varchar(255) NOT NULL,
+  `role` enum('Admin','Manager','Staff') NOT NULL,
+  PRIMARY KEY (`user_id`),
+  UNIQUE KEY `username` (`username`)
+) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `users`
+--
+
+LOCK TABLES `users` WRITE;
+/*!40000 ALTER TABLE `users` DISABLE KEYS */;
+INSERT INTO `users` VALUES (10,'admin','password','Admin'),(15,'manager','password','Manager'),(21,'staff','password','Staff');
+/*!40000 ALTER TABLE `users` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping events for database 'horizon_cinema'
+--
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2025-02-19 11:08:45
diff --git a/README.md b/README.md
index 6769dd6..5814983 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,21 @@
-Hello world!
\ No newline at end of file
+To run the programme: python GUI.py
+
+SQL Database Guidance:
+
+1. Ensure SQL Workbench is installed
+2. Create your own local connection
+3. Click 'Server' - 'Data Import'
+4. Select File - 'horizon_cinema
+5. Click 'start import' 
+
+You will now have the SQL database on your own system. Remember any word done to the database will need to be exported and pushed back onto gitlab with the coding. To export:
+
+1. Click 'server' - 'data export'
+2. Select Database
+3. Click 'Export to Self-Contained File (.sql)'
+4. This will create a file of the database - this needs to be pushed on gitlab with any coding change
+
+To get database connected within coding:
+1. Each person will need to update constants.py file
+2. In file find row 4-7.
+3. Update username & password with your own SQL workbench credentials.
\ No newline at end of file
diff --git a/__pycache__/main.cpython-312.pyc b/__pycache__/main.cpython-312.pyc
index c0a103ab4bc10ecf94bfb2896fbe25635bfaac5d..219c7b89736cb328d9b6f143a51c28993ccb875c 100644
GIT binary patch
delta 2934
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

delta 2348
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

diff --git a/main.py b/main.py
index 85cca51..b9d3f43 100644
--- a/main.py
+++ b/main.py
@@ -1,6 +1,11 @@
 import logging
 logging.basicConfig(level=0, format="{asctime} - {levelname} - {message}", style="{", datefmt="%Y-%m-%d %H:%M")
 from src import *
+from src.dbfunc import get_connection
+import json
+from src.staffUser import StaffUser
+from src.adminUser import AdminUser
+from src.managerUser import ManagerUser
 
 """_summary_
 
@@ -15,40 +20,38 @@ from src import *
 """
 
 
-def loginAttempt(username: str, password: str) -> StaffUser | ManagerUser | AdminUser | None:
-    """_summary_
-
-    queries database for matching args
-    returns user(staff, admin or manager) obj if found
-    else returns None type.
-        
-    Args:
-        username (str): encrypted username.
-        password (str): encrypted password.
-
-    Raises:
-        ValueError: if db returns invalid permissionLevel.
+def loginAttempt(username: str, password: str):
+    """Check if the user exists in the database and return the appropriate user object"""
     
-    Returns:
+    conn = get_connection()
+    if conn:
+        cursor = conn.cursor(dictionary=True)
         
-        StaffUser | ManagerUser | AdminUser | None: returns user object or NoneType if login unsucssesfull.
-    """
-    # get permissionLevel and user_id where username and password are match.
-    conditon = json.loads(select_from_tbl_where("tblUsers", f"userName='{username}' AND userPass='{password}'", "permissionLevel")) # returns a dict with args to be used in condition switch.
+        # Fetch user details from the database
+        cursor.execute("SELECT username, password_hash, role FROM users WHERE username = %s AND password_hash = %s", 
+                       (username, password))
+        user_data = cursor.fetchone()
+        
+        cursor.close()
+        conn.close()
+        
+        if user_data:
+            logging.info(f"User {user_data['username']} logged in successfully.")
+
+            # Return an instance of the correct user class
+            if user_data['role'] == 'Staff':
+                return StaffUser(username=user_data['username'])
+            elif user_data['role'] == 'Admin':
+                return AdminUser(username=user_data['username'])
+            elif user_data['role'] == 'Manager':
+                return ManagerUser(username=user_data['username'])
+            else:
+                logging.error("Unknown role in database.")
+                return None
+        else:
+            logging.warning("Invalid username or password.")
     
-    # based on permissonLevel create the relevant user class object.
-    match conditon['permissionLevel']:
-        case 0:
-            user = StaffUser()
-        case 1:
-            user = AdminUser()
-        case 2:
-            user = ManagerUser()
-        case _:
-            logging.debug(f"input values {username}, {password} returned non-valid permissionLevel.")
-            raise ValueError()
-    user.login(username, password)
-    return user
+    return None  # Return None if no user found
 
 def staffUser_options_cli(user:StaffUser) -> None:
     while True:
@@ -167,4 +170,15 @@ def main_cli() -> None:
 if __name__ == "__main__":
     logging.debug("STARTING main_cli")
     main_cli()
-    logging.debug("main_cli finished, program exiting. bye bye!")
\ No newline at end of file
+    logging.debug("main_cli finished, program exiting. bye bye!")
+    
+def loginAttempt(username: str, password: str):
+    """Check if the user exists in the database"""
+    conn = get_connection()
+    if conn:
+        cursor = conn.cursor(dictionary=True)
+        cursor.execute("SELECT * FROM Users WHERE username = %s AND password_hash = %s", (username, password))
+        user = cursor.fetchone()
+        conn.close()
+        return user  # Returns user details if found
+    return None
\ No newline at end of file
diff --git a/src/__pycache__/__init__.cpython-312.pyc b/src/__pycache__/__init__.cpython-312.pyc
index 3f1fc8b9e0a01db80694b92be3fcaab43153e0cf..aaa784897475dad772c7a61107c760b558fdf570 100644
GIT binary patch
delta 48
zcmZ3=(!j!fnwOW0fq{YH{Pe9GxvLn(Et0KbLW@(2iepMri&FDqTvCg(OY#dQA7o4c
E0ARKdJpcdz

delta 85
zcmZo*S<1qFnwOW0fq{WxKCAUc?kYx`SZAx4(Bjmh;+Q;3-K50q)S?*wyi}K>%(7Gk
oU4_taR|Th{%;J*#oEVqX;_Q<Af|$tE#3BWw7{_3j$#WP}04^>Y2><{9

diff --git a/src/__pycache__/adminUser.cpython-312.pyc b/src/__pycache__/adminUser.cpython-312.pyc
index 59b6ca51d02eb599240a53f84bd560ae3b735992..e862f6391397e9c67c1dd92ae1fbf20e7b084608 100644
GIT binary patch
delta 49
zcmcb^ww;apG%qg~0|NuY`RQ9XaxY>Ow@kK*2`x@7Dvl{hElSOcaY-%CF3B(0{F1Sl
F5de&$5ikG%

delta 86
zcmdnac887oG%qg~0|NuYd{*m?+>01(;+(BwLW@(2ievIDb(0daQ;TB!^HN=kGRsmG
qbQMCwT@{>)GK)*{b7EXli?d7e3t}Qu6N?m#VjP2AHXmUuW&{9xO&k0G

diff --git a/src/__pycache__/bookingObj.cpython-312.pyc b/src/__pycache__/bookingObj.cpython-312.pyc
index 608b7ed8602e445546f3e6f5f05508f9f3851e13..4678c08d458aad37f39f436e7a08d250feb3dad2 100644
GIT binary patch
delta 49
zcmcc4wuz1VG%qg~0|NuY`RQ9Xayv4LTP0h?gche36~~mM7NzFJxTF?mm*f|0?qEF6
F2mpRl5WxTd

delta 86
zcmdnQcAbs;G%qg~0|NuYd{*m?+>VSk@y=E;p~b01#W8u7x=D%IsYNmVd8sZ%nPsU8
qx(cD;t_n^?nZ+gfIWaD&#n~nK1u>DSiA4%VF^<75o0A#OGXelztQ(C0

diff --git a/src/__pycache__/cinemaObj.cpython-312.pyc b/src/__pycache__/cinemaObj.cpython-312.pyc
index 8ed8d33b9767d6df731b780292a6cee3a72e52dc..e889a025d413312cfd167d1a991a2a5909f13fd5 100644
GIT binary patch
delta 49
zcmX>iI$xCgG%qg~0|NuY`RQ9XaxZ5Rw@kK*2`x@7Dvl{hElSOcaY-%CF3B(0{Eq1w
FI{=En5t9G_

delta 86
zcmbO)dPJ1_G%qg~0|NuY{{)wf+{>A4;+(BwLW@(2ievIDb(0daQ;TB!^HN=kGRsmG
qbQMCwT@{>)GK)*{b7EXli?d7e3t}Qu6N?m#VjP2AHlJj=#ts0lCLJRH

diff --git a/src/__pycache__/constants.cpython-312.pyc b/src/__pycache__/constants.cpython-312.pyc
index 7ee442bfdb3ba45e22195732f7d80ad565f07e9a..d3bc49915e4b08b2493f7d0b87854b992d1d9ce7 100644
GIT binary patch
delta 319
zcmcc3a+8JkG%qg~0|Ns?*@ms@(<bt2)iXsgr7%Y^r?5n^q_U>6rLfvCFfed3q_9P?
zgZb=GK1UQsDrYKN3MZ7$6~zVSb3^$&QQTlYFO<&}#RKN^f%ujDngX}@Jo1Y&tMc>W
zoip=Na}#f|6y@iah~DBTNGvWc&o4@;;xsfe*E7&F&@-I4vNMvEfq~%`Yg$fzV#zK3
z`1r)Uy!?{HlFa<P;`sO?HU<WUB6g4p=FGg3B2Ew&<gX%b1_p+e44*+>{AKBE6%$&V
zT2vfUl3J9S7vqvzoL!P%FnI%`o~9TB0|O%i14D5R0|UbcW=2NF4;&1PQWrQS8`y5}
W3O8`w;1vfE5|i1O0$D^D7#IMhI!(s_

delta 334
zcmcb~a+`(sG%qg~0|NuYJ%P>X6%%>2>KUV$QW&F{Q<$PyQdv{kQkZQR7#KJiQdpwc
z!F*OIpDl_bl{1wsg&oT0h~fhCIiY;6C~h#H8_MU1;sNt{K>SKxO}<;Ki6C%`Asu8t
z14E?4Ee>DDi8pHMSr`}?Zn382<R_Ng;*XC{%*)F!Ni50C&nu3PFJfh2U?^e(sbJ2`
zD=FdtaXCQ*$QvseK7$PV73XXf6Iz^FR2-9MshgCTomv#*pO@-Vlv$RlpsNrX?yBHa
zlv!MopA+MfTAW>yUl0?SnpmV@6yq4|5;J)nqppT10|Nsi0|P^GHUk602WCb_#t$3}
ajDj8F4Qw}f`5U-k#AFtxKo(&J1_l5!$xrD3

diff --git a/src/__pycache__/dbfunc.cpython-312.pyc b/src/__pycache__/dbfunc.cpython-312.pyc
index eb843504dc7790e9bd0f122a324b2154a3d89437..55dfe2f82b5871a26d95ff2418ec5379258e663c 100644
GIT binary patch
literal 8557
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
QL~t<jeHP(m6aw1}0RGlfwg3PC

literal 7724
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
YiC{2wAvE$cNb++MqY|S+5vcYC0B`wJOaK4?

diff --git a/src/__pycache__/managerUser.cpython-312.pyc b/src/__pycache__/managerUser.cpython-312.pyc
index ea3ceee10e626fb3e288ed6bfe4a169886048fc3..066114c00fe160c03d83e6e6c335ee8c4347c0b7 100644
GIT binary patch
delta 49
zcmZqYc*M?qnwOW0fq{YH{Pe9Gxfe2uTPIt^gche36~~mM7NzFJxTF?mm*f|0e!(cu
F1OS9S5VimS

delta 86
zcmaFF-p;{&nwOW0fq{XcGsJ!)_d-US1ZS(5(Bjmh;+Q;3-K50q)S?*wyi}K>%(7Gk
pU4_taR|Th{%;J*#oEVqX;_Q<Af|$tE#3BWw7{_3j&4(G~nE-!x8@~Vm

diff --git a/src/__pycache__/reportObj.cpython-312.pyc b/src/__pycache__/reportObj.cpython-312.pyc
index 642554601c431f1833b8aa56b9135901b1a274e8..84e9396990ad1e391f3e1c38a0873b3ca6ac71b5 100644
GIT binary patch
delta 49
zcmaFFx|fyvG%qg~0|NuY`RQ9Xa$7NqTP9n@gche36~~mM7NzFJxTF?mm*f|0Ze&bk
F1OR|25Lf^J

delta 86
zcmdnX`iPbLG%qg~0|NuYGwID6xvdy&;+(BwLW@(2ievIDb(0daQ;TB!^HN=kGRsmG
qbQMCwT@{>)GK)*{b7EXli?d7e3t}Qu6N?m#VjP2AHpeg~G6Ddc^c(pA

diff --git a/src/__pycache__/screenObj.cpython-312.pyc b/src/__pycache__/screenObj.cpython-312.pyc
index 818715ed3fa550818788af402c31924b8c63a4cd..404eaa518842cffc596ae0349cfae21c58093a3f 100644
GIT binary patch
delta 49
zcmew(eO;RSG%qg~0|NuY`RQ9Xa_?mlw@kK*2`x@7Dvl{hElSOcaY-%CF3B(0%)<PG
F3jm=&5n%uT

delta 86
zcmcaE{YRSnG%qg~0|NuYd{*m?+<Td9;+(BwLW@(2ievIDb(0daQ;TB!^HN=kGRsmG
qbQMCwT@{>)GK)*{b7EXli?d7e3t}Qu6N?m#VjP2AHs5FZ!36-PMjb2w

diff --git a/src/__pycache__/staffUser.cpython-312.pyc b/src/__pycache__/staffUser.cpython-312.pyc
index 4988db65302ceac12f65b8e5218373debd4dc439..1aee61bed589a71284cd861e87f81a6ed12f6fe9 100644
GIT binary patch
delta 883
zcmdm`bzO`1G%qg~0|NuY`RQBJqc`$SWvpjmU|?WoU|{&1#lXNYouP)Uh_8eZqy&Uh
z7*m+mFt26;u^AW`87diS7;6~f;qo<1H4O1kSq6q0<{E~0kXc~mH7qp@@gUP7Jk}b9
zcvdizfq|itL6gPr7Dq`^j!9^7YEkhm22G}0tR;y_IjOfelk@XZGD|Y^^EBCRi592k
zq$ZcdrxoSr#+M}J#FuBJ7Nt&RVM-OZOty*vYmO;NElSOcaY-%CF3B&Lyp+jJ%z}Y|
zp@HEcx6l(F#T8sTlCJVN-ry0~{FBL<QAw16fuTqYM2Le32?hp+m5fD_3=9m#Ab%+o
zNllJpnaL=#`3K7xM%E$$1_p-7=h(bhi$p*yeRf?oklG@#$w};Lf;u2=Yzzzx3Sdwq
zHF+lc8Ah4OjRKOJ>p4srg+bc=G}()^L3XKu2=&RwIklufX53;gPR&Vk$t=3Xm|6@9
zHw6WSBB{;XTr(NfAW9h+7;dp-=B4Eqfn9A5(ys`z66VrV+|dx*CfoBoVS{LztjVj$
zC^^}iSB_C?au#n3qs-*@ymi_TNsx)ce)%Pyxdl0?xv6<2sVS~SMfpWVD8|m<^9Acl
zob1joCFq1<Z4tjWSUz*Ix_~eid2ay&Hdl}?nazy?rx{@m3lh>6asdfjF)%P_GJ^xX
zNM~}tkT{zZ$YElW&k2c5J|r|(Tn40ou?Up3G?|K&85kJ+GzE(kCYK4z1^9!MG8dN=
z-D1hfEH1gll9ZpHQzQ*CR|!NIfCz9<fvhQV0CB;#x`SArAR-79=?q06TZ*J6KM`(|
z0W&#3CUeBc=O$+6#m8&%6oJD`V{)5_rM3b{j<vKnwaBo@4`fCJhyZH?x&0T1O>TZl
RX-=wLQT*grB0el2832(l!;k;~

delta 841
zcmcbvwM&clG%qg~0|Nttul?q9wT-+}8Izb87#Nrt7#KbWFfcGoXQ*K-VytASVXR?@
z2dM+&8m1bCct$Xjfq|ihxrQMgqzf!i!&1W#57H0evDPrevw)cl3=EYFnv8y$tdm2T
zQf=a#tztrpQ;UjY@+@_e60=i_V*K+`U5YZxQWbO+Lc?7ZoQg7wOY(DKTvCg(OY#e1
zB2yEK6pUgVgIy+HWOCy(U|?WqV0g$a^ns0mS8%f~vooWT00RR<ksyc=0ujOt3=At7
zi$oY07>Ys8RVWgjJc(r{qxfbW)-#M;w>V0Ya=>nw{FlvZvK^DeWM6h2Hjwfn!O8XP
zYJ!>|oooyY3<_XSBszH;`x!>@$@2sxH_zcPWfWFmU|{gmWG~VHS*HXdlqWyo)M6Bw
z%+DpkD7smLYbK)_M4W+v;TB6~URr(;*p((A#WEoCVJ>^l9St#hatzNCHi)LlZoG<&
zB9k+C<rqaLxAV3zicjX@tAj~S;FA<VHUeY?Z*o!U<N^V)$(Q-`!KxD{r|?S&+GAMP
z&+iSE&z$TcAdE#mL%@K|38YJW^E`pmj4(U%g|xLCLBi$?3=Epg;J_`?1li14oSKuG
zT#}ieR}6}O1qD!W9}(J7FAh@4SOiKUnoLD<;2;w$k_PGFijU9DPbtkwjgP;@6CYn#
znwXQ9n420OU*rRlW-cx%y2X-{SzL09B`H5Yr$`KBvn+_v1rgvt1UaP07Q_X+%LT-8
z0}=k9=w~Qm1KD3BI+;<VO$N;502$2@AD^3;nHL|g$x{RlYt_k%MJ%<!u3{}MPAxJl
g@&*|a3L?PTL>U+uesS19+-p}9Gnrk~hXo`90AHrFmH+?%

diff --git a/src/constants.py b/src/constants.py
index 348dab2..d548e2d 100644
--- a/src/constants.py
+++ b/src/constants.py
@@ -1,10 +1,10 @@
 # all constants are followed by docstrings labeling them, these strings show on hover in most IDEs(vscode and pycharm at least)
 
 # database values:
-DBNAME = "aaaaa"
-DBUSERNAME = ""
-DBPASSWORD = ""
-DBHOSTNAME = ""
+DBNAME = "Horizon_Cinema"
+DBUSERNAME = "root"
+DBPASSWORD = "password"
+DBHOSTNAME = "127.0.0.1"
 
 # screenObj:
 MAX_SIZE:float = 100.0
diff --git a/src/dbfunc.py b/src/dbfunc.py
index 72465bd..d388d5a 100644
--- a/src/dbfunc.py
+++ b/src/dbfunc.py
@@ -1,155 +1,159 @@
-import logging, json, mysql.connector
-from .constants import DBNAME, DBHOSTNAME, DBUSERNAME, DBPASSWORD
+import logging
+import json
+import mysql.connector
 from mysql.connector import errorcode
+from src.constants import DBNAME, DBHOSTNAME, DBUSERNAME, DBPASSWORD
 
+# Configure logging for debugging
+logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 
+def get_connection():
+    """Creates and returns a database connection."""
+    try:
+        conn = mysql.connector.connect(
+            host=DBHOSTNAME,
+            user=DBUSERNAME,
+            password=DBPASSWORD,
+            database=DBNAME
+        )
+        logging.info("Connected to the database successfully.")
+        return conn
+    except mysql.connector.Error as err:
+        if err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
+            logging.error("Invalid database credentials.")
+        elif err.errno == errorcode.ER_BAD_DB_ERROR:
+            logging.error("Database does not exist.")
+        else:
+            logging.error(f"Database connection error: {err}")
+        return None
 
+# Function to SELECT data from any table
+def select_from_table(table, *columns):
+    """Fetches data from a given table."""
+    conn = get_connection()
+    if not conn:
+        return []
 
+    try:
+        cursor = conn.cursor(dictionary=True)
+        query = f"SELECT {', '.join(columns) if columns else '*'} FROM {table}"
+        cursor.execute(query)
+        results = cursor.fetchall()
+        return results
+    except mysql.connector.Error as err:
+        logging.error(f"Error fetching data: {err}")
+        return []
+    finally:
+        cursor.close()
+        conn.close()
 
+# Function to SELECT data with a WHERE condition
+def select_from_table_where(table, condition, *columns):
+    """Fetches data from a table with a WHERE condition."""
+    conn = get_connection()
+    if not conn:
+        return []
 
-def getConnection(db=""):
-    # if specified database
-    if db != "":
-        try:
-            conn = mysql.connector.connect(host = DBHOSTNAME,
-                user = DBUSERNAME,
-                password = DBPASSWORD,
-                database = db)
-        except mysql.connector.Error as err:
-            if err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
-                logging.error("Username or Password is not working")
-            elif err.errno == errorcode.ER_BAD_DB_ERROR:
-                logging.error("Database dose not exist")
-            else:
-                logging.error(err)
-        else:
-            logging.info("Connected to server.")
-            return conn
-    # if database not specified try default from line 5
-    else:
-        try:
-            conn = mysql.connector.connect(host = DBHOSTNAME,
-                user = DBUSERNAME,
-                password = DBPASSWORD)
-        except mysql.connector.Error as err:
-            if err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
-                logging.error("Username or Password is not working")
-            else:
-                logging.error(err)
-        else:
-            logging.error("Connected to server, without database.")
-            return conn
-        
-def select_from_tbl(table:str, dbname, *args) -> json:
-    logging.debug(f"running slect statement with values: tablename='{table}'', dbname='{dbname}'', {args}")
-    conn = getConnection(db=DBNAME)
-    if conn != None:
-        if conn.is_connected():
-            SELECT_statement = f"SELECT "
-            for arg in args:
-                SELECT_statement.append(f"{arg}, ")
-            SELECT_statement.removesuffix(", ")
-            SELECT_statement.append(f" from {table};")
-            
-            dbcursor = conn.cursor()
-            dbcursor.execute(f"USE {dbname}")
-            dbcursor.execute(SELECT_statement)
-            logging.info("SELECT statement executed successfully.")
-            data = dbcursor.fetchall()
-            logging.debug(f"dataOut: {data}")
-            dbcursor.close()
-            conn.close()
-            data = json.dumps(data)
-            return data
-        else:
-            logging.error("conn not connected")
-            raise ConnectionError()
-    else:
-        logging.error("conn returned NoneType")
-        raise ConnectionAbortedError()
-    
-def select_from_tbl_where(table:str, condition:str, dbname:str=DBNAME, *args:str) -> json:
-    """_summary_
-    utility function for getting data from database.
-    
-    SQL query format:
-        'SELECT args FROM table WHERE condition;'
-        
-    Args:
-        table (str): table name in database.
-        dbname (str): database name, defult from constants.
-        condition (str): condition used to search table with more detail and smaller retrun.
+    try:
+        cursor = conn.cursor(dictionary=True)
+        query = f"SELECT {', '.join(columns) if columns else '*'} FROM {table} WHERE {condition}"
+        cursor.execute(query)
+        results = cursor.fetchall()
+        return results
+    except mysql.connector.Error as err:
+        logging.error(f"Error fetching data with condition: {err}")
+        return []
+    finally:
+        cursor.close()
+        conn.close()
 
-    Raises:
-        ConnectionAbortedError: if the connection variables are invalid.
-        ConnectionError: if the database could not be connected to.
+# Function to INSERT data into any table
+def insert_into_table(table, data):
+    """Inserts data into a specified table."""
+    conn = get_connection()
+    if not conn:
+        return False
 
-    Returns:
-        json: json string of returned values.
-    """
-    if len(args) == 0:
-        args = ("*")
-    logging.debug(f"running select statement with values: tablename={table}, dbname={dbname}, condition={condition}, args={args}")
-    conn = getConnection(db=DBNAME)
-    if conn == None:
-        logging.warning("conn returned NoneType")
-        raise ConnectionAbortedError()
-    if conn.is_connected():
-        SELECT_statement = "SELECT "
-        for arg in args:
-            SELECT_statement.append(f"{arg}, ")
-        SELECT_statement.removesuffix(", ")
-        SELECT_statement.append(f" from {table} ")
-        SELECT_statement.append(f"WHERE {condition};")
-        
-        dbcursor = conn.cursor()
-        dbcursor.execute(f"USE {dbname}")
-        dbcursor.execute(SELECT_statement)
-        logging.info("SELECT statement executed successfully.")
-        data = dbcursor.fetchall()
-        logging.debug(f"dataOut: {data}")
-        dbcursor.close()
+    try:
+        cursor = conn.cursor()
+        columns = ', '.join(data.keys())
+        values = ', '.join(['%s'] * len(data))
+        query = f"INSERT INTO {table} ({columns}) VALUES ({values})"
+        cursor.execute(query, tuple(data.values()))
+        conn.commit()
+        logging.info("Data inserted successfully.")
+        return True
+    except mysql.connector.Error as err:
+        logging.error(f"Error inserting data: {err}")
+        return False
+    finally:
+        cursor.close()
         conn.close()
-        data = json.dumps(data)
-        return data
-    else:
-        logging.error("conn not connected")
-        raise ConnectionError()
-        
-    
-def save_to_Tbl(table, data):
-    """need to fix"""
-    logging.debug(f"running update statment with values: tablename='{table}', data='{data}'")
-    if type(data) is json:
-        data = json.loads(data)
-    elif type(data) is dict:
-        pass
-    else:
-        raise TypeError()
-        
-    conn = getConnection(db=DBNAME)
-    if conn == None:
-        logging.error("conn returned NoneType")
-        raise ConnectionAbortedError()
-    if conn.is_connected():
-        logging.debug("MySQL Connection is established.")
-        INSERT_statement = f"INSERT INTO {table} ("
-        
-        keys = ""
-        values = ""
-        for key, value in data.items():
-            keys.append(f"{key}, ")
-            values.append(f"{value}, ")
-        INSERT_statement.append(f"{keys.removesuffix(", ")}) VALUES ({values.removesuffix(", ")});")
-        
-        dbcursor = conn.cursor()
-        dbcursor.execute(INSERT_statement)
+
+# Function to DELETE data
+def delete_from_table(table, condition):
+    """Deletes data from a specified table based on a condition."""
+    conn = get_connection()
+    if not conn:
+        return False
+
+    try:
+        cursor = conn.cursor()
+        query = f"DELETE FROM {table} WHERE {condition}"
+        cursor.execute(query)
+        conn.commit()
+        logging.info("Data deleted successfully.")
+        return True
+    except mysql.connector.Error as err:
+        logging.error(f"Error deleting data: {err}")
+        return False
+    finally:
+        cursor.close()
+        conn.close()
+
+# Function to UPDATE data
+def update_table(table, data, condition):
+    """Updates data in a specified table based on a condition."""
+    conn = get_connection()
+    if not conn:
+        return False
+
+    try:
+        cursor = conn.cursor()
+        set_clause = ', '.join([f"{key} = %s" for key in data.keys()])
+        query = f"UPDATE {table} SET {set_clause} WHERE {condition}"
+        cursor.execute(query, tuple(data.values()))
         conn.commit()
-        dbcursor.close()
-        
+        logging.info("Data updated successfully.")
+        return True
+    except mysql.connector.Error as err:
+        logging.error(f"Error updating data: {err}")
+        return False
+    finally:
+        cursor.close()
+        conn.close()
+
+def fetch_users():
+    conn = get_connection()
+    if conn:
+        cursor = conn.cursor(dictionary=True)
+        cursor.execute("SELECT * FROM Users")
+        users = cursor.fetchall()
+        conn.close()
+        return users
+    return []
+
+def fetch_movies():
+    """
+    Fetch all movies from the database.
+    """
+    conn = get_connection()
+    if conn:
+        cursor = conn.cursor(dictionary=True)
+        cursor.execute("SELECT * FROM Movies")  # Change 'Movies' to your actual table name
+        movies = cursor.fetchall()
+        cursor.close()
         conn.close()
-        logging.debug("INSERT query executed with no issues.")
+        return movies
     else:
-        logging.error("conn not connected")
-        raise ConnectionError()
-    
+        return []
-- 
GitLab