| 9 | namespace Backend.Controllers; |
| 10 | |
| 11 | [ApiController] |
| 12 | [Route("api/[controller]")] |
| 13 | [Authorize] |
| 14 | public class AnalyticsController : ControllerBase |
| 15 | { |
| 16 | private readonly AppDbContext _db; |
| 17 | |
| 18 | public AnalyticsController(AppDbContext db) |
| 19 | { |
| 20 | _db = db; |
| 21 | } |
| 22 | |
| 23 | [HttpGet("board/{boardId}")] |
| 24 | public async Task<IActionResult> GetBoardAnalytics(int boardId) |
| 25 | { |
| 26 | // 1. Verify User has access to the board |
| 27 | var userId = GetUserId(); |
| 28 | var board = await _db.Boards.FindAsync(boardId); |
| 29 | if (board == null) return NotFound(); |
| 30 | |
| 31 | var hasAccess = board.OwnerId == userId || |
| 32 | await _db.BoardMembers.AnyAsync(m => m.BoardId == boardId && m.UserId == userId && m.Status == "Accepted") || |
| 33 | await _db.WorkspaceMembers.AnyAsync(wm => wm.WorkspaceId == board.WorkspaceId && wm.UserId == userId && wm.Status == "Accepted"); |
| 34 | |
| 35 | if (!hasAccess) return Forbid(); |
| 36 | |
| 37 | // 2. Aggregate Task Data with basic projections |
| 38 | var taskData = await _db.TaskCards |
| 39 | .AsNoTracking() |
| 40 | .Where(t => t.Column.BoardId == boardId) |
| 41 | .Select(t => new |
| 42 | { |
| 43 | t.Id, |
| 44 | t.ColumnId, |
| 45 | t.CreatedAt, |
| 46 | TimeLogs = t.TimeLogs.Select(tl => new { tl.DurationMinutes, Username = tl.User.Username }) |
| 47 | }) |
| 48 | .ToListAsync(); |
| 49 | |
| 50 | int totalTasks = taskData.Count; |
| 51 | |
| 52 | // Let's assume the right-most column means "Done", or any column named "Done" / "Completed" |
| 53 | // Since we don't have a strict strict "Done" flag on the Task itself mapping to Agile, |
| 54 | // Let's find columns containing 'Done' or 'Complete' |
| 55 | var doneColumnIds = await _db.Columns |
| 56 | .AsNoTracking() |
| 57 | .Where(c => c.BoardId == boardId && (c.Name.ToLower().Contains("done") || c.Name.ToLower().Contains("complete"))) |
| 58 | .Select(c => c.Id) |
| 59 | .ToListAsync(); |
| 60 | |
| 61 | int completedTasks = taskData.Count(t => doneColumnIds.Contains(t.ColumnId)); |
| 62 | int pendingTasks = totalTasks - completedTasks; |
| 63 | |
| 64 | // 3. User Time Tracking Aggregation |
| 65 | var userTimeData = taskData |
| 66 | .SelectMany(t => t.TimeLogs) |
| 67 | .Where(tl => tl.DurationMinutes.HasValue) |
| 68 | .GroupBy(tl => tl.Username ?? "Unknown") |
nothing calls this directly
no outgoing calls
no test coverage detected