From aece0ff3156c93e8cb5ffcb236b0b73ddb9f6e2b Mon Sep 17 00:00:00 2001 From: anirudh Date: Sat, 21 Oct 2023 20:17:41 +0530 Subject: [PATCH] feat(meeting): :sparkles: get attendance for meeting - use custom response struct --- controllers/meeting.controller.go | 22 ++++++++++++-- mocks/meeting.service_mocks.go | 13 +++++++++ models/meeting.model.go | 10 ++++++- routers/index.go | 3 ++ services/meeting.service.go | 48 ++++++++++++++++++++++++++----- 5 files changed, 85 insertions(+), 11 deletions(-) diff --git a/controllers/meeting.controller.go b/controllers/meeting.controller.go index c369fd6..41db58d 100644 --- a/controllers/meeting.controller.go +++ b/controllers/meeting.controller.go @@ -227,10 +227,7 @@ func (mc *MeetingController) MarkAttendance(c *gin.Context) { return } - // Retrieve the authenticated user from the context currentUser, _ := c.Get("user") - - // Type-assert the user to the models.User struct userID := currentUser.(*models.User).ID // Call the meeting service to mark attendance @@ -243,3 +240,22 @@ func (mc *MeetingController) MarkAttendance(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "Attendance marked successfully.", "onTime": onTime}) } + +// GetAttendanceForMeeting retrieves attendance for a meeting. +func (mc *MeetingController) GetAttendanceForMeeting(c *gin.Context) { + // Get meeting ID from route parameters + meetingID, teamID, err := getTeamAndMeetingFromQueryParams(c) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"message": "Invalid meeting or team ID", "error": err.Error()}) + return + } + + // Call the meeting service to get attendance for the meeting + attendance, err := mc.meetingService.GetAttendanceForMeeting(meetingID, teamID) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to get attendance for the meeting", "error": err.Error()}) + return + } + + c.JSON(http.StatusOK, attendance) +} diff --git a/mocks/meeting.service_mocks.go b/mocks/meeting.service_mocks.go index e988338..d2106ee 100644 --- a/mocks/meeting.service_mocks.go +++ b/mocks/meeting.service_mocks.go @@ -97,6 +97,14 @@ func (m *MockMeetingService) MarkAttendanceForUserInMeeting(userID, meetingID ui return ret0, ret1 } +// GetAttendanceForMeeting mocks the GetAttendanceForMeeting method. +func (m *MockMeetingService) GetAttendanceForMeeting(meetingID, teamID uint) ([]models.MeetingAttendanceListResponse, error) { + ret := m.ctrl.Call(m, "GetAttendanceForMeeting", meetingID, teamID) + ret0, _ := ret[0].([]models.MeetingAttendanceListResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + // MockMeetingServiceMockRecorder is a mock recorder for MockMeetingService. type MockMeetingServiceMockRecorder struct { mock *MockMeetingService @@ -145,3 +153,8 @@ func (mr *MockMeetingServiceMockRecorder) DeleteMeetingByID(meetingID, teamid ui func (mr *MockMeetingServiceMockRecorder) MarkAttendanceForUserInMeeting(userID, meetingID uint, attendanceTime time.Time, teamid uint) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarkAttendanceForUserInMeeting", reflect.TypeOf((*MockMeetingService)(nil).MarkAttendanceForUserInMeeting), userID, meetingID, attendanceTime, teamid) } + +// GetAttendanceForMeeting mocks the GetAttendanceForMeeting method. +func (mr *MockMeetingServiceMockRecorder) GetAttendanceForMeeting(meetingID, teamID uint) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttendanceForMeeting", reflect.TypeOf((*MockMeetingService)(nil).GetAttendanceForMeeting), meetingID, teamID) +} diff --git a/models/meeting.model.go b/models/meeting.model.go index a21946b..f43fa37 100644 --- a/models/meeting.model.go +++ b/models/meeting.model.go @@ -57,7 +57,7 @@ type MeetingAttendance struct { UserID uint `gorm:"primaryKey;not null"` MeetingID uint `gorm:"primaryKey;not null"` AttendanceMarkedAt time.Time `gorm:"not null"` - OnTime bool `gorm:"default:true"` + OnTime bool } func (ma *MeetingAttendance) BeforeCreate(tx *gorm.DB) error { @@ -69,3 +69,11 @@ func (ma *MeetingAttendance) BeforeCreate(tx *gorm.DB) error { } return nil } + +type MeetingAttendanceListResponse struct { + ID uint + MeetingID uint + AttendanceMarkedAt time.Time + OnTime bool + User User +} diff --git a/routers/index.go b/routers/index.go index 14f3494..a736817 100644 --- a/routers/index.go +++ b/routers/index.go @@ -153,6 +153,9 @@ func RegisterRoutes(route *gin.Engine) { // mark attendance for a user in a meeting team.PATCH("/:teamID/meetings/:meetingID/attendance", middleware.BaseAuthMiddleware(), middleware.AuthorizeMember(), meetingController.MarkAttendance) + + // admin get attendance for a meeting + team.GET("/:teamID/meetings/:meetingID/attendance", middleware.BaseAuthMiddleware(), middleware.AuthorizeAdmin(), meetingController.GetAttendanceForMeeting) } } diff --git a/services/meeting.service.go b/services/meeting.service.go index 0a6c750..eea08a1 100644 --- a/services/meeting.service.go +++ b/services/meeting.service.go @@ -29,6 +29,7 @@ type MeetingServiceInterface interface { EndAttendance(meetingID uint, teamid uint) (models.Meeting, error) DeleteMeetingByID(meetingID uint, teamid uint) error MarkAttendanceForUserInMeeting(userID, meetingID uint, attendanceTime time.Time, teamid uint) (bool, error) + GetAttendanceForMeeting(meetingID, teamID uint) ([]models.MeetingAttendanceListResponse, error) } // CreateMeeting creates a new meeting in the database. @@ -239,17 +240,19 @@ func (ms *MeetingService) MarkAttendanceForUserInMeeting(userID, meetingID uint, return false, errors.New("attendance already marked") } + var onTime bool + // if attendance period ended (but meeting period still on), mark attendance as late + if meeting.AttendanceOver && meeting.MeetingPeriod { + onTime = false + } else { + onTime = true + } + meetingAttendance := models.MeetingAttendance{ UserID: userID, MeetingID: meetingID, AttendanceMarkedAt: attendanceTime, - } - - // if attendance period ended (but meeting period still on), mark attendance as late - if meeting.AttendanceOver && meeting.MeetingPeriod { - meetingAttendance.OnTime = false - } else { - meetingAttendance.OnTime = true + OnTime: onTime, } if err := ms.meetingRepo.AddMeetingAttendance(meetingAttendance); err != nil { @@ -259,4 +262,35 @@ func (ms *MeetingService) MarkAttendanceForUserInMeeting(userID, meetingID uint, return meetingAttendance.OnTime, nil } +// GetAttendanceForMeeting retrieves attendance for a meeting. +func (ms *MeetingService) GetAttendanceForMeeting(meetingID, teamID uint) ([]models.MeetingAttendanceListResponse, error) { + _, err := ms.GetMeetingByID(meetingID, teamID) + if err != nil { + return []models.MeetingAttendanceListResponse{}, err + } + attendance, err := ms.meetingRepo.GetMeetingAttendanceByMeetingID(meetingID) + if err != nil { + return nil, err + } + + userRepo := repository.NewUserRepository() + // Get user details for each attendance record, and make array of MeetingAttendanceResponse + var attendanceResponse []models.MeetingAttendanceListResponse + for _, attendanceRecord := range attendance { + user, err := userRepo.GetUserByID(attendanceRecord.UserID) + if err != nil { + return nil, err + } + attendanceResponse = append(attendanceResponse, models.MeetingAttendanceListResponse{ + ID: attendanceRecord.ID, + MeetingID: attendanceRecord.MeetingID, + AttendanceMarkedAt: attendanceRecord.AttendanceMarkedAt, + OnTime: attendanceRecord.OnTime, + User: user, + }) + } + + return attendanceResponse, nil +} + // GetMeetingStatsByMeetingID retrieves meeting stats for a given meeting ID. Stats: total attendance, on time attendance, late attendance.