Gym Check-In Reports & Analytics
Overview
The gym check-in reporting system provides detailed attendance analytics and historical tracking. The system archives older check-in data to maintain performance while keeping complete historical records accessible.
Key Benefits:
- Fast dashboard performance even with thousands of check-ins
- Complete historical data preserved
- Daily, weekly, and monthly statistics
- Individual athlete attendance tracking
- Lifetime check-in counts
How the System Works
Two Types of Data Storage:
- Recent Check-Ins (Visible to Athletes)
- Each athlete can see their last 8 check-ins on their check-in screen
- Additionally, all check-ins from the last 60 days are kept as detailed records
- Used for recent activity and athlete tracking
- Faster queries for current statistics
- Archived Summaries (Older than 60 days AND beyond user’s 8 most recent)
- Daily summary records for historical periods
- Preserves statistics without storing every individual check-in
- Saves storage space while keeping historical data
- Combined with recent data for complete totals
- Important: Each user always keeps their 8 most recent check-ins visible, even if older than the archive cutoff
Example:
- Athlete View: Last 8 check-ins always visible (could span months if infrequent)
- Recent: Individual check-ins from last 60 days (detailed records)
- Archived: Daily totals from months/years ago (summary only)
- Dashboard: Combines both for accurate statistics
Viewing Check-In Reports
Accessing Reports
From Admin Dashboard:
- Open Admin Dashboard (hamburger menu in top-left)
- Statistics are visible on the main dashboard screen
- For detailed reports, go to User Management → Manage Users
- View check-in statistics section
From User Management:
- Open Admin Dashboard (hamburger menu)
- Tap User Management → Manage Users
- Scroll to “Gym Check-In Statistics” section
- View reports and archive options
Statistics Available
Dashboard Quick Stats:
- Today: Number of check-ins so far today
- This Week: Check-ins from Monday through today
- This Month: Check-ins from 1st of month through today
- Total: All-time check-ins (recent + archived)
- Unique Users: Count of different athletes who have checked in
Individual Athlete Stats:
- Total lifetime check-ins for each athlete
- Last check-in date and time
- Check-in frequency (average per week/month)
- Attendance trends
Date Range Reports:
- Select custom date ranges
- View check-ins for specific periods
- Compare time periods
- Export data for analysis
- Real-time progress indicator
- Detailed results showing:
- Check-ins processed
- Aggregates created
- Check-ins deleted (if applicable)
- Date range archived
Recommended Usage:
- Archive check-ins older than 30-60 days
- Enable “Delete after archive” to save storage
- Run monthly or as needed when performance degrades
Firestore Rules
match /gym_checkin_aggregates/{dateId} {
// All authenticated users can read aggregates
allow read: if request.auth != null;
// Only admins can create/update aggregates (via archive process)
allow create, update: if isAdminUser();
// Only admins can delete aggregates
allow delete: if isAdminUser();
}
Performance Benefits
Before Aggregates
- Dashboard queries all check-ins (potentially thousands)
- Slow queries as data grows over time
- Higher Firestore read costs
After Aggregates
- Dashboard queries recent check-ins + aggregates
- Fast queries regardless of historical data size
- Lower Firestore read costs
- Individual check-ins can be deleted after archiving
Example Savings
With 2,000 check-ins over 6 months:
- Before: 2,000 document reads per stats query
- After: ~30 recent check-ins + 180 aggregates = 210 reads
- Savings: ~90% reduction in read operations
If deleting after archive:
- Storage savings: ~85% (only aggregates remain)
Best Practices
Archive Frequency
Recommended schedule:
- Every 60 Days: Archive check-ins older than 60 days (recommended)
- As needed: If dashboard performance degrades
Important: Regardless of the archive cutoff date, each user will always retain their 8 most recent check-ins. This ensures athletes can always see their recent check-in history on their check-in screen.
Cutoff Date Selection
Safe approach:
- Keep last 60 days as individual records (for detailed queries)
- Archive everything older into aggregates
- Delete old individual records to save storage
- Automatic preservation: System keeps each user’s 8 most recent check-ins regardless of date
Example workflow:
// Archive everything before 60 days ago
final cutoffDate = DateTime.now().subtract(Duration(days: 60));
await LocationService.archiveGymCheckIns(
cutoffDate: cutoffDate,
deleteAfterArchive: true, // Save storage, but auto-preserves 8/user
);
What gets preserved when deleting:
- All check-ins from the last 60 days (based on cutoff date)
- PLUS each user’s 8 most recent check-ins (even if older than cutoff)
- This ensures athletes always see their last 8 check-ins on the check-in screen
When NOT to Delete
Keep individual records if you need:
- Detailed location data for old check-ins
- Distance calculations for historical records
- Ability to modify/delete specific old check-ins
In these cases:
await LocationService.archiveGymCheckIns(
cutoffDate: cutoffDate,
deleteAfterArchive: false, // Keep originals
);
Automated Archiving (Future Enhancement)
Consider adding a Cloud Function to run monthly:
// fcm_listener_function/src/index.ts
exports.monthlyArchive = functions.pubsub
.schedule('0 0 1 * *') // First day of each month
.timeZone('America/New_York')
.onRun(async (context) => {
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - 30);
// Archive logic here
return null;
});
Troubleshooting
Archive Fails Partway
Symptoms: Some aggregates created, but error occurs Cause: Firestore batch size exceeded or network error Solution: Archive process uses 500-operation batches and commits incrementally. Partial progress is saved. Re-run the archive to complete.
Stats Don’t Match Expected Values
Symptoms: Dashboard shows different numbers after archiving Cause: Overlapping data (same day in both collections) Solution: Archive process creates aggregates for complete days only. getGymCheckInStats() handles overlaps correctly by checking dates.
Can’t Delete Aggregates
Symptoms: Delete operation denied Cause: Only admins can delete aggregates Solution: Ensure user has admin role in Firestore
Migration Path
For existing deployments with no aggregates:
- Deploy code (this update)
- Test with small archive (last 2-3 days, no delete)
- Verify stats match before/after
- Full archive (30+ days ago, with delete)
- Monitor performance improvement
- Schedule monthly archives going forward
Testing
Manual Test Checklist
- Archive with
deleteAfterArchive = false- verify aggregates created - Check stats dashboard - numbers should match before/after
- Archive with
deleteAfterArchive = true- verify records deleted - Stats still accurate after deletion
- Re-archive same period - should be idempotent (no duplicates)
- Archive with no old records - should handle gracefully
Test Data Setup
// Create test check-ins across multiple dates
for (int i = 0; i < 60; i++) {
await FirebaseFirestore.instance.collection('gym_checkins').add({
'userId': 'test_user_$i',
'userEmail': 'test$i@example.com',
'userName': 'Test User $i',
'timestamp': Timestamp.fromDate(
DateTime.now().subtract(Duration(days: i)),
),
'distanceFromGym': 10.0,
'location': {'latitude': 0.0, 'longitude': 0.0},
});
}
Related Files
lib/models/gym_checkin_aggregate.dart- Data modellib/services/location_service.dart- Archive and query logiclib/screens/admin/user_management_screen.dart- Admin UIfirestore.rules- Security rules for aggregates collectionGYM_CHECKIN_README.md- Original check-in system docs