Skip to content

Commit

Permalink
arghhhhhh
Browse files Browse the repository at this point in the history
  • Loading branch information
Colack committed Dec 17, 2024
1 parent 691f9f3 commit 1b023b2
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 35 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@
/build
__pycache__
*.pyc
credentials.json
credentials.json
.env
/data
61 changes: 61 additions & 0 deletions core/auth.py
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()
83 changes: 83 additions & 0 deletions core/file_manager.py
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)
102 changes: 73 additions & 29 deletions core/ui.py
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))
19 changes: 14 additions & 5 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
import sys
import os
from dotenv import load_dotenv
from core.ui import AutoShopApp
import ttkbootstrap as tb

def main():
csv_file_path = "orders.csv"
debug_mode = "--debug" in sys.argv
# Load environment variables
load_dotenv()
csv_file_path = os.getenv("CSV_FILE_PATH", "orders.csv")
debug_mode = os.getenv("DEBUG_MODE", "False").lower() == "true"

# Handle CLI arguments
new_csv_mode = "--new" in sys.argv

if new_csv_mode:
from core.order_manager import create_csv_file
create_csv_file(csv_file_path)
print("New CSV file created with required fields.")
sys.exit()

root = tb.Window(themename="darkly")
app = AutoShopApp(root, csv_file_path, debug_mode)
# Load default theme
theme = os.getenv("DEFAULT_THEME", "darkly")

# Initialize application
root = tb.Window(themename=theme)
app = AutoShopApp(root, debug_mode)
root.mainloop()

if __name__ == "__main__":
Expand Down
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
ttkbootstrap
PyDrive
PyInstaller
python-dotenv
pyfiglet

0 comments on commit 1b023b2

Please sign in to comment.