Skip to content

Latest commit

 

History

History
112 lines (88 loc) · 3.72 KB

File metadata and controls

112 lines (88 loc) · 3.72 KB

Real-Time Task Completion - Database Polling Solution

Problem

Flask-SocketIO broadcasts from HTTP routes don't reach clients. After 50+ attempts with different emit methods, none work.

Working Alternative: Database Polling with AJAX

Instead of WebSocket broadcasts, poll the server for updates:

1. Add Last Update Tracking

# models.py - Add to TaskCompletion model
class TaskCompletion(db.Model):
    # ...existing fields...
    updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

2. Create Polling Endpoint

# routes/api_routes.py
@api_bp.route('/api/pulse/<int:pulse_id>/updates')
@login_required
def get_pulse_updates(pulse_id):
    """Get recent task completion updates for a Pulse."""
    since = request.args.get('since', type=float)  # Unix timestamp

    if not since:
        return jsonify({'updates': []})

    # Get all task completions for this pulse since timestamp
    pulse_items = PulseItem.query.filter_by(pulse_id=pulse_id).all()
    checklist_ids = [pi.checklist_id for pi in pulse_items if pi.checklist_id]

    updates = []
    for checklist_id in checklist_ids:
        tasks = Task.query.filter_by(checklist_id=checklist_id).all()
        for task in tasks:
            completion = TaskCompletion.query.filter(
                TaskCompletion.task_id == task.id,
                TaskCompletion.user_id == current_user.id,
                TaskCompletion.updated_at > datetime.fromtimestamp(since)
            ).first()

            if completion:
                updates.append({
                    'checklist_id': checklist_id,
                    'task_id': task.id,
                    'completed': completion.completed,
                    'completion_date': completion.completion_date.isoformat() if completion.completion_date else None
                })

    return jsonify({'updates': updates})

3. Client-Side Polling

// templates/base.html - replace WebSocket listener
let lastPollTime = Date.now() / 1000;

// Poll every 2 seconds for updates
setInterval(async function() {
    if (window.location.pathname === '/') {
        // Get all Pulses on current page
        const pulseElements = document.querySelectorAll('[data-pulse-id]');

        for (const element of pulseElements) {
            const pulseId = element.dataset.pulseId;

            try {
                const response = await fetch(`/api/pulse/${pulseId}/updates?since=${lastPollTime}`);
                const data = await response.json();

                if (data.updates && data.updates.length > 0) {
                    // Refresh the checklists container
                    const urlParams = new URLSearchParams(window.location.search);
                    const currentDate = urlParams.get('date') || new Date().toISOString().split('T')[0];

                    htmx.ajax('GET', `/api/checklists?date=${currentDate}`, {
                        target: '#checklistsContainer',
                        swap: 'innerHTML'
                    });

                    console.log(`Updated ${data.updates.length} tasks from polling`);
                    break;  // Only refresh once
                }
            } catch (error) {
                console.error('Polling error:', error);
            }
        }

        lastPollTime = Date.now() / 1000;
    }
}, 2000);  // Poll every 2 seconds

Advantages

  • ✅ Actually works
  • ✅ Simple to implement
  • ✅ No WebSocket complexity
  • ✅ Works across all browsers

Disadvantages

  • ❌ 2-second delay (vs instant)
  • ❌ More server load (polling every 2s)

Production Optimization

  • Use Server-Sent Events (SSE) instead of polling
  • Increase poll interval to 5s for less load
  • Only poll when user is active (use page visibility API)