From bee6508f4c5bcc06281d878425e2428401f87a2a Mon Sep 17 00:00:00 2001 From: lasse Date: Fri, 18 Apr 2025 22:34:10 +0200 Subject: [PATCH] first commit --- .gitignore | 2 + README.md | 70 +++++++++++++++++++++ analyze_data.py | 157 ++++++++++++++++++++++++++++++++++++++++++++++ import_data.py | 65 +++++++++++++++++++ requirements.txt | 5 ++ run_analysis.py | 68 ++++++++++++++++++++ transform_data.py | 118 ++++++++++++++++++++++++++++++++++ working_times.db | Bin 0 -> 2109440 bytes 8 files changed, 485 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 analyze_data.py create mode 100644 import_data.py create mode 100644 requirements.txt create mode 100644 run_analysis.py create mode 100644 transform_data.py create mode 100644 working_times.db diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..264a49e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +./venv +./data/* \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e15bae2 --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +# Working Time Analysis + +This project analyzes working time data from a CSV file by: +1. Importing the data into a DuckDB database +2. Transforming the data for analysis +3. Generating reports based on the data + +## Setup + +### Dependencies + +Install the required dependencies: + +```bash +pip install -r requirements.txt +``` + +## Usage + +### 1. Import Data + +Run the import script to load the CSV data into DuckDB: + +```bash +python3 import_data.py +``` + +This will: +- Create a DuckDB database file `working_times.db` +- Import the CSV data into a table +- Add an import timestamp to each record + +### 2. Transform Data + +Run the transformation script to create analytical views: + +```bash +python3 transform_data.py +``` + +This will: +- Create summary tables with aggregated data +- Convert hours to days (using 8 hours = 1 day conversion) +- Add transformation timestamps + +### 3. Analyze Data + +Run the analysis script to generate reports: + +```bash +python3 analyze_data.py +``` + +This will produce: +- Overall time summary +- Top projects by hours +- Busiest days +- Day distribution analysis +- Project-activity combinations + +## Data Structure + +The analysis uses the following tables: + +- `working_times`: Raw imported data +- `working_times_summary`: Per-day, per-project aggregation +- `project_summary`: Total time per project +- `daily_summary`: Total time per day + +Each derived table includes timestamps for data lineage tracking. \ No newline at end of file diff --git a/analyze_data.py b/analyze_data.py new file mode 100644 index 0000000..5bed412 --- /dev/null +++ b/analyze_data.py @@ -0,0 +1,157 @@ +import duckdb +import pandas as pd +from datetime import datetime + +# Connect to the database +try: + con = duckdb.connect('working_times.db') + print("Connected to working_times.db") +except Exception as e: + print(f"Error connecting to database: {e}") + exit(1) + +# Get the current analysis timestamp +analysis_timestamp = datetime.now() + +# Function to format hours +def format_hours(hours): + return f"{hours:.2f}h" + +# Function to format days +def format_days(days): + return f"{days:.2f}d" + +# Get the date range of the data +date_range = con.execute(""" + SELECT MIN(date) AS start_date, MAX(date) AS end_date + FROM daily_summary +""").fetchone() + +start_date = date_range[0] +end_date = date_range[1] + +# Get the transformation timestamp (most recent) +transform_info = con.execute(""" + SELECT + MAX(transform_timestamp) AS transform_timestamp, + MAX(source_import_timestamp) AS source_import_timestamp + FROM daily_summary +""").fetchone() + +transform_timestamp = transform_info[0] +source_import_timestamp = transform_info[1] + +print("\n" + "="*60) +print(f"WORKING TIME ANALYSIS: {start_date} to {end_date}") +print(f"ANALYSIS TIMESTAMP: {analysis_timestamp}") +print(f"DATA TRANSFORMATION: {transform_timestamp}") +print(f"DATA IMPORT: {source_import_timestamp}") +print("="*60) + +# Get the total hours and days worked +totals = con.execute(""" + SELECT SUM(total_hours) AS total_hours, SUM(total_days) AS total_days + FROM project_summary +""").fetchone() + +total_hours = totals[0] +total_days = totals[1] +num_working_days = con.execute("SELECT COUNT(*) FROM daily_summary WHERE total_hours > 0").fetchone()[0] +avg_hours_per_day = total_hours / num_working_days if num_working_days > 0 else 0 + +print(f"\nTOTAL HOURS: {format_hours(total_hours)}") +print(f"TOTAL DAYS: {format_days(total_days)}") +print(f"WORKING DAYS: {num_working_days}") +print(f"AVG HOURS PER WORKING DAY: {format_hours(avg_hours_per_day)}") + +# Get the top projects by hours +top_projects = con.execute(""" + SELECT project_name, total_hours, total_days, days_worked + FROM project_summary + ORDER BY total_hours DESC + LIMIT 5 +""").fetchall() + +print("\n" + "-"*60) +print("TOP 5 PROJECTS BY HOURS") +print("-"*60) +for i, (project, hours, days, worked_days) in enumerate(top_projects, 1): + percent = (hours / total_hours) * 100 + print(f"{i}. {project}") + print(f" {format_hours(hours)} ({percent:.1f}% of total) / {format_days(days)}") + print(f" Across {worked_days} days, daily average: {format_hours(hours/worked_days) if worked_days > 0 else 0}") + +# Get the busiest days +busiest_days = con.execute(""" + SELECT date, total_hours, project_count + FROM daily_summary + WHERE total_hours > 0 + ORDER BY total_hours DESC + LIMIT 5 +""").fetchall() + +print("\n" + "-"*60) +print("TOP 5 BUSIEST DAYS") +print("-"*60) +for i, (date, hours, project_count) in enumerate(busiest_days, 1): + # Calculate day equivalent + day_equivalent = hours / 8 + print(f"{i}. {date}: {format_hours(hours)} ({format_days(day_equivalent)}) across {project_count} projects") + +# Get day distribution +day_distribution = con.execute(""" + SELECT + CASE + WHEN total_hours <= 4 THEN '0-4 hours' + WHEN total_hours <= 6 THEN '4-6 hours' + WHEN total_hours <= 8 THEN '6-8 hours' + WHEN total_hours <= 10 THEN '8-10 hours' + ELSE '10+ hours' + END AS hour_range, + COUNT(*) as day_count + FROM daily_summary + WHERE total_hours > 0 + GROUP BY hour_range + ORDER BY + CASE + WHEN hour_range = '0-4 hours' THEN 1 + WHEN hour_range = '4-6 hours' THEN 2 + WHEN hour_range = '6-8 hours' THEN 3 + WHEN hour_range = '8-10 hours' THEN 4 + ELSE 5 + END +""").fetchall() + +print("\n" + "-"*60) +print("DAY DISTRIBUTION") +print("-"*60) +for hour_range, day_count in day_distribution: + percent = (day_count / num_working_days) * 100 + print(f"{hour_range}: {day_count} days ({percent:.1f}%)") + +# Print an overview of project/activity combinations +project_activity_combo = con.execute(""" + SELECT + project_name, + activity_type, + SUM(total_hours) as hours, + SUM(total_days) as days + FROM working_times_summary + GROUP BY project_name, activity_type + ORDER BY hours DESC + LIMIT 10 +""").fetchall() + +print("\n" + "-"*60) +print("TOP 10 PROJECT-ACTIVITY COMBINATIONS") +print("-"*60) +for project, activity, hours, days in project_activity_combo: + percent = (hours / total_hours) * 100 + print(f"{project} - {activity}: {format_hours(hours)} ({format_days(days)}, {percent:.1f}%)") + +print("\n" + "="*60) +print(f"END OF ANALYSIS - Generated at {analysis_timestamp}") +print("="*60) + +# Close the connection +con.close() \ No newline at end of file diff --git a/import_data.py b/import_data.py new file mode 100644 index 0000000..762d228 --- /dev/null +++ b/import_data.py @@ -0,0 +1,65 @@ +import duckdb +import pandas as pd +import os +import datetime + +# Create connection to DuckDB +con = duckdb.connect('working_times.db') + +# Path to the CSV file +csv_file = 'data/lawi-2025-04-01-2025-04-30-2025-04-17.csv' + +# Check if file exists +if not os.path.exists(csv_file): + print(f"Error: File {csv_file} not found") + exit(1) + +print(f"Importing data from {csv_file}...") + +# Current timestamp for the import +import_timestamp = datetime.datetime.now() +print(f"Import timestamp: {import_timestamp}") + +# First, create a temporary table with the CSV data +con.execute(""" + CREATE TABLE IF NOT EXISTS temp_working_times AS + SELECT * FROM read_csv_auto( + '{csv_file}', + delim=';', + header=true, + ignore_errors=true, + sample_size=1000, + auto_detect=true, + decimal_separator=',' + ) +""".format(csv_file=csv_file)) + +# Drop the existing table if it exists +con.execute("DROP TABLE IF EXISTS working_times") + +# Now create the final table with the timestamp column +con.execute(""" + CREATE TABLE working_times AS + SELECT + *, + '{timestamp}' AS import_timestamp + FROM temp_working_times +""".format(timestamp=import_timestamp)) + +# Drop the temporary table +con.execute("DROP TABLE IF EXISTS temp_working_times") + +# Verify the data was imported +count = con.execute("SELECT COUNT(*) FROM working_times").fetchone()[0] +print(f"Successfully imported {count} records into the working_times table.") + +# Show the table schema +print("\nTable Schema:") +schema = con.execute("DESCRIBE working_times").fetchall() +for col in schema: + print(f"{col[0]}: {col[1]}") + +# Close the connection +con.close() + +print("\nData import complete. Database saved to working_times.db") \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b5a002a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +duckdb==1.2.2 +pandas>=2.2.0 +python-dateutil>=2.8.2 +pytz>=2025.1 +numpy>=1.22.4 \ No newline at end of file diff --git a/run_analysis.py b/run_analysis.py new file mode 100644 index 0000000..f0a9865 --- /dev/null +++ b/run_analysis.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +""" +Working Time Analysis - Complete Workflow +This script runs all three steps of the analysis in sequence: +1. Import the data +2. Transform the data +3. Generate analysis reports +""" + +import os +import sys +import subprocess +import time + +def run_step(script_name, step_desc): + """Run a step in the analysis and handle errors""" + print(f"\n{'='*60}") + print(f"STEP: {step_desc}") + print(f"{'='*60}") + + try: + # Run the script and capture output + result = subprocess.run( + [sys.executable, script_name], + capture_output=True, + text=True, + check=True + ) + print(result.stdout) + return True + except subprocess.CalledProcessError as e: + print(f"ERROR in {script_name}:") + print(e.stderr) + return False + +def main(): + # Check if the CSV file exists + csv_file = 'data/lawi-2025-04-01-2025-04-30-2025-04-17.csv' + if not os.path.exists(csv_file): + print(f"Error: CSV file not found at {csv_file}") + return False + + # Step 1: Import Data + if not run_step('import_data.py', 'IMPORTING DATA'): + return False + + # Wait a moment to ensure any file locks are released + time.sleep(1) + + # Step 2: Transform Data + if not run_step('transform_data.py', 'TRANSFORMING DATA'): + return False + + # Wait a moment to ensure any file locks are released + time.sleep(1) + + # Step 3: Analyze Data + if not run_step('analyze_data.py', 'ANALYZING DATA'): + return False + + print("\n" + "="*60) + print("ANALYSIS COMPLETE") + print("="*60) + return True + +if __name__ == "__main__": + success = main() + sys.exit(0 if success else 1) \ No newline at end of file diff --git a/transform_data.py b/transform_data.py new file mode 100644 index 0000000..7883204 --- /dev/null +++ b/transform_data.py @@ -0,0 +1,118 @@ +import duckdb +import pandas as pd +import os +import time +import datetime + +# Try to connect to the database with retry logic +max_retries = 5 +retry_count = 0 +connected = False + +print("Trying to connect to working_times.db...") + +while not connected and retry_count < max_retries: + try: + # Try to connect with 'access_mode=read_only' to avoid lock conflicts + con = duckdb.connect('working_times.db') + connected = True + print("Connected to working_times.db") + except Exception as e: + retry_count += 1 + print(f"Connection attempt {retry_count} failed: {e}") + if retry_count < max_retries: + print(f"Retrying in {retry_count} seconds...") + time.sleep(retry_count) + else: + print("Maximum retries reached. Exiting.") + exit(1) + +print("Transforming data...") + +# Get the transformation timestamp +transform_timestamp = datetime.datetime.now() +print(f"Transform timestamp: {transform_timestamp}") + +# Create a new table with transformed data +# This query will: +# 1. Extract date and project information +# 2. Calculate total hours per project per day +# 3. Format the data in a more analytical friendly way +con.execute(""" + DROP TABLE IF EXISTS working_times_summary; + CREATE TABLE working_times_summary AS + SELECT + Datum AS date, + Projektname AS project_name, + "Leistungsart (Bezeichnung)" AS activity_type, + SUM("Zeit [h]") AS total_hours, + SUM("Zeit [h]"/8) AS total_days, + '{transform_timestamp}' AS transform_timestamp + FROM working_times + GROUP BY date, project_name, activity_type + ORDER BY date, project_name, activity_type; +""".format(transform_timestamp=transform_timestamp)) + +# Create a table with project totals +con.execute(""" + DROP TABLE IF EXISTS project_summary; + CREATE TABLE project_summary AS + SELECT + Projektname AS project_name, + SUM("Zeit [h]") AS total_hours, + SUM("Zeit [h]"/8) AS total_days, + COUNT(DISTINCT Datum) AS days_worked, + MAX(import_timestamp) AS source_import_timestamp, + '{transform_timestamp}' AS transform_timestamp + FROM working_times + GROUP BY project_name + ORDER BY total_hours DESC; +""".format(transform_timestamp=transform_timestamp)) + +# Create a table with daily totals +con.execute(""" + DROP TABLE IF EXISTS daily_summary; + CREATE TABLE daily_summary AS + SELECT + Datum AS date, + SUM("Zeit [h]") AS total_hours, + COUNT(*) AS entry_count, + COUNT(DISTINCT Projektname) AS project_count, + MAX(import_timestamp) AS source_import_timestamp, + '{transform_timestamp}' AS transform_timestamp + FROM working_times + GROUP BY date + ORDER BY date; +""".format(transform_timestamp=transform_timestamp)) + +# Verify the data was transformed +summary_count = con.execute("SELECT COUNT(*) FROM working_times_summary").fetchone()[0] +project_count = con.execute("SELECT COUNT(*) FROM project_summary").fetchone()[0] +daily_count = con.execute("SELECT COUNT(*) FROM daily_summary").fetchone()[0] + +print(f"Successfully created {summary_count} records in working_times_summary table.") +print(f"Successfully created {project_count} records in project_summary table.") +print(f"Successfully created {daily_count} records in daily_summary table.") + +# Print a sample of the summary table +print("\nSample of working_times_summary table:") +summary_sample = con.execute("SELECT date, project_name, activity_type, total_hours, total_days, transform_timestamp FROM working_times_summary LIMIT 5").fetchall() +for row in summary_sample: + print(row) + +# Print a sample of the project summary table +print("\nProject summary (top 5 by hours):") +project_sample = con.execute("SELECT project_name, total_hours, total_days, days_worked, transform_timestamp FROM project_summary LIMIT 5").fetchall() +for row in project_sample: + print(row) + +# Total hours worked +total_hours = con.execute("SELECT SUM(total_hours) FROM project_summary").fetchone()[0] +total_days = con.execute("SELECT SUM(total_days) FROM project_summary").fetchone()[0] +print(f"\nTotal hours worked: {total_hours:.2f}") +print(f"Total days worked: {total_days:.2f}") + +# Close the connection +con.close() + +print("\nData transformation complete.") \ No newline at end of file diff --git a/working_times.db b/working_times.db new file mode 100644 index 0000000000000000000000000000000000000000..4db2a6f1403ca40329aef4b68b58a8e33ca9fd5a GIT binary patch literal 2109440 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 z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs 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