Skip to content
Open
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
203 changes: 203 additions & 0 deletions MEETING_ATTENDANCE_TRACKING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
# Meeting Attendance Tracking System

This document describes the meeting attendance tracking system implemented in the Labs GraphQL API.

## Overview

The system provides a **two-pronged approach** to tracking student meeting attendance:

1. **Immediate (Backup):** Capture attendance via mentor reflections/surveys
2. **Future (Primary):** Automatically track attendance from Slack meetings (prepared for future implementation)

## Architecture

### Database Schema

**New Enum: `AttendanceSource`**
- `SLACK_HUDDLE` - Direct tracking from Slack huddle API (future)
- `MESSAGE_ACTIVITY` - Inferred from message activity during meeting time (future)
- `MENTOR_REPORT` - From mentor reflection surveys (active)
- `MANUAL` - Manually entered by admin

**Extended Models:**

**`Meeting`**
- `projectId` - Links meetings to specific projects
- `slackHuddleId` - Slack huddle identifier for automatic tracking
- `scheduledStartAt` - Scheduled meeting start time
- `scheduledEndAt` - Scheduled meeting end time

**`MeetingAttendance`**
- `source` - Tracks where attendance data came from (AttendanceSource)
- `confidence` - Quality score (0.0-1.0) for inferred data
- `metadata` - Additional context (e.g., reflection ID, message count)

**`Project`**
- `meetings` - One-to-many relation to meetings

### GraphQL API

**Types:**
- `Meeting` - Represents a scheduled meeting
- `MeetingAttendance` - Attendance record for a student at a meeting
- `MeetingResponse` - Student's agenda/notes for a meeting
- `StudentAttendanceStat` - Attendance statistics for a student
- `MentorReflectionStat` - Reflection completion statistics for a mentor
- `FlaggedStudent` - Students needing attention

**Queries:**
- `meetings(eventId, projectId)` - List meetings
- `meeting(id)` - Get single meeting
- `meetingAttendance(meetingId)` - Get attendance for a meeting
- `statStudentAttendance(eventId, projectId, minAttendance)` - Get attendance stats
- `statMentorReflectionCompletion(eventId)` - Track mentor reflection submissions
- `flaggedStudents(eventId, minAttendance)` - Get students needing attention

**Mutations:**
- `createMeeting(data)` - Create new meeting
- `recordMeetingAttendance(data)` - Record/update attendance (upsert logic)

### Automation Tasks

**`processMentorReflections`** (runs every Monday at 6 AM)
- Processes mentor reflection survey responses
- Extracts attendance data from response JSON
- Automatically creates Meeting records for the week if needed
- Records MeetingAttendance with source=MENTOR_REPORT
- Supports multiple response formats:
- `response.meetingHeld` - whether meeting occurred
- `response.studentAttendance` - array of attending student IDs
- `response.studentsPresent` - alternative format

**`sendAttendanceAlerts`** (runs every Monday at 9 AM)
- Checks all active events for attendance issues
- Identifies students with <75% attendance (minimum 2 meetings)
- Identifies mentors behind on reflections
- Sends Slack alerts to the `#stats` channel with summary
- Email template available for weekly reports

## Usage

### For Admins: Adding Attendance Question to Mentor Surveys

To enable attendance tracking via mentor reflections, add this question to your mentor reflection survey:

```json
{
"type": "object",
"properties": {
"meetingHeld": {
"type": "boolean",
"title": "Did you hold a meeting with your student(s) this week?"
},
"studentAttendance": {
"type": "array",
"title": "Which students attended your meeting?",
"items": {
"type": "string",
"enum": ["student-id-1", "student-id-2", "student-id-3"]
},
"uniqueItems": true
}
}
}
```

The automation task will automatically process responses and create attendance records.

### For Developers: Querying Attendance Data

**Get attendance stats for an event:**
```graphql
query {
statStudentAttendance(eventId: "event-id") {
student { givenName surname email }
project { description }
meetingsTotal
meetingsAttended
attendancePercentage
isFlagged
dataSources
lastAttendedAt
}
}
```

**Get flagged students:**
```graphql
query {
flaggedStudents(eventId: "event-id", minAttendance: 0.75) {
student { givenName surname }
mentor { givenName surname }
reason
attendancePercentage
missedMeetings
}
}
```

**Record manual attendance:**
```graphql
mutation {
recordMeetingAttendance(data: {
meetingId: "meeting-id"
studentId: "student-id"
attended: true
source: MANUAL
}) {
id attended source
}
}
```

## Future Enhancements (Phase 5+)

### Slack Message Activity Tracking
- Infer attendance from message activity during meeting times
- Source: `MESSAGE_ACTIVITY`, Confidence: 0.7

### Slack Huddle API Integration
- Direct tracking from Slack huddle events
- Real-time attendance capture
- Source: `SLACK_HUDDLE`, Confidence: 1.0

### Hybrid Attendance Resolution
- Merge attendance from multiple sources
- Priority system: SLACK_HUDDLE > MENTOR_REPORT > MESSAGE_ACTIVITY > MANUAL
- Flag conflicts for review

