الميزات: إضافة صفحات الأسعار والمدونة، وتفعيل ميزة تقييم الأدوات
- إضافة روابط جديدة في تذييل صفحات الأسعار والمدونة. - إنشاء مكون صفحة الأسعار لعرض تفاصيل الخطط ومقارنة الميزات. - تطوير مكون صفحة المدونة لعرض منشورات المدونة مع روابط للمقالات الفردية. - تقديم مكون تقييم الأدوات لتلقي ملاحظات المستخدمين حول الأدوات، بما في ذلك التقييم بالنجوم والتعليقات الاختيارية. - تفعيل وظيفة useToolRating لجلب وعرض تقييمات الأدوات. - تحديث أدوات تحسين محركات البحث لتضمين بيانات التقييم في البيانات المنظمة للأدوات. - تحسين ملفات i18n بترجمات للميزات والصفحات الجديدة. - دمج إدارة الموافقة على ملفات تعريف الارتباط لتتبع التحليلات.
This commit is contained in:
76
backend/app/routes/rating.py
Normal file
76
backend/app/routes/rating.py
Normal file
@@ -0,0 +1,76 @@
|
||||
"""Tool ratings routes — collect and serve user feedback per tool."""
|
||||
from flask import Blueprint, request, jsonify
|
||||
|
||||
from app.extensions import limiter
|
||||
from app.services.rating_service import (
|
||||
submit_rating,
|
||||
get_tool_rating_summary,
|
||||
get_all_ratings_summary,
|
||||
)
|
||||
|
||||
rating_bp = Blueprint("rating", __name__)
|
||||
|
||||
|
||||
@rating_bp.route("/submit", methods=["POST"])
|
||||
@limiter.limit("30/hour")
|
||||
def submit_rating_route():
|
||||
"""
|
||||
Submit a rating for a tool.
|
||||
|
||||
Accepts JSON:
|
||||
- tool (str): tool slug e.g. "compress-pdf"
|
||||
- rating (int): 1-5 stars
|
||||
- feedback (str, optional): short text feedback
|
||||
- tag (str, optional): predefined tag like "fast", "accurate", "issue"
|
||||
"""
|
||||
data = request.get_json(silent=True) or {}
|
||||
|
||||
tool = (data.get("tool") or "").strip()
|
||||
rating = data.get("rating")
|
||||
feedback = (data.get("feedback") or "").strip()[:500] # max 500 chars
|
||||
tag = (data.get("tag") or "").strip()[:50]
|
||||
|
||||
if not tool:
|
||||
return jsonify({"error": "Tool slug is required."}), 400
|
||||
|
||||
if not isinstance(rating, (int, float)) or not (1 <= int(rating) <= 5):
|
||||
return jsonify({"error": "Rating must be an integer between 1 and 5."}), 400
|
||||
|
||||
rating = int(rating)
|
||||
fingerprint = _get_fingerprint(request)
|
||||
|
||||
submit_rating(
|
||||
tool=tool,
|
||||
rating=rating,
|
||||
feedback=feedback,
|
||||
tag=tag,
|
||||
fingerprint=fingerprint,
|
||||
)
|
||||
|
||||
return jsonify({"message": "Thank you for your feedback!"}), 201
|
||||
|
||||
|
||||
@rating_bp.route("/tool/<tool_slug>", methods=["GET"])
|
||||
@limiter.limit("60/minute")
|
||||
def get_tool_rating(tool_slug: str):
|
||||
"""Return the aggregate rating summary for one tool."""
|
||||
summary = get_tool_rating_summary(tool_slug)
|
||||
return jsonify(summary)
|
||||
|
||||
|
||||
@rating_bp.route("/all", methods=["GET"])
|
||||
@limiter.limit("20/minute")
|
||||
def get_all_ratings():
|
||||
"""Return rating summaries for all tools."""
|
||||
summaries = get_all_ratings_summary()
|
||||
return jsonify({"tools": summaries})
|
||||
|
||||
|
||||
def _get_fingerprint(req) -> str:
|
||||
"""Build a simple fingerprint from IP + User-Agent to limit duplicate ratings."""
|
||||
import hashlib
|
||||
|
||||
ip = req.remote_addr or "unknown"
|
||||
ua = req.headers.get("User-Agent", "unknown")
|
||||
raw = f"{ip}:{ua}"
|
||||
return hashlib.sha256(raw.encode()).hexdigest()[:32]
|
||||
Reference in New Issue
Block a user