Skip to content

Commit 95f8e29

Browse files
authored
Merge pull request #50 from AhmedIkram05:fix/github-integration-latency
Fix/github-integration-latency
2 parents 14de1cf + 96672c3 commit 95f8e29

23 files changed

Lines changed: 641 additions & 207 deletions

backend/migrations/alembic.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# A generic, single database configuration.
22

33
[alembic]
4+
# path to migration scripts
5+
script_location = .
6+
47
# template used to generate migration files
58
# file_template = %%(rev)s_%%(slug)s
69

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""Add priority to tasks and github_connected to users
2+
3+
Revision ID: 3c8d9e2f1a4b
4+
Revises: d8b7f3a2c1e4
5+
Create Date: 2026-05-06 10:00:00.000000
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
11+
12+
# revision identifiers, used by Alembic.
13+
revision = '3c8d9e2f1a4b'
14+
down_revision = 'd8b7f3a2c1e4'
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade():
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
op.add_column('tasks', sa.Column('priority', sa.String(length=20), nullable=True, server_default='medium'))
22+
op.add_column('users', sa.Column('github_connected', sa.Boolean(), nullable=True, server_default='false'))
23+
# ### end Alembic commands ###
24+
25+
26+
def downgrade():
27+
# ### commands auto generated by Alembic - please adjust! ###
28+
op.drop_column('users', 'github_connected')
29+
op.drop_column('tasks', 'priority')
30+
# ### end Alembic commands ###

backend/src/api/controllers/admin_controller.py

Lines changed: 38 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,55 +4,51 @@
44
from ...db.models import db, User, Project, Task
55
from ..validators.admin_validator import validate_system_settings, validate_user_role_update
66
from ...auth.rbac import Role
7-
from sqlalchemy import func, case
7+
8+
9+
def _safe_query_all(model):
10+
try:
11+
return model.query.all()
12+
except Exception:
13+
return []
14+
15+
16+
def _count(items, predicate):
17+
return sum(1 for item in items if predicate(item))
818

919
def get_system_stats():
1020
"""Controller function to get system statistics for admin dashboard"""
11-
12-
# Single query per table instead of loading all rows into memory
13-
user_stats = db.session.query(
14-
func.count(User.id).label('total'),
15-
func.count(case((User.role == Role.ADMIN.value, 1))).label('admins'),
16-
func.count(case((User.role == Role.TEAM_LEAD.value, 1))).label('team_leads'),
17-
func.count(case((User.role == Role.DEVELOPER.value, 1))).label('developers'),
18-
).one()
1921

20-
project_stats = db.session.query(
21-
func.count(Project.id).label('total'),
22-
func.count(case((Project.status == 'active', 1))).label('active'),
23-
func.count(case((Project.status == 'completed', 1))).label('completed'),
24-
func.count(case((Project.status == 'on_hold', 1))).label('on_hold'),
25-
).one()
22+
users = _safe_query_all(User)
23+
projects = _safe_query_all(Project)
24+
tasks = _safe_query_all(Task)
2625

27-
task_stats = db.session.query(
28-
func.count(Task.id).label('total'),
29-
func.count(case((Task.status == 'todo', 1))).label('todo'),
30-
func.count(case((Task.status == 'in_progress', 1))).label('in_progress'),
31-
func.count(case((Task.status == 'review', 1))).label('review'),
32-
# Cover both 'done' and 'completed' in case of mixed data
33-
func.count(case((Task.status.in_(['done', 'completed']), 1))).label('done'),
34-
).one()
26+
user_stats = {
27+
'total': len(users),
28+
'admins': _count(users, lambda user: getattr(user, 'role', None) == Role.ADMIN.value),
29+
'team_leads': _count(users, lambda user: getattr(user, 'role', None) == Role.TEAM_LEAD.value),
30+
'developers': _count(users, lambda user: getattr(user, 'role', None) == Role.DEVELOPER.value),
31+
}
32+
33+
project_stats = {
34+
'total': len(projects),
35+
'active': _count(projects, lambda project: getattr(project, 'status', None) == 'active'),
36+
'completed': _count(projects, lambda project: getattr(project, 'status', None) == 'completed'),
37+
'on_hold': _count(projects, lambda project: getattr(project, 'status', None) == 'on_hold'),
38+
}
39+
40+
task_stats = {
41+
'total': len(tasks),
42+
'todo': _count(tasks, lambda task: getattr(task, 'status', None) == 'todo'),
43+
'in_progress': _count(tasks, lambda task: getattr(task, 'status', None) == 'in_progress'),
44+
'review': _count(tasks, lambda task: getattr(task, 'status', None) == 'review'),
45+
'done': _count(tasks, lambda task: getattr(task, 'status', None) in {'done', 'completed'}),
46+
}
3547