## Migration

To apply the database changes:

```bash
# Run the migration
npx prisma migrate deploy

# Or for development
npx prisma migrate dev
```

## Testing

1. Create a test meeting:
```graphql
mutation {
createMeeting(data: {
eventId: "test-event"
projectId: "test-project"
visibleAt: "2025-01-01T00:00:00Z"
dueAt: "2025-01-08T00:00:00Z"
scheduledStartAt: "2025-01-03T14:00:00Z"
scheduledEndAt: "2025-01-03T15:00:00Z"
}) { id }
}
```

2. Record attendance
3. Check stats with `statStudentAttendance`
4. Verify alert in Slack mentor channel (Mondays at 9 AM)

## Support

For questions or issues, contact the engineering team or open an issue in the repository.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"prisma": "prisma format && prisma generate",
"dev": "ts-node-dev --no-notify --respawn --transpile-only src",
"debug": "ts-node-dev --no-notify --respawn src",
"test:attendance": "ts-node --transpile-only src/automation/tasks/testProcessMentorReflections.ts && ts-node --transpile-only src/automation/tasks/testSendAttendanceAlerts.ts",
"test:attendance-slack": "ts-node --transpile-only scripts/testAttendanceSlack.ts",
"send-event-recommendations": "ts-node scripts/sendEventRecommendations.ts",
"swagger": "rm src/badgr/Api.ts; swagger-typescript-api -p badgr-api-v2.yaml -n Api2.ts -o ./src/badgr; echo 'type json = JSON;' | cat - src/badgr/Api2.ts > src/badgr/Api.ts; rm src/badgr/Api2.ts"
},
Expand Down Expand Up @@ -88,4 +90,4 @@
"ts-node-dev": "^1.1.6",
"typescript": "^5.2.2"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-- CreateEnum: Add attendance source tracking
CREATE TYPE "AttendanceSource" AS ENUM ('SLACK_HUDDLE', 'MESSAGE_ACTIVITY', 'MENTOR_REPORT', 'MANUAL');

-- AlterTable: Add attendance tracking fields to MeetingAttendance
ALTER TABLE "MeetingAttendance" ADD COLUMN "source" "AttendanceSource" NOT NULL DEFAULT 'MANUAL';
ALTER TABLE "MeetingAttendance" ADD COLUMN "confidence" DOUBLE PRECISION NOT NULL DEFAULT 1.0;
ALTER TABLE "MeetingAttendance" ADD COLUMN "metadata" JSONB;

-- AlterTable: Add Slack and project fields to Meeting
ALTER TABLE "Meeting" ADD COLUMN "slackHuddleId" TEXT;
ALTER TABLE "Meeting" ADD COLUMN "scheduledStartAt" TIMESTAMP(3);
ALTER TABLE "Meeting" ADD COLUMN "scheduledEndAt" TIMESTAMP(3);
ALTER TABLE "Meeting" ADD COLUMN "projectId" TEXT;

-- AddForeignKey: Link meetings to projects
ALTER TABLE "Meeting" ADD CONSTRAINT "Meeting_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE SET NULL ON UPDATE CASCADE;

-- CreateIndex: Add index for source-based queries
CREATE INDEX "MeetingAttendance_source_idx" ON "MeetingAttendance"("source");

-- CreateIndex: Add index for project-based meeting queries
CREATE INDEX "Meeting_projectId_idx" ON "Meeting"("projectId");
25 changes: 25 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,22 @@ model Meeting {
notesStudentSchema Json?
notesStudentUi Json?

// Slack Integration
slackHuddleId String?
scheduledStartAt DateTime?
scheduledEndAt DateTime?

// Relations
event Event @relation(fields: [eventId], references: [id])
eventId String

project Project? @relation(fields: [projectId], references: [id])
projectId String?

responses MeetingResponse[]
attendance MeetingAttendance[]

@@index([projectId])
}

model MeetingAttendance {
Expand All @@ -239,12 +249,19 @@ model MeetingAttendance {
attended Boolean @default(false)
prepared Boolean @default(false)

// Attendance tracking
source AttendanceSource @default(MANUAL)
confidence Float @default(1.0)
metadata Json?

// Relations
meeting Meeting @relation(fields: [meetingId], references: [id])
meetingId String

student Student? @relation(fields: [studentId], references: [id])
studentId String?

@@index([source])
}

model MeetingResponse {
Expand Down Expand Up @@ -490,6 +507,13 @@ enum PrStatus {
CLOSED_MERGED
}

enum AttendanceSource {
SLACK_HUDDLE
MESSAGE_ACTIVITY
MENTOR_REPORT
MANUAL
}

model Project {
// Metadata
id String @id @default(cuid())
Expand Down Expand Up @@ -536,6 +560,7 @@ model Project {
standupResults StandupResult[]
artifacts Artifact[]
files File[]
meetings Meeting[]
}

model ArtifactType {
Expand Down
Loading
Loading