164 lines
5.3 KiB
Python
164 lines
5.3 KiB
Python
import tempfile
|
|
import os
|
|
import asyncio
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
|
|
from app.celery import celery_app
|
|
from app.analysis.score_following import ScoreFollower
|
|
from app.analysis.metrics import PerformanceMetrics
|
|
from app.analysis.measure_analysis import MeasureAnalyzer
|
|
from app.schemas.analysis import AnalysisStatus
|
|
|
|
# This will be imported when storage_service is available
|
|
# from app.services.storage_service import storage_service
|
|
|
|
# In-memory store for analysis jobs (should be replaced with Redis/DB in production)
|
|
analysis_jobs = {}
|
|
|
|
@celery_app.task(bind=True, name="analyze_performance_task")
|
|
def analyze_performance_task(self, job_id: str, score_midi_url: str,
|
|
performance_midi_url: str, user_id: Optional[str] = None,
|
|
session_id: Optional[str] = None, analysis_type: str = "full"):
|
|
"""
|
|
Celery task for comprehensive performance analysis
|
|
"""
|
|
current_task = self
|
|
|
|
current_task.update_state(
|
|
state='PROCESSING',
|
|
meta={'current': 0, 'total': 100, 'status': 'Downloading files...'}
|
|
)
|
|
|
|
try:
|
|
# Download MIDI files
|
|
current_task.update_state(
|
|
state='PROCESSING',
|
|
meta={'current': 20, 'total': 100, 'status': 'Downloading score MIDI...'}
|
|
)
|
|
|
|
# TODO: Implement file download when storage_service is available
|
|
# score_content = asyncio.run(storage_service.download_file(score_midi_url))
|
|
# For now, assume local files
|
|
score_path = score_midi_url
|
|
|
|
current_task.update_state(
|
|
state='PROCESSING',
|
|
meta={'current': 40, 'total': 100, 'status': 'Downloading performance MIDI...'}
|
|
)
|
|
|
|
# performance_content = asyncio.run(storage_service.download_file(performance_midi_url))
|
|
performance_path = performance_midi_url
|
|
|
|
# Initialize analysis components
|
|
current_task.update_state(
|
|
state='PROCESSING',
|
|
meta={'current': 60, 'total': 100, 'status': 'Initializing analysis...'}
|
|
)
|
|
|
|
score_follower = ScoreFollower(score_midi_path=score_path)
|
|
performance_notes = score_follower._load_score_notes(performance_path)
|
|
|
|
# Perform alignment
|
|
current_task.update_state(
|
|
state='PROCESSING',
|
|
meta={'current': 70, 'total': 100, 'status': 'Aligning performance with score...'}
|
|
)
|
|
|
|
alignment_result = score_follower.align_performance(performance_notes)
|
|
|
|
if not alignment_result['success']:
|
|
raise Exception(f"Alignment failed: {alignment_result.get('error', 'Unknown error')}")
|
|
|
|
# Compute metrics
|
|
current_task.update_state(
|
|
state='PROCESSING',
|
|
meta={'current': 80, 'total': 100, 'status': 'Computing performance metrics...'}
|
|
)
|
|
|
|
metrics_calculator = PerformanceMetrics(
|
|
score_follower.score_notes,
|
|
performance_notes
|
|
)
|
|
|
|
overall_assessment = metrics_calculator.compute_overall_assessment(
|
|
alignment_result['alignment']
|
|
)
|
|
|
|
# Measure-level analysis
|
|
current_task.update_state(
|
|
state='PROCESSING',
|
|
meta={'current': 90, 'total': 100, 'status': 'Analyzing measure performance...'}
|
|
)
|
|
|
|
measure_analyzer = MeasureAnalyzer(score_follower.score_notes)
|
|
measure_results = measure_analyzer.analyze_measure_performance(
|
|
performance_notes, alignment_result['alignment']
|
|
)
|
|
|
|
problem_measures = measure_analyzer.get_problem_measures(measure_results)
|
|
|
|
# Prepare final result
|
|
final_result = {
|
|
'job_id': job_id,
|
|
'performance_metrics': overall_assessment,
|
|
'measure_metrics': measure_results,
|
|
'problem_measures': problem_measures,
|
|
'alignment_data': alignment_result,
|
|
'success': True
|
|
}
|
|
|
|
# Update job store
|
|
analysis_jobs[job_id] = {
|
|
"status": AnalysisStatus.COMPLETED,
|
|
"result": final_result,
|
|
"created_at": datetime.utcnow()
|
|
}
|
|
|
|
current_task.update_state(
|
|
state='SUCCESS',
|
|
meta={'current': 100, 'total': 100, 'status': 'Analysis completed'}
|
|
)
|
|
|
|
return final_result
|
|
|
|
except Exception as e:
|
|
error_result = {
|
|
'job_id': job_id,
|
|
'success': False,
|
|
'error': str(e)
|
|
}
|
|
|
|
analysis_jobs[job_id] = {
|
|
"status": AnalysisStatus.FAILED,
|
|
"result": error_result,
|
|
"created_at": datetime.utcnow()
|
|
}
|
|
|
|
current_task.update_state(
|
|
state='FAILURE',
|
|
meta={'current': 0, 'total': 100, 'status': f'Failed: {str(e)}'}
|
|
)
|
|
|
|
return error_result
|
|
|
|
|
|
@celery_app.task(name="process_transcription_task")
|
|
def process_transcription_task(job_id: str, audio_url: str):
|
|
"""
|
|
Celery task for audio transcription
|
|
Placeholder for Feature 3 integration
|
|
"""
|
|
# TODO: Implement when transcription service is available
|
|
pass
|
|
|
|
|
|
@celery_app.task(name="generate_report_task")
|
|
def generate_report_task(session_id: str, report_type: str = "pdf"):
|
|
"""
|
|
Celery task for report generation
|
|
Placeholder for Feature 5 integration
|
|
"""
|
|
# TODO: Implement when report generation is available
|
|
pass
|