tfrere's picture
tfrere HF Staff
update
6bda4a6
#!/usr/bin/env node
import { fileURLToPath } from 'url';
import path from 'path';
import fs from 'fs/promises';
import chalk from 'chalk';
import readline from 'readline';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const RESULTS_DIR = path.join(__dirname, 'results');
const VISUALIZATIONS_DIR = path.join(__dirname, 'visualizations');
const PUBLIC_DATA_DIR = path.join(__dirname, '..', '..', '..', '..', 'public', 'data');
/**
* Interface de ligne de commande interactive
*/
function createInterface() {
return readline.createInterface({
input: process.stdin,
output: process.stdout
});
}
/**
* Pose une question et retourne la réponse
*/
function askQuestion(rl, question) {
return new Promise((resolve) => {
rl.question(question, resolve);
});
}
/**
* Liste tous les résultats de tests disponibles
*/
async function listTestResults() {
try {
const files = await fs.readdir(RESULTS_DIR);
const resultFiles = files.filter(f => f.endsWith('.json'));
const results = [];
for (const file of resultFiles) {
const filepath = path.join(RESULTS_DIR, file);
const content = await fs.readFile(filepath, 'utf8');
const data = JSON.parse(content);
results.push({
filename: file,
filepath,
testName: data.metadata.test_name,
config: data.metadata.config,
stats: data.metadata.stats,
generatedAt: data.metadata.generated_at
});
}
// Trier par date de génération (plus récent en premier)
results.sort((a, b) => new Date(b.generatedAt) - new Date(a.generatedAt));
return results;
} catch (error) {
console.error(chalk.red('❌ Erreur lors de la lecture des résultats:'), error.message);
return [];
}
}
/**
* Affiche la liste des résultats disponibles
*/
function displayResults(results) {
console.log(chalk.blue.bold('\n📋 Tests UMAP disponibles:\n'));
if (results.length === 0) {
console.log(chalk.yellow('⚠️ Aucun résultat de test trouvé.'));
console.log(chalk.gray(' Exécutez d\'abord: npm run batch-test'));
return;
}
results.forEach((result, index) => {
const date = new Date(result.generatedAt).toLocaleString('fr-FR');
console.log(chalk.cyan(` ${index + 1}. ${result.testName}`));
console.log(chalk.gray(` 📊 ${result.stats.totalFonts} polices`));
console.log(chalk.gray(` 📏 Plage: [${result.stats.xRange[0].toFixed(1)}, ${result.stats.xRange[1].toFixed(1)}] x [${result.stats.yRange[0].toFixed(1)}, ${result.stats.yRange[1].toFixed(1)}]`));
console.log(chalk.gray(` ⚙️ nNeighbors: ${result.config.nNeighbors}, minDist: ${result.config.minDist}`));
console.log(chalk.gray(` 🎯 Weights: ${result.config.embeddingWeight}/${result.config.categoryWeight}`));
console.log(chalk.gray(` 🔗 Fusion: ${result.config.enableFontFusion ? 'ON' : 'OFF'}`));
console.log(chalk.gray(` 📅 ${date}`));
console.log('');
});
}
/**
* Affiche les détails d'un résultat
*/
function displayResultDetails(result) {
console.log(chalk.blue.bold(`\n📊 Détails du test: ${result.testName}\n`));
console.log(chalk.yellow('⚙️ Configuration:'));
console.log(chalk.gray(` nNeighbors: ${result.config.nNeighbors}`));
console.log(chalk.gray(` minDist: ${result.config.minDist}`));
console.log(chalk.gray(` metric: ${result.config.metric}`));
console.log(chalk.gray(` embeddingWeight: ${result.config.embeddingWeight}`));
console.log(chalk.gray(` categoryWeight: ${result.config.categoryWeight}`));
console.log(chalk.gray(` enableFontFusion: ${result.config.enableFontFusion}`));
console.log(chalk.gray(` fusionPrefixLength: ${result.config.fusionPrefixLength}`));
console.log(chalk.yellow('\n📈 Statistiques:'));
console.log(chalk.gray(` Total polices: ${result.stats.totalFonts}`));
console.log(chalk.gray(` Dimensions embedding: ${result.stats.embeddingDimensions}`));
console.log(chalk.gray(` Dimensions catégorie: ${result.stats.categoryDimensions}`));
console.log(chalk.gray(` Plage X: [${result.stats.xRange[0].toFixed(2)}, ${result.stats.xRange[1].toFixed(2)}]`));
console.log(chalk.gray(` Plage Y: [${result.stats.yRange[0].toFixed(2)}, ${result.stats.yRange[1].toFixed(2)}]`));
console.log(chalk.yellow('\n📅 Métadonnées:'));
console.log(chalk.gray(` Généré le: ${new Date(result.generatedAt).toLocaleString('fr-FR')}`));
console.log(chalk.gray(` Fichier: ${result.filename}`));
}
/**
* Déploie un résultat vers l'application
*/
async function deployResult(result) {
try {
console.log(chalk.blue('🔄 Déploiement vers l\'application...'));
// Lire les données du résultat
const content = await fs.readFile(result.filepath, 'utf8');
const data = JSON.parse(content);
// Créer le dossier de destination s'il n'existe pas
await fs.mkdir(PUBLIC_DATA_DIR, { recursive: true });
// Copier vers public/data/typography_data.json
const targetPath = path.join(PUBLIC_DATA_DIR, 'typography_data.json');
await fs.writeFile(targetPath, content);
console.log(chalk.green('✅ Données déployées avec succès !'));
console.log(chalk.gray(` Source: ${result.filename}`));
console.log(chalk.gray(` Destination: public/data/typography_data.json`));
// Vérifier s'il y a une visualisation correspondante
const vizFilename = result.filename.replace('.json', '.html');
const vizPath = path.join(VISUALIZATIONS_DIR, vizFilename);
try {
await fs.access(vizPath);
console.log(chalk.blue('📊 Visualisation disponible:'));
console.log(chalk.gray(` ${vizPath}`));
} catch {
console.log(chalk.yellow('⚠️ Aucune visualisation trouvée pour ce test.'));
}
return true;
} catch (error) {
console.error(chalk.red('❌ Erreur lors du déploiement:'), error.message);
return false;
}
}
/**
* Ouvre une visualisation dans le navigateur
*/
async function openVisualization(result) {
try {
const vizFilename = result.filename.replace('.json', '.html');
const vizPath = path.join(VISUALIZATIONS_DIR, vizFilename);
await fs.access(vizPath);
console.log(chalk.blue('🌐 Ouverture de la visualisation...'));
console.log(chalk.gray(` Fichier: ${vizPath}`));
// Ouvrir dans le navigateur par défaut
const { exec } = await import('child_process');
const { promisify } = await import('util');
const execAsync = promisify(exec);
await execAsync(`open "${vizPath}"`);
console.log(chalk.green('✅ Visualisation ouverte dans le navigateur !'));
} catch (error) {
console.error(chalk.red('❌ Erreur lors de l\'ouverture:'), error.message);
}
}
/**
* Menu principal interactif
*/
async function showMenu(results) {
const rl = createInterface();
while (true) {
console.log(chalk.blue.bold('\n🎛️ Menu de sélection UMAP\n'));
console.log(chalk.gray('1. Lister tous les tests'));
console.log(chalk.gray('2. Voir les détails d\'un test'));
console.log(chalk.gray('3. Déployer un test vers l\'application'));
console.log(chalk.gray('4. Ouvrir la visualisation d\'un test'));
console.log(chalk.gray('5. Quitter'));
const choice = await askQuestion(rl, '\n👉 Votre choix (1-5): ');
switch (choice.trim()) {
case '1':
displayResults(results);
break;
case '2':
if (results.length === 0) {
console.log(chalk.yellow('⚠️ Aucun test disponible.'));
break;
}
const detailIndex = await askQuestion(rl, `Numéro du test (1-${results.length}): `);
const detailIdx = parseInt(detailIndex) - 1;
if (detailIdx >= 0 && detailIdx < results.length) {
displayResultDetails(results[detailIdx]);
} else {
console.log(chalk.red('❌ Numéro invalide.'));
}
break;
case '3':
if (results.length === 0) {
console.log(chalk.yellow('⚠️ Aucun test disponible.'));
break;
}
const deployIndex = await askQuestion(rl, `Numéro du test à déployer (1-${results.length}): `);
const deployIdx = parseInt(deployIndex) - 1;
if (deployIdx >= 0 && deployIdx < results.length) {
const confirm = await askQuestion(rl, `Déployer "${results[deployIdx].testName}" ? (y/N): `);
if (confirm.toLowerCase() === 'y' || confirm.toLowerCase() === 'yes') {
await deployResult(results[deployIdx]);
} else {
console.log(chalk.yellow('❌ Déploiement annulé.'));
}
} else {
console.log(chalk.red('❌ Numéro invalide.'));
}
break;
case '4':
if (results.length === 0) {
console.log(chalk.yellow('⚠️ Aucun test disponible.'));
break;
}
const vizIndex = await askQuestion(rl, `Numéro du test à visualiser (1-${results.length}): `);
const vizIdx = parseInt(vizIndex) - 1;
if (vizIdx >= 0 && vizIdx < results.length) {
await openVisualization(results[vizIdx]);
} else {
console.log(chalk.red('❌ Numéro invalide.'));
}
break;
case '5':
console.log(chalk.green('👋 Au revoir !'));
rl.close();
return;
default:
console.log(chalk.red('❌ Choix invalide.'));
}
}
}
/**
* Mode ligne de commande direct
*/
async function directMode() {
const args = process.argv.slice(2);
if (args.length === 0) {
console.log(chalk.red('❌ Usage: npm run select [test-name]'));
console.log(chalk.gray(' ou: npm run select --list'));
return;
}
const results = await listTestResults();
if (args[0] === '--list' || args[0] === '-l') {
displayResults(results);
return;
}
const testName = args[0];
const result = results.find(r => r.testName === testName);
if (!result) {
console.log(chalk.red(`❌ Test "${testName}" non trouvé.`));
console.log(chalk.gray('Utilisez --list pour voir les tests disponibles.'));
return;
}
displayResultDetails(result);
const rl = createInterface();
const deploy = await askQuestion(rl, 'Déployer ce test ? (y/N): ');
rl.close();
if (deploy.toLowerCase() === 'y' || deploy.toLowerCase() === 'yes') {
await deployResult(result);
}
}
/**
* Fonction principale
*/
async function main() {
try {
console.log(chalk.blue.bold('🎯 Sélection et déploiement UMAP\n'));
const results = await listTestResults();
// Mode interactif si pas d'arguments
if (process.argv.length === 2) {
await showMenu(results);
} else {
// Mode ligne de commande
await directMode();
}
} catch (error) {
console.error(chalk.red('💥 Erreur fatale:'), error.message);
process.exit(1);
}
}
// Exécuter si appelé directement
if (import.meta.url === `file://${process.argv[1]}`) {
main();
}