Spaces:
Running
Running
thadillo
commited on
Commit
·
316fc26
1
Parent(s):
71797a4
Phase 3 complete: Backend API for sentence-level analysis
Browse files- Update /api/analyze to support sentence-level (use_sentences flag)
- Add /api/update-sentence-category endpoint
- Automatically create training examples from sentence corrections
- Recalculate submission primary category from sentences
- app/routes/admin.py +89 -7
app/routes/admin.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
from flask import Blueprint, render_template, request, redirect, url_for, session, flash, jsonify, send_file
|
| 2 |
-
from app.models.models import Token, Submission, Settings, TrainingExample, FineTuningRun
|
| 3 |
from app import db
|
| 4 |
from app.analyzer import get_analyzer
|
| 5 |
from functools import wraps
|
|
@@ -277,6 +277,57 @@ def update_category(submission_id):
|
|
| 277 |
print(f"Error updating category: {str(e)}")
|
| 278 |
return jsonify({'success': False, 'error': str(e)}), 500
|
| 279 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 280 |
@bp.route('/api/toggle-flag/<int:submission_id>', methods=['POST'])
|
| 281 |
@admin_required
|
| 282 |
def toggle_flag(submission_id):
|
|
@@ -298,12 +349,17 @@ def delete_submission(submission_id):
|
|
| 298 |
def analyze_submissions():
|
| 299 |
data = request.json
|
| 300 |
analyze_all = data.get('analyze_all', False)
|
|
|
|
| 301 |
|
| 302 |
# Get submissions to analyze
|
| 303 |
if analyze_all:
|
| 304 |
to_analyze = Submission.query.all()
|
| 305 |
else:
|
| 306 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 307 |
|
| 308 |
if not to_analyze:
|
| 309 |
return jsonify({'success': False, 'error': 'No submissions to analyze'}), 400
|
|
@@ -316,13 +372,38 @@ def analyze_submissions():
|
|
| 316 |
|
| 317 |
for submission in to_analyze:
|
| 318 |
try:
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 322 |
success_count += 1
|
| 323 |
|
| 324 |
except Exception as e:
|
| 325 |
-
|
| 326 |
error_count += 1
|
| 327 |
continue
|
| 328 |
|
|
@@ -331,7 +412,8 @@ def analyze_submissions():
|
|
| 331 |
return jsonify({
|
| 332 |
'success': True,
|
| 333 |
'analyzed': success_count,
|
| 334 |
-
'errors': error_count
|
|
|
|
| 335 |
})
|
| 336 |
|
| 337 |
@bp.route('/export/json')
|
|
|
|
| 1 |
from flask import Blueprint, render_template, request, redirect, url_for, session, flash, jsonify, send_file
|
| 2 |
+
from app.models.models import Token, Submission, Settings, TrainingExample, FineTuningRun, SubmissionSentence
|
| 3 |
from app import db
|
| 4 |
from app.analyzer import get_analyzer
|
| 5 |
from functools import wraps
|
|
|
|
| 277 |
print(f"Error updating category: {str(e)}")
|
| 278 |
return jsonify({'success': False, 'error': str(e)}), 500
|
| 279 |
|
| 280 |
+
@bp.route('/api/update-sentence-category/<int:sentence_id>', methods=['POST'])
|
| 281 |
+
@admin_required
|
| 282 |
+
def update_sentence_category(sentence_id):
|
| 283 |
+
"""Update category for a specific sentence"""
|
| 284 |
+
try:
|
| 285 |
+
sentence = SubmissionSentence.query.get_or_404(sentence_id)
|
| 286 |
+
data = request.json
|
| 287 |
+
new_category = data.get('category')
|
| 288 |
+
|
| 289 |
+
# Store original
|
| 290 |
+
original_category = sentence.category
|
| 291 |
+
|
| 292 |
+
# Validate category
|
| 293 |
+
if new_category and new_category not in CATEGORIES:
|
| 294 |
+
return jsonify({'success': False, 'error': f'Invalid category: {new_category}'}), 400
|
| 295 |
+
|
| 296 |
+
# Update sentence
|
| 297 |
+
sentence.category = new_category
|
| 298 |
+
|
| 299 |
+
# Create/update training example for this sentence
|
| 300 |
+
if new_category:
|
| 301 |
+
existing = TrainingExample.query.filter_by(sentence_id=sentence_id).first()
|
| 302 |
+
|
| 303 |
+
if existing:
|
| 304 |
+
existing.original_category = original_category
|
| 305 |
+
existing.corrected_category = new_category
|
| 306 |
+
existing.correction_timestamp = datetime.utcnow()
|
| 307 |
+
else:
|
| 308 |
+
training_example = TrainingExample(
|
| 309 |
+
sentence_id=sentence_id,
|
| 310 |
+
submission_id=sentence.submission_id,
|
| 311 |
+
message=sentence.text, # Just the sentence text
|
| 312 |
+
original_category=original_category,
|
| 313 |
+
corrected_category=new_category,
|
| 314 |
+
contributor_type=sentence.submission.contributor_type
|
| 315 |
+
)
|
| 316 |
+
db.session.add(training_example)
|
| 317 |
+
|
| 318 |
+
# Update parent submission's primary category (recalculate from sentences)
|
| 319 |
+
submission = sentence.submission
|
| 320 |
+
submission.category = submission.get_primary_category()
|
| 321 |
+
|
| 322 |
+
db.session.commit()
|
| 323 |
+
|
| 324 |
+
return jsonify({'success': True, 'category': new_category})
|
| 325 |
+
|
| 326 |
+
except Exception as e:
|
| 327 |
+
db.session.rollback()
|
| 328 |
+
logger.error(f"Error updating sentence category: {str(e)}")
|
| 329 |
+
return jsonify({'success': False, 'error': str(e)}), 500
|
| 330 |
+
|
| 331 |
@bp.route('/api/toggle-flag/<int:submission_id>', methods=['POST'])
|
| 332 |
@admin_required
|
| 333 |
def toggle_flag(submission_id):
|
|
|
|
| 349 |
def analyze_submissions():
|
| 350 |
data = request.json
|
| 351 |
analyze_all = data.get('analyze_all', False)
|
| 352 |
+
use_sentences = data.get('use_sentences', True) # NEW: sentence-level flag (default: True)
|
| 353 |
|
| 354 |
# Get submissions to analyze
|
| 355 |
if analyze_all:
|
| 356 |
to_analyze = Submission.query.all()
|
| 357 |
else:
|
| 358 |
+
# For sentence-level, look for submissions without sentence analysis
|
| 359 |
+
if use_sentences:
|
| 360 |
+
to_analyze = Submission.query.filter_by(sentence_analysis_done=False).all()
|
| 361 |
+
else:
|
| 362 |
+
to_analyze = Submission.query.filter_by(category=None).all()
|
| 363 |
|
| 364 |
if not to_analyze:
|
| 365 |
return jsonify({'success': False, 'error': 'No submissions to analyze'}), 400
|
|
|
|
| 372 |
|
| 373 |
for submission in to_analyze:
|
| 374 |
try:
|
| 375 |
+
if use_sentences:
|
| 376 |
+
# NEW: Sentence-level analysis
|
| 377 |
+
sentence_results = analyzer.analyze_with_sentences(submission.message)
|
| 378 |
+
|
| 379 |
+
# Clear old sentences for this submission
|
| 380 |
+
SubmissionSentence.query.filter_by(submission_id=submission.id).delete()
|
| 381 |
+
|
| 382 |
+
# Create new sentence records
|
| 383 |
+
for idx, result in enumerate(sentence_results):
|
| 384 |
+
sentence = SubmissionSentence(
|
| 385 |
+
submission_id=submission.id,
|
| 386 |
+
sentence_index=idx,
|
| 387 |
+
text=result['text'],
|
| 388 |
+
category=result['category'],
|
| 389 |
+
confidence=result.get('confidence')
|
| 390 |
+
)
|
| 391 |
+
db.session.add(sentence)
|
| 392 |
+
|
| 393 |
+
submission.sentence_analysis_done = True
|
| 394 |
+
# Set primary category for backward compatibility
|
| 395 |
+
submission.category = submission.get_primary_category()
|
| 396 |
+
|
| 397 |
+
logger.info(f"Analyzed submission {submission.id} into {len(sentence_results)} sentences")
|
| 398 |
+
else:
|
| 399 |
+
# OLD: Submission-level analysis (backward compatible)
|
| 400 |
+
category = analyzer.analyze(submission.message)
|
| 401 |
+
submission.category = category
|
| 402 |
+
|
| 403 |
success_count += 1
|
| 404 |
|
| 405 |
except Exception as e:
|
| 406 |
+
logger.error(f"Error analyzing submission {submission.id}: {e}")
|
| 407 |
error_count += 1
|
| 408 |
continue
|
| 409 |
|
|
|
|
| 412 |
return jsonify({
|
| 413 |
'success': True,
|
| 414 |
'analyzed': success_count,
|
| 415 |
+
'errors': error_count,
|
| 416 |
+
'sentence_level': use_sentences
|
| 417 |
})
|
| 418 |
|
| 419 |
@bp.route('/export/json')
|