marcosremar2 commited on
Commit
317700f
·
1 Parent(s): 97280ac

Fix pandas version compatibility and add comprehensive API test script

Browse files
Files changed (2) hide show
  1. Dockerfile +5 -3
  2. test_api.py +269 -0
Dockerfile CHANGED
@@ -8,7 +8,7 @@ ENV DEBIAN_FRONTEND=noninteractive
8
  ENV LC_ALL=C.UTF-8
9
  ENV LANG=C.UTF-8
10
 
11
- # Install system dependencies
12
  RUN apt-get update && \
13
  apt-get install -y \
14
  python3 \
@@ -21,9 +21,11 @@ RUN apt-get update && \
21
  curl \
22
  git \
23
  build-essential \
 
 
24
  && rm -rf /var/lib/apt/lists/*
25
 
26
- # Install Python packages
27
  RUN pip3 install --no-cache-dir \
28
  montreal-forced-aligner==2.2.17 \
29
  fastapi==0.104.1 \
@@ -31,7 +33,7 @@ RUN pip3 install --no-cache-dir \
31
  python-multipart==0.0.6 \
32
  pydantic==2.5.0 \
33
  textgrid==1.5 \
34
- pandas==2.1.4 \
35
  numpy==1.24.3
36
 
37
  # Create workspace
 
8
  ENV LC_ALL=C.UTF-8
9
  ENV LANG=C.UTF-8
10
 
11
+ # Install system dependencies and update CA certificates
12
  RUN apt-get update && \
13
  apt-get install -y \
14
  python3 \
 
21
  curl \
22
  git \
23
  build-essential \
24
+ ca-certificates \
25
+ && update-ca-certificates \
26
  && rm -rf /var/lib/apt/lists/*
27
 
28
+ # Install Python packages with compatible versions for Python 3.8
29
  RUN pip3 install --no-cache-dir \
30
  montreal-forced-aligner==2.2.17 \
31
  fastapi==0.104.1 \
 
33
  python-multipart==0.0.6 \
34
  pydantic==2.5.0 \
35
  textgrid==1.5 \
36
+ pandas==2.0.3 \
37
  numpy==1.24.3
38
 
39
  # Create workspace
test_api.py ADDED
@@ -0,0 +1,269 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Script de teste para a API do Montreal Forced Aligner Portuguese
4
+ Testa todos os endpoints e funcionalidades da API
5
+ """
6
+
7
+ import requests
8
+ import json
9
+ import time
10
+ import os
11
+ from pathlib import Path
12
+ import wave
13
+ import numpy as np
14
+ from io import BytesIO
15
+
16
+ # Configuração da API
17
+ API_BASE_URL = "http://localhost:7860" # Para teste local
18
+ # API_BASE_URL = "https://marcosremar2-ufpalign.hf.space" # Para Hugging Face Spaces
19
+
20
+ def create_test_audio(filename="test_audio.wav", duration=3, sample_rate=16000):
21
+ """
22
+ Cria um arquivo de áudio de teste simples (tom puro).
23
+ Para teste real, use um arquivo WAV com fala em português.
24
+ """
25
+ t = np.linspace(0, duration, int(sample_rate * duration), False)
26
+ # Tom de 440 Hz (Lá)
27
+ audio = np.sin(2 * np.pi * 440 * t) * 0.3
28
+
29
+ # Converter para 16-bit
30
+ audio_int16 = (audio * 32767).astype(np.int16)
31
+
32
+ # Salvar como WAV
33
+ with wave.open(filename, 'w') as wav_file:
34
+ wav_file.setnchannels(1) # Mono
35
+ wav_file.setsampwidth(2) # 16-bit
36
+ wav_file.setframerate(sample_rate)
37
+ wav_file.writeframes(audio_int16.tobytes())
38
+
39
+ print(f"✅ Arquivo de áudio de teste criado: {filename}")
40
+ return filename
41
+
42
+ def test_health_check():
43
+ """Testa o endpoint de health check"""
44
+ print("\n🔍 Testando Health Check...")
45
+ try:
46
+ response = requests.get(f"{API_BASE_URL}/health", timeout=10)
47
+ if response.status_code == 200:
48
+ data = response.json()
49
+ print(f"✅ Health Check OK: {data}")
50
+ return True
51
+ else:
52
+ print(f"❌ Health Check falhou: {response.status_code}")
53
+ return False
54
+ except Exception as e:
55
+ print(f"❌ Erro no Health Check: {e}")
56
+ return False
57
+
58
+ def test_models_list():
59
+ """Testa o endpoint de listagem de modelos"""
60
+ print("\n🔍 Testando listagem de modelos...")
61
+ try:
62
+ response = requests.get(f"{API_BASE_URL}/models", timeout=30)
63
+ if response.status_code == 200:
64
+ data = response.json()
65
+ print(f"✅ Modelos disponíveis:")
66
+ print(f" Acústicos: {data.get('acoustic_models', [])[:3]}...")
67
+ print(f" G2P: {data.get('g2p_models', [])[:3]}...")
68
+ return True
69
+ else:
70
+ print(f"❌ Falha ao listar modelos: {response.status_code}")
71
+ return False
72
+ except Exception as e:
73
+ print(f"❌ Erro ao listar modelos: {e}")
74
+ return False
75
+
76
+ def test_alignment(audio_file=None, text="Esta é uma frase de teste em português"):
77
+ """Testa o endpoint principal de alinhamento"""
78
+ print("\n🔍 Testando alinhamento forçado...")
79
+
80
+ # Usar arquivo de áudio fornecido ou criar um de teste
81
+ if audio_file is None:
82
+ audio_file = create_test_audio()
83
+
84
+ if not os.path.exists(audio_file):
85
+ print(f"❌ Arquivo de áudio não encontrado: {audio_file}")
86
+ return False
87
+
88
+ try:
89
+ # Preparar dados para upload
90
+ files = {'audio': open(audio_file, 'rb')}
91
+ data = {'text': text}
92
+
93
+ print(f"📝 Texto: {text}")
94
+ print(f"🎵 Áudio: {audio_file}")
95
+ print("⏳ Enviando para alinhamento...")
96
+
97
+ # Fazer requisição
98
+ response = requests.post(
99
+ f"{API_BASE_URL}/align",
100
+ files=files,
101
+ data=data,
102
+ timeout=300 # 5 minutos
103
+ )
104
+
105
+ files['audio'].close()
106
+
107
+ if response.status_code == 200:
108
+ result = response.json()
109
+ print(f"✅ Alinhamento concluído!")
110
+ print(f" Arquivo: {result['filename']}")
111
+ print(f" Duração: {result['duration']:.2f}s")
112
+ print(f" Tiers: {len(result['tiers'])}")
113
+
114
+ # Mostrar algumas informações dos tiers
115
+ for tier in result['tiers']:
116
+ print(f" - {tier['name']}: {len(tier['intervals'])} intervalos")
117
+
118
+ return result
119
+ else:
120
+ error_data = response.json() if response.headers.get('content-type') == 'application/json' else response.text
121
+ print(f"❌ Falha no alinhamento: {response.status_code}")
122
+ print(f" Erro: {error_data}")
123
+ return False
124
+
125
+ except Exception as e:
126
+ print(f"❌ Erro no alinhamento: {e}")
127
+ return False
128
+ finally:
129
+ # Limpar arquivo de teste se foi criado
130
+ if audio_file == "test_audio.wav" and os.path.exists(audio_file):
131
+ os.remove(audio_file)
132
+
133
+ def test_download(filename):
134
+ """Testa o download do arquivo TextGrid"""
135
+ print(f"\n🔍 Testando download do TextGrid: {filename}")
136
+ try:
137
+ # Remover extensão .TextGrid se presente
138
+ base_filename = filename.replace('.TextGrid', '')
139
+
140
+ response = requests.get(f"{API_BASE_URL}/download/{base_filename}", timeout=30)
141
+
142
+ if response.status_code == 200:
143
+ print(f"✅ Download concluído!")
144
+ print(f" Tamanho: {len(response.content)} bytes")
145
+ print(f" Tipo: {response.headers.get('content-type', 'unknown')}")
146
+
147
+ # Salvar arquivo localmente para verificação
148
+ download_filename = f"downloaded_{base_filename}.TextGrid"
149
+ with open(download_filename, 'w', encoding='utf-8') as f:
150
+ f.write(response.text)
151
+ print(f" Salvo como: {download_filename}")
152
+
153
+ return True
154
+ else:
155
+ print(f"❌ Falha no download: {response.status_code}")
156
+ return False
157
+ except Exception as e:
158
+ print(f"❌ Erro no download: {e}")
159
+ return False
160
+
161
+ def test_web_interface():
162
+ """Testa se a interface web está acessível"""
163
+ print("\n🔍 Testando interface web...")
164
+ try:
165
+ response = requests.get(API_BASE_URL, timeout=10)
166
+ if response.status_code == 200:
167
+ if "MFA Portuguese Alignment" in response.text:
168
+ print("✅ Interface web acessível e funcionando")
169
+ return True
170
+ else:
171
+ print("⚠️ Interface web acessível mas conteúdo inesperado")
172
+ return False
173
+ else:
174
+ print(f"❌ Interface web inacessível: {response.status_code}")
175
+ return False
176
+ except Exception as e:
177
+ print(f"❌ Erro na interface web: {e}")
178
+ return False
179
+
180
+ def run_full_test(audio_file=None, text=None):
181
+ """Executa todos os testes da API"""
182
+ print("🚀 Iniciando testes completos da API MFA Portuguese")
183
+ print(f"🌐 URL base: {API_BASE_URL}")
184
+ print("="*60)
185
+
186
+ results = {}
187
+
188
+ # Texto padrão em português
189
+ if text is None:
190
+ text = "Olá, este é um teste de alinhamento forçado para português brasileiro."
191
+
192
+ # 1. Health Check
193
+ results['health'] = test_health_check()
194
+
195
+ # 2. Interface Web
196
+ results['web'] = test_web_interface()
197
+
198
+ # 3. Listar Modelos
199
+ results['models'] = test_models_list()
200
+
201
+ # 4. Alinhamento Principal
202
+ alignment_result = test_alignment(audio_file, text)
203
+ results['alignment'] = alignment_result is not False
204
+
205
+ # 5. Download (se alinhamento foi bem-sucedido)
206
+ if alignment_result and isinstance(alignment_result, dict):
207
+ results['download'] = test_download(alignment_result['filename'])
208
+ else:
209
+ results['download'] = False
210
+ print("\n⏭️ Pulando teste de download (alinhamento falhou)")
211
+
212
+ # Relatório final
213
+ print("\n" + "="*60)
214
+ print("📊 RELATÓRIO FINAL DOS TESTES")
215
+ print("="*60)
216
+
217
+ total_tests = len(results)
218
+ passed_tests = sum(1 for result in results.values() if result)
219
+
220
+ for test_name, result in results.items():
221
+ status = "✅ PASSOU" if result else "❌ FALHOU"
222
+ print(f"{test_name.upper():12} | {status}")
223
+
224
+ print("-"*60)
225
+ print(f"RESUMO: {passed_tests}/{total_tests} testes passaram")
226
+
227
+ if passed_tests == total_tests:
228
+ print("🎉 Todos os testes passaram! API funcionando perfeitamente.")
229
+ return True
230
+ else:
231
+ print("⚠️ Alguns testes falharam. Verifique os logs acima.")
232
+ return False
233
+
234
+ def test_with_real_audio():
235
+ """
236
+ Exemplo de como testar com áudio real.
237
+ Substitua pelos seus próprios arquivos.
238
+ """
239
+ # Exemplo de uso com arquivo real
240
+ audio_file = "exemplo.wav" # Substitua pelo seu arquivo
241
+ text = "Transcrição exata do que está sendo falado no áudio"
242
+
243
+ if os.path.exists(audio_file):
244
+ print(f"\n🎯 Testando com áudio real: {audio_file}")
245
+ return run_full_test(audio_file, text)
246
+ else:
247
+ print(f"⚠️ Arquivo {audio_file} não encontrado. Usando áudio de teste.")
248
+ return run_full_test()
249
+
250
+ if __name__ == "__main__":
251
+ import argparse
252
+
253
+ parser = argparse.ArgumentParser(description="Testa a API do MFA Portuguese")
254
+ parser.add_argument("--url", default=API_BASE_URL, help="URL base da API")
255
+ parser.add_argument("--audio", help="Arquivo de áudio para teste")
256
+ parser.add_argument("--text", help="Texto para alinhamento")
257
+ parser.add_argument("--real", action="store_true", help="Tenta usar áudio real")
258
+
259
+ args = parser.parse_args()
260
+
261
+ # Atualizar URL se fornecida
262
+ API_BASE_URL = args.url
263
+
264
+ if args.real:
265
+ success = test_with_real_audio()
266
+ else:
267
+ success = run_full_test(args.audio, args.text)
268
+
269
+ exit(0 if success else 1)