- Création de docs/app/ pour la documentation de l'application - Création de docs/data/ pour les données utilisées par l'application - Déplacement de la documentation technique vers docs/app/ - Déplacement des données métier vers docs/data/ - Mise à jour de tous les liens et références dans les fichiers - Mise à jour des scripts (extract-technologies.js, analyze-business-metrics.js) - Mise à jour des fichiers JavaScript (custom.js, strategie-link.js) - Création de README.md dans docs/, docs/app/ et docs/data/ - Mise à jour du Readme.md principal avec les nouveaux chemins
262 lines
11 KiB
JavaScript
Executable File
262 lines
11 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Script pour extraire les technologies de technologies-duniter.md
|
|
* et générer les blips pour le radar business
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
// Mapping des compétences de l'équipe
|
|
const teamSkills = {
|
|
'poka': ['Flutter', 'Dart', 'ProxMox', 'bash', 'Python', 'infrastructure'],
|
|
'ManUtopiK': ['VueJS', 'Nuxt.js', 'JavaScript', 'TypeScript', 'CMS', 'web'],
|
|
'aya': ['Linux', 'glusterfs', 'cephfs', 'ipfs', 'infrastructure', 'systèmes distribués', 'ThreeFold'],
|
|
'Eloïs': ['Rust', 'blockchain', 'Substrate', 'migration'],
|
|
'Fred': ['IPFS', 'Secure ScuttleButt', 'Nostr', 'TiddlyWiki', 'développement', 'ThreeFold'],
|
|
'Vivien': ['Cesium', 'Godot'],
|
|
'1000i100': ['Serverless', 'GitLab', 'CI/CD', 'Docker', 'web'],
|
|
'tuxmain': ['cryptographie', 'chiffrage', 'math', 'électronique'],
|
|
'boris': ['UX', 'UI', 'Figma', 'LLM', 'JavaScript', 'TypeScript', 'APIs', 'Vis.js'],
|
|
'Syoul': ['bidouille', 'résilience', 'domotique', 'infrastructure décentralisée'],
|
|
'Hugo': ['financement', 'rédaction', 'gestion'],
|
|
'Yvv': ['gestion', 'médiathèque', 'wiki']
|
|
};
|
|
|
|
// Mapping technologies -> compétences de l'équipe
|
|
function findTeamCoverage(techName, techKeywords) {
|
|
const coverage = new Set();
|
|
const techLower = techName.toLowerCase();
|
|
const keywords = techKeywords.map(k => k.toLowerCase());
|
|
|
|
for (const [member, skills] of Object.entries(teamSkills)) {
|
|
const memberSkills = skills.map(s => s.toLowerCase());
|
|
|
|
// Vérifier si le nom de la technologie correspond
|
|
if (memberSkills.some(skill => techLower.includes(skill) || skill.includes(techLower))) {
|
|
coverage.add(member);
|
|
}
|
|
|
|
// Vérifier les mots-clés
|
|
for (const keyword of keywords) {
|
|
if (memberSkills.some(skill => keyword.includes(skill) || skill.includes(keyword))) {
|
|
coverage.add(member);
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
count: coverage.size,
|
|
members: Array.from(coverage),
|
|
level: coverage.size >= 3 ? 'expert' : coverage.size >= 2 ? 'intermediate' : 'beginner',
|
|
gap: coverage.size === 0 ? 'high' : coverage.size === 1 ? 'high' : coverage.size === 2 ? 'medium' : 'low'
|
|
};
|
|
}
|
|
|
|
// Classification par quadrant et ring (à affiner manuellement)
|
|
function classifyTechnology(techName, category) {
|
|
const name = techName.toLowerCase();
|
|
|
|
// Technologies différenciantes (core/strategic)
|
|
if (name.includes('rust') || name.includes('substrate') || name.includes('blockchain')) {
|
|
return {
|
|
quadrant: 'technologies-differentiantes',
|
|
ring: 'core',
|
|
businessImpact: 'high',
|
|
differentiation: 'high',
|
|
riskLevel: 'medium'
|
|
};
|
|
}
|
|
|
|
// Technologies de commodité (support)
|
|
if (name.includes('docker') || name.includes('postgresql') || name.includes('linux')) {
|
|
return {
|
|
quadrant: 'technologies-commodite',
|
|
ring: 'support',
|
|
businessImpact: 'medium',
|
|
differentiation: 'low',
|
|
riskLevel: 'low'
|
|
};
|
|
}
|
|
|
|
// Technologies émergentes (strategic/assess)
|
|
if (name.includes('ipfs') || name.includes('nostr') || name.includes('serverless') ||
|
|
name.includes('threefold') || name.includes('zero os') || name.includes('mycelium') ||
|
|
name.includes('aibox') || name.includes('3node') || name.includes('d3.js') ||
|
|
name.includes('echarts') || name.includes('grafana') || name.includes('leaflet') ||
|
|
name.includes('cytoscape')) {
|
|
return {
|
|
quadrant: 'technologies-emergentes',
|
|
ring: 'strategic',
|
|
businessImpact: 'high',
|
|
differentiation: 'high',
|
|
riskLevel: 'medium'
|
|
};
|
|
}
|
|
|
|
// Par défaut
|
|
return {
|
|
quadrant: 'technologies-commodite',
|
|
ring: 'support',
|
|
businessImpact: 'medium',
|
|
differentiation: 'medium',
|
|
riskLevel: 'medium'
|
|
};
|
|
}
|
|
|
|
// Générer un blip
|
|
function generateBlip(tech, category, description) {
|
|
const classification = classifyTechnology(tech.name, category);
|
|
const coverage = findTeamCoverage(tech.name, tech.keywords || []);
|
|
|
|
const blip = `---
|
|
title: "${tech.name}"
|
|
ring: ${classification.ring}
|
|
quadrant: ${classification.quadrant}
|
|
tags: [${(tech.tags || []).join(', ')}]
|
|
businessImpact: ${classification.businessImpact}
|
|
costToReplace: ${tech.costToReplace || 0}
|
|
revenueImpact: ${tech.revenueImpact || 'indirect'}
|
|
riskLevel: ${classification.riskLevel}
|
|
competencyLevel: ${coverage.level}
|
|
maintenanceCost: ${tech.maintenanceCost || 0}
|
|
differentiation: ${classification.differentiation}
|
|
teamCoverage: ${coverage.count}
|
|
skillGap: ${coverage.gap}
|
|
---
|
|
|
|
${description || tech.description || `Description de ${tech.name}.`}
|
|
|
|
## Impact Business
|
|
|
|
${tech.businessImpact || 'À compléter'}
|
|
|
|
## Coûts
|
|
|
|
- Coût de remplacement : ${tech.costToReplace || 0}€
|
|
- Coût de maintenance annuel : ${tech.maintenanceCost || 0}€
|
|
|
|
## Compétences
|
|
|
|
- Nombre de personnes maîtrisant : ${coverage.count}
|
|
- Membres de l'équipe : ${coverage.members.join(', ') || 'Aucun'}
|
|
- Niveau moyen : ${coverage.level}
|
|
- Risque de compétence manquante : ${coverage.gap}
|
|
|
|
## Recommandations
|
|
|
|
${tech.recommendations || 'À compléter avec des recommandations stratégiques.'}
|
|
`;
|
|
|
|
return blip;
|
|
}
|
|
|
|
// Parser le fichier technologies-duniter.md
|
|
function parseTechnologiesFile(filePath) {
|
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
const technologies = [];
|
|
|
|
// Liste des technologies principales à extraire
|
|
const techList = [
|
|
{ name: 'Rust', keywords: ['Rust', 'blockchain', 'Substrate'], category: 'Langages' },
|
|
{ name: 'Python', keywords: ['Python', 'CLI'], category: 'Langages' },
|
|
{ name: 'JavaScript/TypeScript', keywords: ['JavaScript', 'TypeScript', 'web'], category: 'Langages' },
|
|
{ name: 'Dart', keywords: ['Dart', 'Flutter'], category: 'Langages' },
|
|
{ name: 'Substrate Framework', keywords: ['Substrate', 'Rust', 'blockchain'], category: 'Frameworks' },
|
|
{ name: 'Nuxt.js', keywords: ['Nuxt', 'Vue', 'SSR'], category: 'Frameworks' },
|
|
{ name: 'Vue.js', keywords: ['Vue', 'JavaScript'], category: 'Frameworks' },
|
|
{ name: 'Flutter', keywords: ['Flutter', 'Dart'], category: 'Frameworks' },
|
|
{ name: 'NetlifyCMS', keywords: ['CMS', 'Git'], category: 'CMS' },
|
|
{ name: 'WordUp CMS', keywords: ['CMS'], category: 'CMS' },
|
|
{ name: 'Docker', keywords: ['Docker', 'conteneurisation'], category: 'Infrastructure' },
|
|
{ name: 'Kubernetes', keywords: ['Kubernetes', 'orchestration'], category: 'Infrastructure' },
|
|
{ name: 'PostgreSQL', keywords: ['PostgreSQL', 'base de données'], category: 'Infrastructure' },
|
|
{ name: 'ProxMox', keywords: ['ProxMox', 'virtualisation'], category: 'Infrastructure' },
|
|
{ name: 'Linux', keywords: ['Linux', 'système'], category: 'Infrastructure' },
|
|
{ name: 'IPFS', keywords: ['IPFS', 'distribué'], category: 'Technologies distribuées' },
|
|
{ name: 'Nostr', keywords: ['Nostr', 'protocole'], category: 'Technologies distribuées' },
|
|
{ name: 'GitLab CI/CD', keywords: ['GitLab', 'CI/CD'], category: 'DevOps' },
|
|
{ name: 'Serverless', keywords: ['Serverless'], category: 'Architecture' },
|
|
{ name: 'Squid', keywords: ['Squid', 'indexer', 'GraphQL'], category: 'Outils' },
|
|
{ name: 'Cryptographie', keywords: ['cryptographie', 'chiffrage'], category: 'Sécurité' },
|
|
{ name: 'Bash', keywords: ['bash', 'scripting'], category: 'Outils' },
|
|
// Technologies ThreeFold
|
|
{ name: 'Zero OS', keywords: ['Zero OS', 'bare metal', 'cloud décentralisé'], category: 'Infrastructure décentralisée' },
|
|
{ name: 'ThreeFold Grid', keywords: ['ThreeFold', 'Grid', 'infrastructure décentralisée'], category: 'Infrastructure décentralisée' },
|
|
{ name: '3Node', keywords: ['3Node', 'nœuds', 'serveurs'], category: 'Infrastructure décentralisée' },
|
|
{ name: 'ThreeFold Compute', keywords: ['ThreeFold', 'Compute', 'edge computing'], category: 'Infrastructure décentralisée' },
|
|
{ name: 'ThreeFold Data Storage', keywords: ['ThreeFold', 'Storage', 'stockage distribué'], category: 'Infrastructure décentralisée' },
|
|
{ name: 'Mycelium Network', keywords: ['Mycelium', 'Network', 'réseau overlay'], category: 'Infrastructure décentralisée' },
|
|
{ name: 'ThreeFold Blockchain', keywords: ['ThreeFold', 'Blockchain'], category: 'Blockchain' },
|
|
{ name: 'ThreeFold Cloud', keywords: ['ThreeFold', 'Cloud', 'Kubernetes'], category: 'Cloud décentralisé' },
|
|
{ name: 'AIBox', keywords: ['AIBox', 'IA', 'machine learning'], category: 'IA' },
|
|
// Technologies DataViz
|
|
{ name: 'D3.js', keywords: ['D3.js', 'DataViz', 'JavaScript'], category: 'Data Visualization' },
|
|
{ name: 'ECharts', keywords: ['ECharts', 'DataViz', 'Apache'], category: 'Data Visualization' },
|
|
{ name: 'Grafana', keywords: ['Grafana', 'Monitoring', 'Dashboard'], category: 'Data Visualization' },
|
|
{ name: 'Leaflet', keywords: ['Leaflet', 'Cartographie', 'Map'], category: 'Data Visualization' },
|
|
{ name: 'Cytoscape.js', keywords: ['Cytoscape', 'Graphes', 'Réseaux'], category: 'Data Visualization' }
|
|
];
|
|
|
|
// Pour chaque technologie, créer un blip
|
|
for (const tech of techList) {
|
|
// Extraire la description depuis le fichier si disponible
|
|
let description = '';
|
|
const techRegex = new RegExp(`#### ${tech.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?(?=#### |## |$)`, 'i');
|
|
const match = content.match(techRegex);
|
|
if (match) {
|
|
description = match[0].split('\n').slice(1).join('\n').trim();
|
|
// Limiter à 10 lignes
|
|
description = description.split('\n').slice(0, 10).join('\n');
|
|
} else {
|
|
description = `Technologie ${tech.name} utilisée dans l'écosystème Duniter/Ğ1.`;
|
|
}
|
|
|
|
technologies.push({
|
|
name: tech.name,
|
|
category: tech.category,
|
|
description: description,
|
|
keywords: tech.keywords,
|
|
tags: tech.keywords.slice(0, 3)
|
|
});
|
|
}
|
|
|
|
return technologies;
|
|
}
|
|
|
|
// Main
|
|
function main() {
|
|
const techFile = path.join(__dirname, '../docs/data/technologies-duniter.md');
|
|
const outputDir = path.join(__dirname, '../radar-business/2025-01-15');
|
|
|
|
if (!fs.existsSync(outputDir)) {
|
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
}
|
|
|
|
console.log('Extraction des technologies...');
|
|
const technologies = parseTechnologiesFile(techFile);
|
|
|
|
console.log(`Trouvé ${technologies.length} technologies`);
|
|
|
|
for (const tech of technologies) {
|
|
const blip = generateBlip(tech, tech.category, tech.description);
|
|
const filename = tech.name.toLowerCase()
|
|
.replace(/[^a-z0-9]+/g, '-')
|
|
.replace(/^-|-$/g, '') + '.md';
|
|
|
|
const filePath = path.join(outputDir, filename);
|
|
fs.writeFileSync(filePath, blip, 'utf-8');
|
|
console.log(`Généré: ${filename}`);
|
|
}
|
|
|
|
console.log(`\n${technologies.length} blips générés dans ${outputDir}`);
|
|
}
|
|
|
|
if (require.main === module) {
|
|
main();
|
|
}
|
|
|
|
module.exports = { parseTechnologiesFile, generateBlip, findTeamCoverage };
|
|
|