Spaces:
Sleeping
Sleeping
Initial project setup with multi-URL API
Browse files- app.py +1 -1
- process_interview.py +33 -36
app.py
CHANGED
|
@@ -134,7 +134,7 @@ def analyze_multiple_audios(file_paths_or_urls: List[str]) -> Tuple[str, str, Li
|
|
| 134 |
|
| 135 |
combined_summary = "\n\n---\n\n".join(all_summaries)
|
| 136 |
# Ensure the combined_json_list is a valid JSON array string
|
| 137 |
-
combined_json_list = "[\n" + ",\n".join(all_json_data) + "\n]"
|
| 138 |
|
| 139 |
return combined_summary, combined_json_list, all_pdf_paths
|
| 140 |
|
|
|
|
| 134 |
|
| 135 |
combined_summary = "\n\n---\n\n".join(all_summaries)
|
| 136 |
# Ensure the combined_json_list is a valid JSON array string
|
| 137 |
+
combined_json_list = "[\n" + ",\n".join(all_json_data) + "\n]"
|
| 138 |
|
| 139 |
return combined_summary, combined_json_list, all_pdf_paths
|
| 140 |
|
process_interview.py
CHANGED
|
@@ -23,8 +23,8 @@ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, Tabl
|
|
| 23 |
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
| 24 |
from reportlab.lib.units import inch
|
| 25 |
from reportlab.lib import colors
|
| 26 |
-
import matplotlib.pyplot as plt
|
| 27 |
-
from reportlab.platypus import Image
|
| 28 |
# --- End Imports for enhanced PDF ---
|
| 29 |
from transformers import AutoTokenizer, AutoModel
|
| 30 |
import spacy
|
|
@@ -36,6 +36,7 @@ from concurrent.futures import ThreadPoolExecutor
|
|
| 36 |
logging.basicConfig(level=logging.INFO)
|
| 37 |
logger = logging.getLogger(__name__)
|
| 38 |
logging.getLogger("nemo_logging").setLevel(logging.ERROR)
|
|
|
|
| 39 |
|
| 40 |
# Configuration
|
| 41 |
AUDIO_DIR = "./uploads"
|
|
@@ -211,10 +212,14 @@ def process_utterance(utterance, full_audio, wav_file):
|
|
| 211 |
segment.export(temp_path, format="wav")
|
| 212 |
|
| 213 |
with torch.no_grad():
|
| 214 |
-
embedding = speaker_model.get_embedding(temp_path).
|
|
|
|
|
|
|
|
|
|
|
|
|
| 215 |
|
| 216 |
query_result = index.query(
|
| 217 |
-
vector=
|
| 218 |
top_k=1,
|
| 219 |
include_metadata=True
|
| 220 |
)
|
|
@@ -225,7 +230,7 @@ def process_utterance(utterance, full_audio, wav_file):
|
|
| 225 |
else:
|
| 226 |
speaker_id = f"unknown_{uuid.uuid4().hex[:6]}"
|
| 227 |
speaker_name = f"Speaker_{speaker_id[-4:]}"
|
| 228 |
-
index.upsert([(speaker_id,
|
| 229 |
|
| 230 |
os.remove(temp_path)
|
| 231 |
|
|
@@ -233,10 +238,10 @@ def process_utterance(utterance, full_audio, wav_file):
|
|
| 233 |
**utterance,
|
| 234 |
'speaker': speaker_name,
|
| 235 |
'speaker_id': speaker_id,
|
| 236 |
-
'embedding':
|
| 237 |
}
|
| 238 |
except Exception as e:
|
| 239 |
-
logger.error(f"Utterance processing failed: {str(e)}")
|
| 240 |
return {
|
| 241 |
**utterance,
|
| 242 |
'speaker': 'Unknown',
|
|
@@ -484,27 +489,9 @@ def generate_voice_interpretation(analysis: Dict) -> str:
|
|
| 484 |
|
| 485 |
|
| 486 |
# --- Chart Generation Function ---
|
| 487 |
-
|
| 488 |
-
|
| 489 |
-
|
| 490 |
-
scores = [composite_scores.get('anxiety', 0), composite_scores.get('confidence', 0)]
|
| 491 |
-
|
| 492 |
-
fig, ax = plt.subplots(figsize=(4, 2.5)) # Smaller size for embedding in PDF
|
| 493 |
-
ax.bar(labels, scores, color=['lightcoral', 'lightskyblue'])
|
| 494 |
-
ax.set_ylabel('Score')
|
| 495 |
-
ax.set_title('Anxiety vs. Confidence Scores')
|
| 496 |
-
ax.set_ylim(0, 1.0) # Assuming scores are normalized 0-1
|
| 497 |
-
|
| 498 |
-
for i, v in enumerate(scores):
|
| 499 |
-
ax.text(i, v + 0.05, f"{v:.2f}", color='black', ha='center', fontweight='bold')
|
| 500 |
-
|
| 501 |
-
# هذه الأوامر يجب أن تكون خارج الـ loop عشان يتم تنفيذها مرة واحدة بعد رسم كل العناصر
|
| 502 |
-
plt.tight_layout()
|
| 503 |
-
plt.savefig(chart_path)
|
| 504 |
-
plt.close(fig) # Close the figure to free up memory
|
| 505 |
-
except Exception as e:
|
| 506 |
-
logger.error(f"Error generating chart: {str(e)}")
|
| 507 |
-
# You might want to create a placeholder image or simply log the error
|
| 508 |
|
| 509 |
|
| 510 |
# --- Acceptance Probability Calculation ---
|
|
@@ -572,9 +559,9 @@ def calculate_acceptance_probability(analysis_data: Dict) -> float:
|
|
| 572 |
# Normalize to 0-1 and then to percentage
|
| 573 |
# These max/min values are rough estimates and should be calibrated with real data
|
| 574 |
min_possible_score = (0 * w_confidence) + (0 * abs(w_anxiety)) + (0 * w_fluency) + (0 * w_speaking_rate) + (
|
| 575 |
-
|
| 576 |
max_possible_score = (1 * w_confidence) + (1 * abs(w_anxiety)) + (1 * w_fluency) + (1 * w_speaking_rate) + (
|
| 577 |
-
|
| 578 |
|
| 579 |
# Prevent division by zero if all weights are zero or min/max are same
|
| 580 |
if max_possible_score == min_possible_score:
|
|
@@ -680,11 +667,13 @@ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text:
|
|
| 680 |
prob_color = colors.green if acceptance_prob >= 70 else (
|
| 681 |
colors.orange if acceptance_prob >= 40 else colors.red)
|
| 682 |
|
|
|
|
| 683 |
story.append(Paragraph(
|
| 684 |
-
f"<font size='12' color='{prob_color.hexval}'><b>Estimated Acceptance Probability: {acceptance_prob:.2f}%</b></font>",
|
| 685 |
ParagraphStyle(name='AcceptanceProbability', parent=styles['Normal'], fontSize=12, spaceAfter=10,
|
| 686 |
alignment=1)
|
| 687 |
))
|
|
|
|
| 688 |
|
| 689 |
if acceptance_prob >= 80:
|
| 690 |
story.append(
|
|
@@ -769,17 +758,25 @@ def create_pdf_report(analysis_data: Dict, output_path: str, gemini_report_text:
|
|
| 769 |
story.append(Spacer(1, 0.2 * inch))
|
| 770 |
|
| 771 |
# --- Charts ---
|
| 772 |
-
story.append(Paragraph("Score Visualization:", h3))
|
| 773 |
chart_path = os.path.join(OUTPUT_DIR, f"anxiety_confidence_{uuid.uuid4().hex[:8]}.png")
|
| 774 |
-
generate_anxiety_confidence_chart
|
| 775 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 776 |
if os.path.exists(chart_path):
|
| 777 |
-
img = Image(chart_path, width=3.5*inch, height=2.0*inch)
|
| 778 |
story.append(img)
|
| 779 |
story.append(Spacer(1, 0.1 * inch))
|
| 780 |
os.remove(chart_path)
|
| 781 |
-
except Exception as
|
| 782 |
-
logger.warning(
|
|
|
|
|
|
|
| 783 |
# --- End Charts ---
|
| 784 |
|
| 785 |
# Detailed Interpretation from Gemini (if present)
|
|
|
|
| 23 |
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
| 24 |
from reportlab.lib.units import inch
|
| 25 |
from reportlab.lib import colors
|
| 26 |
+
import matplotlib.pyplot as plt # تم تفعيله
|
| 27 |
+
from reportlab.platypus import Image # تم تفعيله
|
| 28 |
# --- End Imports for enhanced PDF ---
|
| 29 |
from transformers import AutoTokenizer, AutoModel
|
| 30 |
import spacy
|
|
|
|
| 36 |
logging.basicConfig(level=logging.INFO)
|
| 37 |
logger = logging.getLogger(__name__)
|
| 38 |
logging.getLogger("nemo_logging").setLevel(logging.ERROR)
|
| 39 |
+
logging.getLogger("nemo").setLevel(logging.ERROR)
|
| 40 |
|
| 41 |
# Configuration
|
| 42 |
AUDIO_DIR = "./uploads"
|
|
|
|
| 212 |
segment.export(temp_path, format="wav")
|
| 213 |
|
| 214 |
with torch.no_grad():
|
| 215 |
+
embedding = speaker_model.get_embedding(temp_path).cpu().numpy() # Ensure numpy array
|
| 216 |
+
|
| 217 |
+
# --- FIX: Convert embedding to a flat list for Pinecone query ---
|
| 218 |
+
embedding_list = embedding.flatten().tolist()
|
| 219 |
+
# --- End FIX ---
|
| 220 |
|
| 221 |
query_result = index.query(
|
| 222 |
+
vector=embedding_list, # Use the corrected flat list
|
| 223 |
top_k=1,
|
| 224 |
include_metadata=True
|
| 225 |
)
|
|
|
|
| 230 |
else:
|
| 231 |
speaker_id = f"unknown_{uuid.uuid4().hex[:6]}"
|
| 232 |
speaker_name = f"Speaker_{speaker_id[-4:]}"
|
| 233 |
+
index.upsert([(speaker_id, embedding_list, {"speaker_name": speaker_name})]) # Use corrected list
|
| 234 |
|
| 235 |
os.remove(temp_path)
|
| 236 |
|
|
|
|
| 238 |
**utterance,
|
| 239 |
'speaker': speaker_name,
|
| 240 |
'speaker_id': speaker_id,
|
| 241 |
+
'embedding': embedding_list # Store the corrected list
|
| 242 |
}
|
| 243 |
except Exception as e:
|
| 244 |
+
logger.error(f"Utterance processing failed: {str(e)}", exc_info=True)
|
| 245 |
return {
|
| 246 |
**utterance,
|
| 247 |
'speaker': 'Unknown',
|
|
|
|
| 489 |
|
| 490 |
|
| 491 |
# --- Chart Generation Function ---
|
| 492 |
+
# Removed function as charts are no longer included
|
| 493 |
+
# def generate_anxiety_confidence_chart(composite_scores: Dict, chart_path: str):
|
| 494 |
+
# pass # Placeholder if function is called but not defined
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 495 |
|
| 496 |
|
| 497 |
# --- Acceptance Probability Calculation ---
|
|
|
|
| 559 |
# Normalize to 0-1 and then to percentage
|
| 560 |
# These max/min values are rough estimates and should be calibrated with real data
|
| 561 |
min_possible_score = (0 * w_confidence) + (0 * abs(w_anxiety)) + (0 * w_fluency) + (0 * w_speaking_rate) + (
|
| 562 |
+
0 * abs(w_filler_repetition)) + (0 * w_content_strengths)
|
| 563 |
max_possible_score = (1 * w_confidence) + (1 * abs(w_anxiety)) + (1 * w_fluency) + (1 * w_speaking_rate) + (
|
| 564 |
+
1 * abs(w_filler_repetition)) + (1 * w_content_strengths)
|
| 565 |
|
| 566 |
# Prevent division by zero if all weights are zero or min/max are same
|
| 567 |
if max_possible_score == min_possible_score:
|
|
|
|
| 667 |
prob_color = colors.green if acceptance_prob >= 70 else (
|
| 668 |
colors.orange if acceptance_prob >= 40 else colors.red)
|
| 669 |
|
| 670 |
+
# --- FIX: Call .hexval() as a method ---
|
| 671 |
story.append(Paragraph(
|
| 672 |
+
f"<font size='12' color='{prob_color.hexval()}'><b>Estimated Acceptance Probability: {acceptance_prob:.2f}%</b></font>",
|
| 673 |
ParagraphStyle(name='AcceptanceProbability', parent=styles['Normal'], fontSize=12, spaceAfter=10,
|
| 674 |
alignment=1)
|
| 675 |
))
|
| 676 |
+
# --- End FIX ---
|
| 677 |
|
| 678 |
if acceptance_prob >= 80:
|
| 679 |
story.append(
|
|
|
|
| 758 |
story.append(Spacer(1, 0.2 * inch))
|
| 759 |
|
| 760 |
# --- Charts ---
|
| 761 |
+
story.append(Paragraph("Score Visualization:", h3))
|
| 762 |
chart_path = os.path.join(OUTPUT_DIR, f"anxiety_confidence_{uuid.uuid4().hex[:8]}.png")
|
| 763 |
+
# --- FIX: ensure matplotlib and Image are imported and generate_anxiety_confidence_chart is callable ---
|
| 764 |
try:
|
| 765 |
+
# This function call requires matplotlib and Image to be properly imported and generate_anxiety_confidence_chart to be defined.
|
| 766 |
+
# If you want charts, make sure you have 'matplotlib' in requirements.txt.
|
| 767 |
+
# If you explicitly removed charts, ensure generate_anxiety_confidence_chart is replaced with a dummy or removed entirely.
|
| 768 |
+
# For this response, I am assuming you DO want charts, as per your question.
|
| 769 |
+
# So the imports are at the top, and this function will be called.
|
| 770 |
+
generate_anxiety_confidence_chart(voice_analysis['composite_scores'], chart_path)
|
| 771 |
if os.path.exists(chart_path):
|
| 772 |
+
img = Image(chart_path, width=3.5 * inch, height=2.0 * inch)
|
| 773 |
story.append(img)
|
| 774 |
story.append(Spacer(1, 0.1 * inch))
|
| 775 |
os.remove(chart_path)
|
| 776 |
+
except Exception as chart_e:
|
| 777 |
+
logger.warning(
|
| 778 |
+
f"Could not add chart image to PDF: {chart_e}. Is matplotlib installed and the function defined correctly?")
|
| 779 |
+
# --- End FIX ---
|
| 780 |
# --- End Charts ---
|
| 781 |
|
| 782 |
# Detailed Interpretation from Gemini (if present)
|