Skip to content

Commit 440cff8

Browse files
committed
Merge branch 'master' of github.com:chamilo/chamilo-lms
2 parents 962be7f + 76cb706 commit 440cff8

File tree

12 files changed

+63888
-26070
lines changed

12 files changed

+63888
-26070
lines changed

public/main/inc/ajax/record_audio_rtc.ajax.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,25 @@
4444
break;
4545

4646
case Asset::EXERCISE_FEEDBACK:
47+
/** @var TrackEExercise|null $exeAttempt */
48+
$exeAttempt = Container::getTrackEExerciseRepository()->find($trackExerciseId);
49+
50+
if (null === $exeAttempt) {
51+
exit;
52+
}
53+
54+
// Make feedback asset unique per attempt + question (not only per question)
55+
$assetTitle = sprintf('feedback_%d_%d', $questionId, $trackExerciseId);
4756
$asset = (new Asset())
4857
->setCategory(Asset::EXERCISE_FEEDBACK)
49-
->setTitle("feedback_$questionId")
58+
->setTitle($assetTitle)
5059
;
5160

5261
$asset = $assetRepo->createFromRequest($asset, $_FILES['audio_blob']);
5362

5463
$attemptFeedback = (new AttemptFeedback())
5564
->setAsset($asset);
5665

57-
/** @var TrackEExercise $exeAttempt */
58-
$exeAttempt = Container::getTrackEExerciseRepository()->find($trackExerciseId);
5966
$attempt = $exeAttempt->getAttemptByQuestionId($questionId);
6067

6168
if (null === $attempt) {

public/main/inc/lib/diagnoser.lib.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,7 @@ public function get_php_data()
718718
'OPcache' => ['link' => 'https://php.net/opcache', 'expected' => 2, 'comment' => get_lang('This extension should be loaded.')],
719719
'openssl' => ['link' => 'https://php.net/openssl', 'expected' => 2, 'comment' => get_lang('This extension should be loaded.')],
720720
'xsl' => ['link' => 'https://php.net/xsl', 'expected' => 2, 'comment' => get_lang('This extension should be loaded.')],
721+
'xapian' => ['link' => 'https://xapian.org/docs/bindings/php/', 'expected' => 2, 'comment' => get_lang('This extension should be loaded.')],
721722
];
722723

