From 7f91e5e6df907cfa1e4f3bd241ab6c68728fb3e7 Mon Sep 17 00:00:00 2001 From: Gauri Joshi <> Date: Sun, 24 Aug 2025 15:07:41 +0200 Subject: [PATCH] able to rate on dashboard --- dashboard_api.py | 49 +++++++++++- templates/dashboard.html | 156 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 200 insertions(+), 5 deletions(-) diff --git a/dashboard_api.py b/dashboard_api.py index 2505429..0dbf7e0 100644 --- a/dashboard_api.py +++ b/dashboard_api.py @@ -1,10 +1,10 @@ import os -from flask import Flask, jsonify, render_template +from flask import Flask, jsonify, render_template, request from flask_cors import CORS from dotenv import load_dotenv from src.database.manager import setup_database_tables -from src.database.preference_operations import get_training_data_from_database, get_unrated_videos_with_features_from_database, get_rated_count_from_database +from src.database.preference_operations import get_training_data_from_database, get_unrated_videos_with_features_from_database, get_rated_count_from_database, save_video_rating_to_database from src.database.video_operations import get_unrated_videos_from_database from src.ml.model_training import create_recommendation_model, train_model_on_user_preferences from src.ml.predictions import predict_video_preferences_with_model @@ -79,6 +79,51 @@ def get_recommendations(): 'error': str(e) }), 500 +@app.route('/api/rate', methods=['POST']) +def rate_video(): + try: + data = request.json + video_id = data.get('video_id') + liked = data.get('liked') + + if not video_id or liked is None: + return jsonify({ + 'success': False, + 'error': 'Missing video_id or liked parameter' + }), 400 + + # Save the rating + save_video_rating_to_database(video_id, liked, "", dashboard_api.db_path) + + # Check if we should retrain the model + model_retrained = False + rated_count = get_rated_count_from_database(dashboard_api.db_path) + + if rated_count >= 10: # Minimum ratings needed for training + # Retrain the model with new data + if not dashboard_api.model: + dashboard_api.model = create_recommendation_model() + + training_data = get_training_data_from_database(dashboard_api.db_path) + success = train_model_on_user_preferences(dashboard_api.model, training_data) + + if success: + dashboard_api.model_trained = True + model_retrained = True + + return jsonify({ + 'success': True, + 'message': 'Rating saved successfully', + 'model_retrained': model_retrained, + 'total_ratings': rated_count + }) + + except Exception as e: + return jsonify({ + 'success': False, + 'error': str(e) + }), 500 + def format_view_count(count): if count >= 1000000: return f"{count/1000000:.1f}M views" diff --git a/templates/dashboard.html b/templates/dashboard.html index 4571954..44da3ef 100644 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -209,6 +209,56 @@ font-size: 14px; } + .rating-buttons { + display: flex; + gap: 8px; + margin-top: 8px; + justify-content: center; + } + + .rating-btn { + padding: 6px 12px; + border: 1px solid #333; + border-radius: 20px; + background-color: #222; + color: white; + cursor: pointer; + font-size: 14px; + display: flex; + align-items: center; + gap: 4px; + transition: all 0.2s ease; + } + + .rating-btn:hover { + transform: scale(1.05); + } + + .rating-btn.like-btn:hover { + background-color: #00d400; + border-color: #00d400; + } + + .rating-btn.dislike-btn:hover { + background-color: #ff4444; + border-color: #ff4444; + } + + .rating-btn.liked { + background-color: #00d400; + border-color: #00d400; + } + + .rating-btn.disliked { + background-color: #ff4444; + border-color: #ff4444; + } + + .rating-btn:disabled { + opacity: 0.6; + cursor: not-allowed; + } + .loading { text-align: center; padding: 60px; @@ -343,20 +393,28 @@ } videoGrid.innerHTML = videos.map(video => ` -
-
+
+
${video.title}
${video.confidence}% match
-
${video.title}
+
${video.title}
${video.channel_name} ${video.views_formatted}
+
+ + +
`).join(''); @@ -372,6 +430,98 @@ window.open(url, '_blank'); } + async function rateVideo(videoId, liked) { + const likeBtn = document.getElementById(`like-${videoId}`); + const dislikeBtn = document.getElementById(`dislike-${videoId}`); + + // Disable buttons during request + likeBtn.disabled = true; + dislikeBtn.disabled = true; + + try { + const response = await fetch('/api/rate', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + video_id: videoId, + liked: liked + }) + }); + + const result = await response.json(); + + if (result.success) { + // Update button states + likeBtn.classList.remove('liked'); + dislikeBtn.classList.remove('disliked'); + + if (liked) { + likeBtn.classList.add('liked'); + likeBtn.textContent = '👍 Liked'; + } else { + dislikeBtn.classList.add('disliked'); + dislikeBtn.textContent = '👎 Disliked'; + } + + // Show feedback + showNotification(liked ? 'Video liked! 👍' : 'Video disliked! 👎'); + + // Refresh recommendations after rating if model was retrained + if (result.model_retrained) { + setTimeout(() => { + showNotification('🤖 AI model updated with your feedback!'); + loadRecommendations(); + }, 1500); + } + } else { + throw new Error(result.error || 'Failed to rate video'); + } + } catch (error) { + console.error('Error rating video:', error); + showNotification('Failed to rate video: ' + error.message); + } + + // Re-enable buttons + likeBtn.disabled = false; + dislikeBtn.disabled = false; + } + + function showNotification(message) { + // Create notification element + const notification = document.createElement('div'); + notification.style.cssText = ` + position: fixed; + top: 20px; + right: 20px; + background-color: #333; + color: white; + padding: 12px 16px; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0,0,0,0.4); + z-index: 1000; + transition: all 0.3s ease; + transform: translateX(100%); + `; + notification.textContent = message; + + document.body.appendChild(notification); + + // Animate in + setTimeout(() => { + notification.style.transform = 'translateX(0)'; + }, 100); + + // Remove after 3 seconds + setTimeout(() => { + notification.style.transform = 'translateX(100%)'; + setTimeout(() => { + document.body.removeChild(notification); + }, 300); + }, 3000); + } + // Load recommendations when page loads document.addEventListener('DOMContentLoaded', loadRecommendations);