3648
return jsonify({
37-
'users': {
38-
'total': user_stats.total,
39-
'admins': user_stats.admins,
40-
'team_leads': user_stats.team_leads,
41-
'developers': user_stats.developers,
42-
},
43-
'projects': {
44-
'total': project_stats.total,
45-
'active': project_stats.active,
46-
'completed': project_stats.completed,
47-
'on_hold': project_stats.on_hold,
48-
},
49-
'tasks': {
50-
'total': task_stats.total,
51-
'todo': task_stats.todo,
52-
'in_progress': task_stats.in_progress,
53-
'review': task_stats.review,
54-
'done': task_stats.done, # ← frontend reads tasks.done
55-
}
49+
'users': user_stats,
50+
'projects': project_stats,
51+
'tasks': task_stats,
5652
})
5753

5854
def get_system_settings():

backend/src/api/controllers/comments_controller.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,16 @@ def get_task_comments(task_id):
1919
for comment in comments:
2020
user = User.query.get(comment.user_id)
2121

22+
updated_at = getattr(comment, 'updated_at', None)
2223
comment_data = {
2324
'id': comment.id,
2425
'content': comment.content,
2526
'user_id': comment.user_id,
2627
'user_name': user.name if user else 'Unknown',
28+
'author_name': user.name if user else 'Unknown',
2729
'user_avatar': getattr(user, 'avatar', None) if user else None,
2830
'created_at': comment.created_at.isoformat() if comment.created_at else None,
29-
'updated_at': comment.updated_at.isoformat() if comment.updated_at else None
31+
'updated_at': updated_at.isoformat() if updated_at else None
3032
}
3133
comments_data.append(comment_data)
3234

@@ -66,6 +68,7 @@ def add_comment(task_id):
6668
'content': new_comment.content,
6769
'user_id': user_id,
6870
'user_name': user.name if user else 'Unknown',
71+
'author_name': user.name if user else 'Unknown',
6972
'user_avatar': getattr(user, 'avatar', None) if user else None,
7073
'created_at': new_comment.created_at.isoformat() if new_comment.created_at else None
7174
}

backend/src/api/controllers/dashboard_controller.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,22 @@
66
from datetime import datetime, timedelta
77
import traceback
88
import logging
9-
from sqlalchemy import func, case
109

1110
# Configure logging
1211
logging.basicConfig(level=logging.INFO)
1312
logger = logging.getLogger(__name__)
1413

14+
15+
def _safe_query_all(model):
16+
try:
17+
return model.query.all()
18+
except Exception:
19+
return []
20+
21+
22+
def _count(items, predicate):
23+
return sum(1 for item in items if predicate(item))
24+
1525
def get_user_tasks(user_id):
1626
"""Helper function to get all tasks for a user"""
1727
try:
@@ -186,21 +196,12 @@ def get_client_dashboard():
186196
# Get tasks assigned to this user
187197
assigned_tasks = get_user_tasks(user_id)
188198

189-
# Get task statistics
190-
counts = db.session.query(
191-
func.count(Task.id).label('total'),
192-
func.count(case((Task.status == 'todo', 1))).label('todo'),
193-
func.count(case((Task.status == 'in_progress', 1))).label('in_progress'),
194-
func.count(case((Task.status == 'review', 1))).label('review'),
195-
func.count(case((Task.status.in_(['done', 'completed']), 1))).label('done'),
196-
).one()
197-
198199
task_stats = {
199-
'total': counts.total,
200-
'todo': counts.todo,
201-
'in_progress': counts.in_progress,
202-
'review': counts.review,
203-
'done': counts.done,
200+
'total': len(assigned_tasks),
201+
'todo': _count(assigned_tasks, lambda task: getattr(task, 'status', None) == 'todo'),
202+
'in_progress': _count(assigned_tasks, lambda task: getattr(task, 'status', None) == 'in_progress'),
203+
'review': _count(assigned_tasks, lambda task: getattr(task, 'status', None) == 'review'),
204+
'done': _count(assigned_tasks, lambda task: getattr(task, 'status', None) in {'done', 'completed'}),
204205
}
205206

206207
# Get tasks due soon

0 commit comments

Comments
 (0)