Files
Reklamator/app/routes/submission.py
T

180 lines
5.9 KiB
Python
Raw Normal View History

"""Submission routes - anonymous feedback submission"""
import threading
import os
from flask import Blueprint, render_template, request, redirect, url_for, flash, abort, current_app
from app.models.product import Product
from app.services.feedback_storage import FeedbackStorageService
from app.services.ai_analyzer import ClaudeAnalyzer
from app.utils.file_validator import validate_file, scan_file_for_viruses
bp = Blueprint('submission', __name__, url_prefix='/submit')
@bp.route('/<product_slug>', methods=['GET'])
def form(product_slug):
"""Display feedback submission form
Args:
product_slug: Product submission URL slug
Returns:
Rendered submission form template or 404
"""
# Load product by slug
product = Product.get_by_slug(product_slug)
if not product:
abort(404, description="Product not found")
# Check if product is archived
if product.is_archived():
abort(404, description="This product is no longer accepting feedback")
return render_template('submission/form.html', product=product)
@bp.route('/<product_slug>', methods=['POST'])
def submit(product_slug):
"""Process feedback submission
Args:
product_slug: Product submission URL slug
Returns:
Redirect to success page or error page
"""
# Load product by slug
product = Product.get_by_slug(product_slug)
if not product:
abort(404, description="Product not found")
# Check if product is archived
if product.is_archived():
abort(404, description="This product is no longer accepting feedback")
# Get form data
feedback_text = request.form.get('feedback_text', '').strip()
# Get uploaded files
uploaded_files = request.files.getlist('files')
# Filter out empty file inputs
files = [f for f in uploaded_files if f and f.filename != '']
# Validation: Must provide either text or files
if not feedback_text and not files:
abort(400, description="Please provide either feedback text or attachments")
# Validation: Maximum 3 files
if len(files) > 3:
abort(400, description="Maximum 3 attachments allowed")
# Validate each file
for file in files:
is_valid, error_message = validate_file(file)
if not is_valid:
abort(400, description=error_message)
# Scan for viruses
is_clean, virus_message = scan_file_for_viruses(file)
if not is_clean:
abort(400, description=f"File rejected: {virus_message}")
# Save feedback
try:
feedback = FeedbackStorageService.save_complete_feedback(
product_id=product.product_id,
content_text=feedback_text if feedback_text else None,
files=files if files else None
)
# Trigger background analysis (T084, T085)
if feedback_text: # Only analyze if there's text content
_trigger_background_analysis(feedback, feedback_text, product)
return render_template('submission/success.html',
product=product,
feedback_id=feedback.feedback_id)
except Exception as e:
# Log error
current_app.logger.error(f"Error saving feedback: {e}")
return render_template('submission/error.html',
product=product,
error_message="An error occurred while saving your feedback. Please try again."), 500
def _trigger_background_analysis(feedback, feedback_text, product):
"""Trigger background AI analysis task (T084)
Args:
feedback: Feedback instance
feedback_text: Feedback text content
product: Product instance
"""
# Get the current app instance to pass to background thread
app = current_app._get_current_object()
# Run analysis in background thread
thread = threading.Thread(
target=_analyze_feedback_background,
args=(app, feedback.product_id, feedback.feedback_id, feedback_text, product.owner_language)
)
thread.daemon = True
thread.start()
def _analyze_feedback_background(app, product_id, feedback_id, feedback_text, target_language):
"""Background task for AI analysis (T086-T088)
This runs in a separate thread to avoid blocking the submission response.
Args:
app: Flask app instance for application context
product_id: Product ID
feedback_id: Feedback ID
feedback_text: Feedback text to analyze
target_language: Target language for translation
"""
# Run within Flask application context
with app.app_context():
try:
# Update status to "analyzing" (T086)
FeedbackStorageService.update_feedback_status_by_id(
product_id, feedback_id, 'analyzing'
)
# Get API key from environment
api_key = os.getenv('ANTHROPIC_API_KEY')
if not api_key:
raise Exception("ANTHROPIC_API_KEY not configured")
# Initialize analyzer
analyzer = ClaudeAnalyzer(api_key=api_key)
# Analyze feedback
result = analyzer.analyze_feedback(
feedback_text=feedback_text,
target_language=target_language,
product_id=product_id
)
# Save analysis results
FeedbackStorageService.save_analysis(product_id, feedback_id, result)
# Update status to "analyzed" (T087)
FeedbackStorageService.update_feedback_status_by_id(
product_id, feedback_id, 'analyzed'
)
except Exception as e:
# Update status to "analysis_failed" on error (T088)
FeedbackStorageService.update_feedback_status_by_id(
product_id, feedback_id, 'analysis_failed'
)
# Log error
print(f"Analysis failed for feedback {feedback_id}: {e}")