first commit
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
import logging
|
||||
import pathlib
|
||||
import sys
|
||||
import traceback
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from api_client import KleerAPI
|
||||
from config import Config
|
||||
from services import (
|
||||
create_time_event,
|
||||
extract_work_time,
|
||||
get_user_by_name,
|
||||
parse_csv,
|
||||
parse_xlsx,
|
||||
)
|
||||
|
||||
|
||||
def run_cli():
|
||||
"""Run the CLI interface for time reporting."""
|
||||
# Validate configuration
|
||||
try:
|
||||
Config.validate_config()
|
||||
except ValueError as e:
|
||||
logging.error(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
# Find CSV and XLSX files
|
||||
current_directory = pathlib.Path.cwd()
|
||||
data_files = list(current_directory.glob('*.csv')) + list(current_directory.glob('*.xlsx'))
|
||||
|
||||
if not data_files:
|
||||
logging.error("No CSV or XLSX files found in the current directory.")
|
||||
sys.exit(1)
|
||||
|
||||
logging.info(f"Data files found: {', '.join(f.name for f in data_files)}")
|
||||
|
||||
# Initialize API client
|
||||
kleer_api = KleerAPI()
|
||||
|
||||
# Process each CSV file
|
||||
for data_file in data_files:
|
||||
logging.info(f"Processing file: {data_file.name}")
|
||||
|
||||
try:
|
||||
# Get user information
|
||||
users_data = kleer_api.get_user_info()
|
||||
username = Config.get_username()
|
||||
|
||||
try:
|
||||
user_id = get_user_by_name(users_data, username)
|
||||
except ValueError as e:
|
||||
logging.error(str(e))
|
||||
continue
|
||||
|
||||
logging.info(f"Using user ID: {user_id} for events.")
|
||||
|
||||
# Get projects
|
||||
projects = kleer_api.get_projects(user_id=user_id)
|
||||
if not projects:
|
||||
logging.warning("No projects found for user.")
|
||||
continue
|
||||
|
||||
logging.info("Available projects from Kleer API:")
|
||||
for name, value in projects.items():
|
||||
logging.info(f"- {name}: {value}")
|
||||
|
||||
# Parse CSV file
|
||||
if data_file.suffix.lower() == '.xlsx':
|
||||
content = data_file.read_bytes()
|
||||
rows = parse_xlsx(content)
|
||||
else:
|
||||
with data_file.open(mode='r', newline='', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
rows = parse_csv(content)
|
||||
|
||||
work_time = extract_work_time(rows)
|
||||
|
||||
if not work_time:
|
||||
logging.info(f"No valid work time found in {data_file.name}.")
|
||||
continue
|
||||
|
||||
# Let user select project
|
||||
project_info = _select_project(projects)
|
||||
if not project_info:
|
||||
logging.warning("No project selected, skipping file.")
|
||||
continue
|
||||
|
||||
# Display work time summary
|
||||
logging.info("Total work time by date:")
|
||||
total_work_time = 0.0
|
||||
|
||||
for date, total_time in sorted(work_time.items()):
|
||||
logging.info(f"{date}: {total_time:.2f} hours")
|
||||
total_work_time += total_time
|
||||
|
||||
# Create and submit event
|
||||
event = create_time_event(
|
||||
user_id=user_id,
|
||||
project_id=project_info["id"],
|
||||
activity=project_info["activity"],
|
||||
date=date,
|
||||
hours=total_time
|
||||
)
|
||||
kleer_api.put_event(event)
|
||||
|
||||
logging.info(f"Total work time for all dates: {total_work_time:.2f} hours")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error processing {csv_file.name}: {e}")
|
||||
logging.error(traceback.format_exc())
|
||||
|
||||
|
||||
def _select_project(projects: Dict[str, Dict[str, Any]]) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Let user select a project from available projects.
|
||||
|
||||
Args:
|
||||
projects: Dictionary of available projects
|
||||
|
||||
Returns:
|
||||
Selected project info or None if cancelled
|
||||
"""
|
||||
while True:
|
||||
print("\nAvailable projects:")
|
||||
project_list = list(projects.items())
|
||||
|
||||
for idx, (name, value) in enumerate(project_list, start=1):
|
||||
print(f"{idx}. {name} (ID: {value['id']}, Activity: {value['activity']})")
|
||||
|
||||
project_choice = input("\nSelect a project by number (or 'q' to quit): ")
|
||||
|
||||
if project_choice.lower() == 'q':
|
||||
return None
|
||||
|
||||
try:
|
||||
project_index = int(project_choice) - 1
|
||||
if 0 <= project_index < len(project_list):
|
||||
_, project_info = project_list[project_index]
|
||||
return project_info
|
||||
else:
|
||||
logging.error("Invalid project selection. Please try again.")
|
||||
except ValueError:
|
||||
logging.error("Invalid input. Please enter a number or 'q' to quit.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_cli()
|
||||
Reference in New Issue
Block a user