Files
HourStack/EnterTime.py
T

130 lines
5.1 KiB
Python

import os
import argparse
from datetime import datetime
from utils import load_config, load_json, save_json
def get_next_id(data_list, id_prefix, id_field):
"""Generates the next incremental ID for clients or projects."""
if not data_list:
return f"{id_prefix}-001"
ids = [int(item[id_field].split('-')[1]) for item in data_list]
return f"{id_prefix}-{max(ids) + 1:03d}"
def select_or_create_client(data_dir, clients_data):
"""Allows selecting an active client or creating a new one."""
active_clients = [c for c in clients_data["Clients"] if c.get("Active", True)]
print("\n--- Select Client ---")
for i, client in enumerate(active_clients, 1):
print(f"{i}. {client['Name']}")
print(f"{len(active_clients) + 1}. [ADD NEW CLIENT]")
choice = input("\nSelect a number: ")
if choice == str(len(active_clients) + 1):
# Create New Client Flow
new_name = input("Client Name: ")
new_rate = float(input("Default Hourly Rate: ") or 0)
new_id = get_next_id(clients_data["Clients"], "CLT", "ClientID")
new_client = {
"ClientID": new_id,
"Name": new_name,
"Active": True,
"BillingAddress": {"Street1": "", "Street2": "", "City": "", "State": "", "PostalCode": "", "Country": ""},
"BillingEmail": "",
"DefaultRate": new_rate
}
clients_data["Clients"].append(new_client)
save_json(os.path.join(data_dir, "Clients.json"), clients_data)
return new_id
else:
return active_clients[int(choice) - 1]["ClientID"]
def select_or_create_project(data_dir, projects_data, client_id):
"""Allows selecting an active project for the client or creating a new one."""
client_projects = [p for p in projects_data["Projects"] if p["ClientID"] == client_id and p.get("Active", True)]
print("\n--- Select Project ---")
print("0. No Project")
for i, project in enumerate(client_projects, 1):
print(f"{i}. {project['Name']}")
print(f"{len(client_projects) + 1}. [ADD NEW PROJECT]")
choice = input("\nSelect a number: ")
if choice == "0":
return ""
elif choice == str(len(client_projects) + 1):
# Create New Project Flow
new_name = input("Project Name: ")
new_rate = float(input("Project Billing Rate (0 for client default): ") or 0)
new_id = get_next_id(projects_data["Projects"], "PRJ", "ProjectID")
new_project = {
"ProjectID": new_id,
"ClientID": client_id,
"Name": new_name,
"BillingRate": new_rate,
"Active": True
}
projects_data["Projects"].append(new_project)
save_json(os.path.join(data_dir, "Projects.json"), projects_data)
return new_id
else:
return client_projects[int(choice) - 1]["ProjectID"]
def main():
# Setup argument parser to accept data from Apple Shortcuts
parser = argparse.ArgumentParser(description="Log a time entry.")
parser.add_argument("--date", type=str, help="Date in YYYY-MM-DD format")
parser.add_argument("--duration", type=float, help="Duration in hours")
parser.add_argument("--client", type=str, help="Client ID")
parser.add_argument("--project", type=str, help="Project ID (Optional)", default="")
parser.add_argument("--desc", type=str, help="Description of work")
args = parser.parse_args()
config = load_config()
data_dir = config["DataDirectory"]
# Load existing data
clients_data = load_json(os.path.join(data_dir, "Clients.json"), {"Clients": []})
projects_data = load_json(os.path.join(data_dir, "Projects.json"), {"Projects": []})
# Check if data was passed via parameters (e.g., from Apple Shortcuts)
if args.duration and args.client and args.desc:
date_str = args.date if args.date else datetime.now().strftime("%Y-%m-%d")
duration = args.duration
client_id = args.client
project_id = args.project
description = args.desc
else:
# Fall back to interactive mode if no parameters were passed
date_str = input(f"Date (YYYY-MM-DD) [Default: {datetime.now().strftime('%Y-%m-%d')}]: ") or datetime.now().strftime("%Y-%m-%d")
duration = float(input("Duration (e.g., 1.5): "))
client_id = select_or_create_client(data_dir, clients_data)
project_id = select_or_create_project(data_dir, projects_data, client_id)
description = input("Description: ")
# Generate Entry
entry_id = datetime.now().strftime("%Y%m%d%H%M%S")
entry = {
"ID": entry_id,
"Date": date_str,
"Duration": duration,
"ClientID": client_id,
"ProjectID": project_id,
"Description": description,
"Invoiced": False,
"InvoiceNumber": ""
}
# Save to Annual Log
year = date_str.split('-')[0]
log_path = os.path.join(data_dir, f"{year}_Time_Log.json")
log_data = load_json(log_path, {"Year": int(year), "Entries": []})
log_data["Entries"].append(entry)
save_json(log_path, log_data)
print(f"\nSuccess: Entry {entry_id} saved to {year}_Time_Log.json")
if __name__ == "__main__":
main()