able to rate on dashboard
This commit is contained in:
parent
46409b62e3
commit
7f91e5e6df
@ -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"
|
||||
|
||||
@ -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 => `
|
||||
<div class="video-card" onclick="openVideo('${video.url}')">
|
||||
<div class="video-thumbnail">
|
||||
<div class="video-card">
|
||||
<div class="video-thumbnail" onclick="openVideo('${video.url}')">
|
||||
<img src="${video.thumbnail}" alt="${video.title}" onerror="this.src='data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%22320%22 height=%22180%22><rect width=%22100%%22 height=%22100%%22 fill=%22%23333%22/><text x=%2250%%22 y=%2250%%22 text-anchor=%22middle%22 dy=%22.3em%22 fill=%22%23999%22>No Image</text></svg>'">
|
||||
<div class="confidence-badge ${getConfidenceClass(video.confidence)}">
|
||||
${video.confidence}% match
|
||||
</div>
|
||||
</div>
|
||||
<div class="video-info">
|
||||
<div class="video-title">${video.title}</div>
|
||||
<div class="video-title" onclick="openVideo('${video.url}')" style="cursor: pointer;">${video.title}</div>
|
||||
<div class="video-meta">
|
||||
<span>${video.channel_name}</span>
|
||||
<span>•</span>
|
||||
<span>${video.views_formatted}</span>
|
||||
</div>
|
||||
<div class="rating-buttons">
|
||||
<button class="rating-btn like-btn" onclick="rateVideo('${video.id}', true)" id="like-${video.id}">
|
||||
👍 Like
|
||||
</button>
|
||||
<button class="rating-btn dislike-btn" onclick="rateVideo('${video.id}', false)" id="dislike-${video.id}">
|
||||
👎 Dislike
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).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);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user