Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,8 @@ node_modules/

# Temporary files
*.tmp
*.temp
*.temp

# User uploaded content
static/uploads/avatars/*
!static/uploads/avatars/.gitkeep
6 changes: 6 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

ENV LANG=en_US.UTF-8
ENV TZ=Europe/Berlin

# Create www-data user and log directory
RUN groupadd -r www-data && useradd -r -g www-data www-data || true
RUN mkdir -p /var/log/uwsgi && chown -R www-data:www-data /var/log/uwsgi
Expand All @@ -35,6 +38,9 @@ COPY . .
# Create the SQLite database directory with proper permissions
RUN mkdir -p /app/instance && chmod 777 /app/instance

# Create uploads directory with proper permissions
RUN mkdir -p /app/static/uploads/avatars && chmod -R 777 /app/static/uploads

VOLUME /data
RUN mkdir /data && chmod 777 /data

Expand Down
2,650 changes: 2,395 additions & 255 deletions app.py

Large diffs are not rendered by default.

63 changes: 62 additions & 1 deletion data_formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,65 @@ def format_team_data(entries, granularity='daily'):
'total_hours': round(data['total_hours'], 2)
})

return {'team_data': team_data}
return {'team_data': team_data}


def format_burndown_data(tasks, start_date, end_date):
"""Format data for burndown chart visualization."""
from datetime import datetime, timedelta
from models import Task, TaskStatus

if not tasks:
return {'burndown': {'dates': [], 'remaining': [], 'ideal': []}}

# Convert string dates to datetime objects if needed
if isinstance(start_date, str):
start_date = datetime.strptime(start_date, '%Y-%m-%d').date()
if isinstance(end_date, str):
end_date = datetime.strptime(end_date, '%Y-%m-%d').date()

# Generate date range
current_date = start_date
dates = []
while current_date <= end_date:
dates.append(current_date.strftime('%Y-%m-%d'))
current_date += timedelta(days=1)

total_tasks = len(tasks)
if total_tasks == 0:
return {'burndown': {'dates': dates, 'remaining': [0] * len(dates), 'ideal': [0] * len(dates)}}

# Calculate ideal burndown (linear decrease from total to 0)
total_days = len(dates)
ideal_burndown = []
for i in range(total_days):
remaining_ideal = total_tasks - (total_tasks * i / (total_days - 1)) if total_days > 1 else 0
ideal_burndown.append(max(0, round(remaining_ideal, 1)))

# Calculate actual remaining tasks for each date
actual_remaining = []
for date_str in dates:
date_obj = datetime.strptime(date_str, '%Y-%m-%d').date()

# Count tasks not completed by this date
remaining_count = 0
for task in tasks:
# Task is remaining if:
# 1. It's not completed, OR
# 2. It was completed after this date
if task.status != TaskStatus.COMPLETED:
remaining_count += 1
elif task.completed_date and task.completed_date > date_obj:
remaining_count += 1

actual_remaining.append(remaining_count)

return {
'burndown': {
'dates': dates,
'remaining': actual_remaining,
'ideal': ideal_burndown,
'total_tasks': total_tasks,
'tasks_completed': total_tasks - (actual_remaining[-1] if actual_remaining else total_tasks)
}
}
Loading
Loading