# Engagement-Based Rating System

## Overview
A comprehensive engagement scoring system that ranks posts based on weighted user interactions with time decay.

## Architecture

### Database Schema
**Table**: `nurse_posts`

New columns added:
- `likes_count` (unsigned int) - Cached count of likes
- `dislikes_count` (unsigned int) - Cached count of dislikes  
- `comments_count` (unsigned int) - Cached count of comments
- `reposts_count` (unsigned int) - Cached count of reposts
- `shares_count` (unsigned int) - Cached count of shares (future use)
- `engagement_score` (decimal 10,2) - Raw weighted score with time decay
- `signal_score` (decimal 5,2) - Normalized score (0-100)
- `signal_level` (tinyint) - UI display level (0-4 bars)

**Index**: Composite index on `(signal_score, created_at)` for optimized feed queries.

## Scoring Algorithm

### Signal Weights
```
Like:     +1.0
Dislike:  -1.5
Comment:  +2.0
Repost:   +2.5
Share:    +3.0
```

### Time Decay Formula
```
decay_factor = 0.5 ^ (hours_since_post / 48)
```
- **Half-life**: 48 hours
- Older posts naturally decrease in score
- Keeps feed fresh with recent content

### Normalization
```
signal_score = min(100, (decayed_score / top_score) * 100)
```
- Scales relative to highest-performing post
- Range: 0-100

### Signal Levels (UI Bars)
```
0-10:   Level 0 (no bars)
10-30:  Level 1 (1 bar)
30-60:  Level 2 (2 bars)
60-80:  Level 3 (3 bars)
80-100: Level 4 (4 bars)
```

## Service Layer

### PostEngagementService
**Location**: `app/Services/PostEngagementService.php`

**Key Methods**:
- `recomputeForPost($post, $topScore)` - Full scoring pipeline for single post
- `syncCountsAndRecompute($post)` - Sync counts from relationships + recompute
- `recomputeRecentPosts($days = 7)` - Batch update for recent posts
- `computeTopScore()` - Find max score among recent posts

**Usage**:
```php
use App\Services\PostEngagementService;

// After any interaction
$this->engagementService->syncCountsAndRecompute($post);
```

## Integration Points

### Controller Integration
**File**: `app/Http/Controllers/Nurse/ConnectController.php`

Engagement recomputation triggers:
- `toggleLike()` - After like/unlike
- `toggleDislike()` - After dislike/undislike
- `storeComment()` - After comment created
- `toggleRepost()` - After repost/unrepost

### Model Updates
**File**: `app/Models/NursePost.php`

Added fillable fields and casts for all engagement metrics.

## Scheduled Updates

### Command
**File**: `app/Console/Commands/RecomputePostEngagement.php`

**Signature**: `posts:recompute-engagement {--days=7}`

**Purpose**: Apply time decay to all recent posts (scores naturally decrease as posts age).

### Scheduler Configuration
**File**: `routes/console.php`

```php
Schedule::command('posts:recompute-engagement')->hourly();
```

Runs every hour to keep scores current with time decay.

## Usage Guide

### Manual Recomputation
```bash
# Recompute posts from last 7 days (default)
php artisan posts:recompute-engagement

# Recompute posts from last 30 days
php artisan posts:recompute-engagement --days=30
```

### Query Posts by Engagement
```php
// Get top posts by signal score
$topPosts = NursePost::orderBy('signal_score', 'desc')
    ->orderBy('created_at', 'desc')
    ->limit(20)
    ->get();

// Get posts by signal level
$highEngagement = NursePost::where('signal_level', '>=', 3)->get();
```

### Display Signal Bars in UI
```blade
{{-- In your Blade view --}}
@if($post->signal_level > 0)
    <div class="signal-bars">
        @for($i = 1; $i <= $post->signal_level; $i++)
            <span class="signal-bar active"></span>
        @endfor
        @for($i = $post->signal_level + 1; $i <= 4; $i++)
            <span class="signal-bar"></span>
        @endfor
    </div>
    <span class="signal-score">{{ number_format($post->signal_score, 1) }}%</span>
@endif
```

## Testing

### Verify Counts are Syncing
```bash
php artisan tinker
```
```php
$post = App\Models\NursePost::find(1);
echo "Likes: {$post->likes_count}, Comments: {$post->comments_count}";
```

### Test Engagement Service
```php
$service = app(App\Services\PostEngagementService::class);
$post = App\Models\NursePost::find(1);
$service->syncCountsAndRecompute($post);
echo "Score: {$post->signal_score}, Level: {$post->signal_level}";
```

### View Top Posts
```bash
php artisan tinker --execute="echo App\Models\NursePost::select('id', 'signal_score', 'signal_level', 'likes_count', 'comments_count')->orderBy('signal_score', 'desc')->limit(10)->get()->toJson(JSON_PRETTY_PRINT);"
```

## Monitoring

### Check Scheduler Status
```bash
php artisan schedule:list
```

### Run Scheduler Manually (Development)
```bash
php artisan schedule:run
```

### Production Setup
Add to crontab:
```
* * * * * cd /path-to-cura-app && php artisan schedule:run >> /dev/null 2>&1
```

## Performance Considerations

1. **Composite Index**: Speeds up feed queries sorted by signal_score + created_at
2. **Cached Counts**: Avoids expensive COUNT queries on each page load
3. **Selective Updates**: Only recomputes posts from last 7 days by default
4. **Background Processing**: Scheduler runs hourly, not on every interaction

## Future Enhancements

1. **Share Tracking**: Implement share functionality and integrate into scoring
2. **Category Weights**: Different weights per post category (medical vs general)
3. **User Reputation**: Factor in poster's reputation into engagement score
4. **A/B Testing**: Test different weight configurations
5. **Analytics Dashboard**: Visualize engagement trends over time

## Migration History

- `2025_12_02_035631` - Added engagement scoring columns
- Initial data sync: 14 posts updated

## Status

✅ **FULLY IMPLEMENTED AND TESTED**

The system is live and automatically updating scores on every interaction.