723724
foreach ($extensions as $extension => $data) {

public/main/inc/lib/exercise.lib.php

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5593,7 +5593,7 @@ public static function getOralFileAudio(int $trackExerciseId, int $questionId, b
55935593
*/
55945594
public static function getOralFeedbackAudio(int $attemptId, int $questionId): string
55955595
{
5596-
/** @var TrackEExercise $tExercise */
5596+
/** @var TrackEExercise|null $tExercise */
55975597
$tExercise = Container::getTrackEExerciseRepository()->find($attemptId);
55985598

55995599
if (null === $tExercise) {
@@ -5606,23 +5606,45 @@ public static function getOralFeedbackAudio(int $attemptId, int $questionId): st
56065606
return '';
56075607
}
56085608

5609-
$assetRepo = Container::getAssetRepository();
5610-
$html = '';
5609+
$feedbacks = $qAttempt->getAttemptFeedbacks();
56115610

5612-
// Keep only the latest audio feedback to avoid duplicated players
5613-
foreach ($qAttempt->getAttemptFeedbacks() as $attemptFeedback) {
5614-
$html = Display::tag(
5615-
'audio',
5616-
'',
5617-
[
5618-
'src' => $assetRepo->getAssetUrl($attemptFeedback->getAsset()),
5619-
'controls' => '',
5620-
]
5611+
if ($feedbacks->isEmpty()) {
5612+
return '';
5613+
}
56215614

5622-
);
5615+
$latestFeedback = null;
5616+
5617+
foreach ($feedbacks as $feedback) {
5618+
// Skip feedbacks without asset, just in case
5619+
if (null === $feedback->getAsset()) {
5620+
continue;
5621+
}
5622+
5623+
if (null === $latestFeedback) {
5624+
$latestFeedback = $feedback;
5625+
continue;
5626+
}
5627+
5628+
// Choose the feedback with the latest createdAt
5629+
if ($feedback->getCreatedAt() > $latestFeedback->getCreatedAt()) {
5630+
$latestFeedback = $feedback;
5631+
}
56235632
}
56245633

5625-
return $html;
5634+
if (null === $latestFeedback) {
5635+
return '';
5636+
}
5637+
5638+
$assetRepo = Container::getAssetRepository();
5639+
5640+
return Display::tag(
5641+
'audio',
5642+
'',
5643+
[
5644+
'src' => $assetRepo->getAssetUrl($latestFeedback->getAsset()),
5645+
'controls' => '',
5646+
]
5647+
);
56265648
}
56275649

56285650
public static function getUploadAnswerFiles(int $trackExerciseId, int $questionId, bool $returnUrls = false)

tests/scripts/lang/ai_translate.php

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -45,24 +45,35 @@
4545
$logFile = __DIR__ . "/grok_translate.log";
4646

4747
// Batch size for API calls
48-
$batchSize = 50;
48+
$batchSize = 100;
49+
$batchSizeInTestMode = 20;
4950

5051
// ===================== HELPER FUNCTIONS =====================
5152

5253
/**
5354
* Simple stderr output.
5455
*/
55-
function eprintln(string $msg): void {
56+
function eprintln(string $msg, bool $timestam = false): void {
57+
if ($timestam) {
58+
$msg = '[' . date('H:i:s') . '] ' . $msg;
59+
}
5660
fwrite(STDERR, $msg . PHP_EOL);
5761
}
5862

5963
/**
60-
* Get human-readable language name from code (best-effort).
64+
* Get a human-readable language name from code (best-effort).
6165
*/
6266
function getLanguageName(string $code): string {
6367
static $map = [
6468
'fr' => 'French',
69+
'bg' => 'Bulgarian',
70+
'bn_BD' => 'Bengali (Bangladesh)',
71+
'bo_CN' => 'Tibetan (China)',
72+
'bs_BA' => 'Bosnian (Bosnia and Herzegovina)',
73+
'cs_CZ' => 'Czech (Czech Republic)',
74+
'eo' => 'Esperanto',
6575
'es' => 'Spanish',
76+
'es_MX' => 'Spanish (Mexico)',
6677
'de' => 'German',
6778
'it' => 'Italian',
6879
'nl' => 'Dutch',
@@ -483,7 +494,7 @@ function callGrokTranslateBatch(
483494
'Authorization: Bearer ' . $apiKey,
484495
],
485496
CURLOPT_POSTFIELDS => $payloadJson,
486-
CURLOPT_TIMEOUT => 60,
497+
CURLOPT_TIMEOUT => 600,
487498
]);
488499

489500
$responseBody = curl_exec($ch);
@@ -642,10 +653,10 @@ function buildTargetPoContent(
642653
}
643654

644655
// Parse base messages.en.po
645-
eprintln("Loading base file: {$basePoFile}");
656+
eprintln("Loading base file: {$basePoFile}", true);
646657
$baseEntries = parseBasePoFile($basePoFile);
647658
$totalTerms = count($baseEntries);
648-
eprintln("Base entries loaded: {$totalTerms} (including header and plurals).");
659+
eprintln("Base entries loaded: {$totalTerms} (including header and plurals).", true);
649660

650661
foreach ($langCodes as $lang) {
651662
$lang = trim($lang);
@@ -657,7 +668,7 @@ function buildTargetPoContent(
657668

658669
$targetFile = $translationsDir . "messages.{$lang}.po";
659670
eprintln("------------------------------------------------------------");
660-
eprintln("Processing language: {$lang} ({$targetLangName})");
671+
eprintln("Processing language: {$lang} ({$targetLangName})", true);
661672
eprintln("Target file: {$targetFile}");
662673

663674
if (is_file($targetFile)) {
@@ -682,7 +693,7 @@ function buildTargetPoContent(
682693
$keepRawSingular = []; // msgid => true for entries that we don't modify at all
683694
$processedCount = 0;
684695
$apiBatchCount = 0;
685-
$maxBatches = $testMode ? 1 : PHP_INT_MAX;
696+
$maxBatches = $testMode ? $batchSizeInTestMode : PHP_INT_MAX;
686697

687698
$pendingBatch = [];
688699
$pendingMap = []; // id => ['msgid'=>..., 'action'=>...]
@@ -696,7 +707,7 @@ function buildTargetPoContent(
696707
// Header and plural entries are not sent to API
697708
$processedCount++;
698709
if ($processedCount % 50 === 0) {
699-
eprintln("[{$lang}] Progress: {$processedCount} / {$totalTerms} entries processed (header/plurals included).");
710+
eprintln("[{$lang}] Progress: {$processedCount} / {$totalTerms} entries processed (header/plurals included).", true);
700711
}
701712
continue;
702713
}
@@ -738,7 +749,7 @@ function buildTargetPoContent(
738749
if (count($pendingBatch) >= $batchSize) {
739750
$apiBatchCount++;
740751
eprintln("[{$lang}] Sending batch {$apiBatchCount} to Grok API ("
741-
. count($pendingBatch) . " terms).");
752+
. count($pendingBatch) . " terms).", true);
742753

743754
try {
744755
$translations = callGrokTranslateBatch(
@@ -749,9 +760,9 @@ function buildTargetPoContent(
749760
$pendingBatch
750761
);
751762
eprintln("[{$lang}] Grok API batch {$apiBatchCount} completed, "
752-
. count($translations) . " translations received.");
763+
. count($translations) . " translations received.", true);
753764
} catch (Throwable $ex) {
754-
eprintln("[{$lang}] ERROR while calling Grok API: " . $ex->getMessage());
765+
eprintln("[{$lang}] ERROR while calling Grok API: " . $ex->getMessage(), true);
755766
exit(1);
756767
}
757768

@@ -772,14 +783,15 @@ function buildTargetPoContent(
772783

773784
$targetTranslations[$msgidBatch] = $translated;
774785
logAction($logFile, $lang, $msgidBatch, $actionBatch);
786+
sleep(1);
775787
}
776788

777789
$pendingBatch = [];
778790
$pendingMap = [];
779791

780792
if ($testMode && $apiBatchCount >= $maxBatches) {
781-
eprintln("[{$lang}] Test mode: only one batch has been sent to Grok. "
782-
. "Remaining untranslated entries will be left as-is.");
793+
eprintln("[{$lang}] Test mode: only $batchSizeInTestMode batches have been sent to Grok. "
794+
. "Remaining untranslated entries will be left as-is.", true);
783795
}
784796
}
785797
} elseif ($needsTranslation && $apiBatchCount >= $maxBatches) {
@@ -791,15 +803,15 @@ function buildTargetPoContent(
791803

792804
$processedCount++;
793805
if ($processedCount % 50 === 0) {
794-
eprintln("[{$lang}] Progress: {$processedCount} / {$totalTerms} entries processed (header/plurals included).");
806+
eprintln("[{$lang}] Progress: {$processedCount} / {$totalTerms} entries processed (header/plurals included).", true);
795807
}
796808
}
797809

798810
// Flush remaining pending batch (if any and allowed)
799811
if (!empty($pendingBatch) && $apiBatchCount < $maxBatches) {
800812
$apiBatchCount++;
801813
eprintln("[{$lang}] Sending final batch {$apiBatchCount} to Grok API ("
802-
. count($pendingBatch) . " terms).");
814+
. count($pendingBatch) . " terms).", true);
803815

804816
try {
805817
$translations = callGrokTranslateBatch(
@@ -810,9 +822,9 @@ function buildTargetPoContent(
810822
$pendingBatch
811823
);
812824
eprintln("[{$lang}] Grok API final batch {$apiBatchCount} completed, "
813-
. count($translations) . " translations received.");
825+
. count($translations) . " translations received.", true);
814826
} catch (Throwable $ex) {
815-
eprintln("[{$lang}] ERROR while calling Grok API: " . $ex->getMessage());
827+
eprintln("[{$lang}] ERROR while calling Grok API: " . $ex->getMessage(), true);
816828
exit(1);
817829
}
818830

@@ -838,14 +850,14 @@ function buildTargetPoContent(
838850
$newContent = buildTargetPoContent($baseEntries, $targetParsed, $targetTranslations, $keepRawSingular);
839851

840852
if (file_put_contents($targetFile, $newContent) === false) {
841-
eprintln("[{$lang}] ERROR: Unable to write updated translation file: {$targetFile}");
853+
eprintln("[{$lang}] ERROR: Unable to write updated translation file: {$targetFile}", true);
842854
exit(1);
843855
}
844856

845-
eprintln("[{$lang}] Updated translation file written: {$targetFile}");
857+
eprintln("[{$lang}] Updated translation file written: {$targetFile}", true);
846858
if ($testMode) {
847-
eprintln("[{$lang}] NOTE: Test mode was enabled; only one API batch of up to {$batchSize} terms was translated.");
859+
eprintln("[{$lang}] NOTE: Test mode was enabled; only {$batchSizeInTestMode} API batch of up to {$batchSize} terms was translated.");
848860
}
849861
}
850862

851-
eprintln("All requested languages processed.");
863+
eprintln("All requested languages processed.", true);

0 commit comments

Comments
 (0)