-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
236 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,4 +5,6 @@ | |
/build | ||
__pycache__ | ||
*.pyc | ||
credentials.json | ||
credentials.json | ||
.env | ||
/data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import bcrypt | ||
import json | ||
|
||
class AuthManager: | ||
"""Manages user accounts and permissions.""" | ||
|
||
def __init__(self, user_file="users.json"): | ||
self.user_file = user_file | ||
self.users = self.load_users() | ||
|
||
def load_users(self): | ||
"""Load users from the JSON file.""" | ||
try: | ||
with open(self.user_file, "r") as file: | ||
return json.load(file) | ||
except FileNotFoundError: | ||
return {} | ||
|
||
def save_users(self): | ||
"""Save users to the JSON file.""" | ||
with open(self.user_file, "w") as file: | ||
json.dump(self.users, file, indent=4) | ||
|
||
def add_user(self, username, password, role="staff"): | ||
"""Add a new user (pending approval).""" | ||
if username in self.users: | ||
raise ValueError("User already exists") | ||
hashed_pw = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode() | ||
self.users[username] = { | ||
"password": hashed_pw, | ||
"role": role, | ||
"status": "pending" | ||
} | ||
self.save_users() | ||
|
||
def authenticate(self, username, password): | ||
"""Authenticate a user.""" | ||
user = self.users.get(username) | ||
if not user or user["status"] != "active": | ||
return False | ||
return bcrypt.checkpw(password.encode(), user["password"].encode()) | ||
|
||
def get_role(self, username): | ||
"""Get the role of a user.""" | ||
return self.users.get(username, {}).get("role", None) | ||
|
||
def get_pending_users(self): | ||
"""Get all users pending approval.""" | ||
return {user: data for user, data in self.users.items() if data["status"] == "pending"} | ||
|
||
def approve_user(self, username): | ||
"""Approve a pending user.""" | ||
if username in self.users and self.users[username]["status"] == "pending": | ||
self.users[username]["status"] = "active" | ||
self.save_users() | ||
|
||
def reject_user(self, username): | ||
"""Reject (delete) a pending user.""" | ||
if username in self.users and self.users[username]["status"] == "pending": | ||
del self.users[username] | ||
self.save_users() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import os | ||
import csv | ||
import json | ||
|
||
class FileManager: | ||
"""Handles CSV and metadata files.""" | ||
|
||
def __init__(self, data_folder="data"): | ||
self.data_folder = data_folder | ||
self.metadata_file = os.path.join(self.data_folder, "metadata.json") | ||
self.ensure_data_folder() | ||
self.load_metadata() | ||
|
||
def ensure_data_folder(self): | ||
"""Ensure the data folder exists.""" | ||
if not os.path.exists(self.data_folder): | ||
os.makedirs(self.data_folder) | ||
|
||
def load_metadata(self): | ||
"""Load metadata for CSV files.""" | ||
try: | ||
with open(self.metadata_file, "r") as file: | ||
self.metadata = json.load(file) | ||
except FileNotFoundError: | ||
self.metadata = {} | ||
self.save_metadata() | ||
|
||
def save_metadata(self): | ||
"""Save metadata to the JSON file.""" | ||
with open(self.metadata_file, "w") as file: | ||
json.dump(self.metadata, file, indent=4) | ||
|
||
def create_csv(self, name, fields): | ||
"""Create a new CSV file with specified fields.""" | ||
file_path = os.path.join(self.data_folder, f"{name}.csv") | ||
if name in self.metadata: | ||
raise ValueError("A file with this name already exists.") | ||
|
||
with open(file_path, "w", newline="") as file: | ||
writer = csv.DictWriter(file, fieldnames=fields) | ||
writer.writeheader() | ||
|
||
# Add to metadata | ||
self.metadata[name] = { | ||
"file_path": file_path, | ||
"fields": fields | ||
} | ||
self.save_metadata() | ||
return file_path | ||
|
||
def list_csv_files(self): | ||
"""List all available CSV files.""" | ||
return self.metadata | ||
|
||
def get_csv_fields(self, name): | ||
"""Get the fields of a CSV file.""" | ||
return self.metadata.get(name, {}).get("fields") | ||
|
||
def read_csv(self, name): | ||
"""Read data from a CSV file.""" | ||
if name not in self.metadata: | ||
raise ValueError("CSV file not found.") | ||
file_path = self.metadata[name]["file_path"] | ||
data = [] | ||
try: | ||
with open(file_path, "r", newline="") as file: | ||
reader = csv.DictReader(file) | ||
data.extend(reader) | ||
except FileNotFoundError: | ||
raise ValueError(f"CSV file {name} does not exist.") | ||
return data | ||
|
||
def write_csv(self, name, rows): | ||
"""Write data to a CSV file.""" | ||
if name not in self.metadata: | ||
raise ValueError("CSV file not found.") | ||
file_path = self.metadata[name]["file_path"] | ||
fields = self.metadata[name]["fields"] | ||
|
||
with open(file_path, "w", newline="") as file: | ||
writer = csv.DictWriter(file, fieldnames=fields) | ||
writer.writeheader() | ||
writer.writerows(rows) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1,96 @@ | ||
import tkinter as tk | ||
from tkinter import ttk, messagebox | ||
from core.file_manager import FileManager | ||
from core.auth import AuthManager | ||
from core.order_manager import read_orders, save_orders | ||
from core.backup import backup_to_google_drive | ||
|
||
class AutoShopApp: | ||
def __init__(self, root, csv_file, debug_mode): | ||
def __init__(self, root, debug_mode): | ||
self.root = root | ||
self.csv_file = csv_file | ||
self.debug_mode = debug_mode | ||
self.file_manager = FileManager() | ||
self.orders = [] | ||
self.current_csv = None | ||
self.setup_ui() | ||
self.read_orders() | ||
|
||
def setup_ui(self): | ||
self.root.title("AutoShop Manager") | ||
"""Set up the UI.""" | ||
self.root.title("AutoShop Manager - CSV Management") | ||
|
||
# Select CSV File Section | ||
file_frame = ttk.LabelFrame(self.root, text="CSV File Management") | ||
file_frame.grid(row=0, column=0, padx=10, pady=10, sticky="ew") | ||
|
||
# UI Components | ||
form_frame = ttk.LabelFrame(self.root, text="Order Details") | ||
form_frame.grid(row=0, column=0, padx=10, pady=10, sticky="ew") | ||
ttk.Label(file_frame, text="Select CSV File:").grid(row=0, column=0, sticky="w", padx=5, pady=5) | ||
self.csv_var = tk.StringVar() | ||
self.csv_dropdown = ttk.Combobox(file_frame, textvariable=self.csv_var, state="readonly") | ||
self.csv_dropdown.grid(row=0, column=1, padx=5, pady=5) | ||
self.load_csv_files() | ||
|
||
ttk.Label(form_frame, text="Customer Name:").grid(row=0, column=0, sticky="w", padx=5, pady=5) | ||
self.customer_name_var = tk.StringVar() | ||
ttk.Entry(form_frame, textvariable=self.customer_name_var).grid(row=0, column=1, padx=5, pady=5) | ||
ttk.Button(file_frame, text="Load", command=self.load_csv).grid(row=0, column=2, padx=5, pady=5) | ||
ttk.Button(file_frame, text="Create New CSV", command=self.show_create_csv).grid(row=1, column=0, columnspan=3, padx=5, pady=5) | ||
|
||
ttk.Label(form_frame, text="Contact (Phone/Email):").grid(row=1, column=0, sticky="w", padx=5, pady=5) | ||
self.contact_var = tk.StringVar() | ||
ttk.Entry(form_frame, textvariable=self.contact_var).grid(row=1, column=1, padx=5, pady=5) | ||
|
||
ttk.Label(form_frame, text="Vehicle Model:").grid(row=2, column=0, sticky="w", padx=5, pady=5) | ||
self.vehicle_model_var = tk.StringVar() | ||
ttk.Entry(form_frame, textvariable=self.vehicle_model_var).grid(row=2, column=1, padx=5, pady=5) | ||
|
||
# Buttons | ||
ttk.Button(form_frame, text="Backup", command=self.backup_to_drive).grid(row=3, column=1, sticky="ew", padx=5, pady=5) | ||
|
||
# Table | ||
table_frame = ttk.LabelFrame(self.root, text="Orders") | ||
table_frame.grid(row=1, column=0, padx=10, pady=10, sticky="nsew") | ||
self.tree = ttk.Treeview(table_frame, columns=("Order ID", "Customer", "Vehicle"), show="headings") | ||
# Orders Table | ||
self.table_frame = ttk.LabelFrame(self.root, text="Orders") | ||
self.table_frame.grid(row=1, column=0, padx=10, pady=10, sticky="nsew") | ||
self.tree = ttk.Treeview(self.table_frame, columns=("Order ID", "Customer", "Vehicle"), show="headings") | ||
for col in self.tree["columns"]: | ||
self.tree.heading(col, text=col) | ||
self.tree.pack(fill="both", expand=True) | ||
|
||
def read_orders(self): | ||
self.orders = read_orders(self.csv_file) | ||
def load_csv_files(self): | ||
"""Load available CSV files into the dropdown.""" | ||
files = self.file_manager.list_csv_files() | ||
self.csv_dropdown["values"] = list(files.keys()) | ||
|
||
def load_csv(self): | ||
"""Load the selected CSV file.""" | ||
file_name = self.csv_var.get() | ||
if not file_name: | ||
messagebox.showerror("Error", "Please select a CSV file.") | ||
return | ||
self.current_csv = file_name | ||
self.orders = self.file_manager.read_csv(file_name) | ||
self.populate_table() | ||
|
||
def populate_table(self): | ||
"""Populate the orders table.""" | ||
self.tree.delete(*self.tree.get_children()) | ||
for order in self.orders: | ||
self.tree.insert("", "end", values=(order['order_id'], order['customer_name'], order['vehicle_model'])) | ||
|
||
def backup_to_drive(self): | ||
backup_to_google_drive(self.csv_file) | ||
def show_create_csv(self): | ||
"""Show the UI to create a new CSV file.""" | ||
for widget in self.root.winfo_children(): | ||
widget.destroy() | ||
|
||
self.root.title("Create New CSV File") | ||
frame = ttk.Frame(self.root) | ||
frame.pack(padx=10, pady=10) | ||
|
||
ttk.Label(frame, text="CSV File Name:").grid(row=0, column=0, sticky="w", padx=5, pady=5) | ||
self.new_csv_name_var = tk.StringVar() | ||
ttk.Entry(frame, textvariable=self.new_csv_name_var).grid(row=0, column=1, padx=5, pady=5) | ||
|
||
ttk.Label(frame, text="Fields (comma-separated):").grid(row=1, column=0, sticky="w", padx=5, pady=5) | ||
self.new_csv_fields_var = tk.StringVar() | ||
ttk.Entry(frame, textvariable=self.new_csv_fields_var).grid(row=1, column=1, padx=5, pady=5) | ||
|
||
ttk.Button(frame, text="Create", command=self.create_csv).grid(row=2, column=1, sticky="ew", padx=5, pady=5) | ||
ttk.Button(frame, text="Back", command=self.setup_ui).grid(row=2, column=0, sticky="ew", padx=5, pady=5) | ||
|
||
def create_csv(self): | ||
"""Create a new CSV file.""" | ||
name = self.new_csv_name_var.get() | ||
fields = [field.strip() for field in self.new_csv_fields_var.get().split(",") if field.strip()] | ||
if not name or not fields: | ||
messagebox.showerror("Error", "Both file name and fields are required.") | ||
return | ||
try: | ||
self.file_manager.create_csv(name, fields) | ||
messagebox.showinfo("Success", f"CSV file '{name}' created.") | ||
self.setup_ui() | ||
self.load_csv_files() | ||
except ValueError as e: | ||
messagebox.showerror("Error", str(e)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
ttkbootstrap | ||
PyDrive | ||
PyInstaller | ||
python-dotenv | ||
pyfiglet |