adpa-enterprise-framework-automation
Version:
Modular, standards-compliant Node.js/TypeScript automation framework for enterprise requirements, project, and data management. Provides CLI and API for BABOK v3, PMBOK 7th Edition, and DMBOK 2.0 (in progress). Production-ready Express.js API with TypeSpe
369 lines • 15 kB
JavaScript
/**
* Document Intelligence Service
* Phase 1: AI-powered document analysis and optimization
*/
import { Logger } from '../utils/logger.js';
/**
* Document Intelligence implementation using AI analysis
* Provides insights for better document structure and content optimization
*/
export class DocumentIntelligence {
logger;
constructor() {
this.logger = new Logger('DocumentIntelligence');
}
/**
* Analyze document structure and provide optimization recommendations
*/
async analyzeDocumentStructure(markdownContent) {
const operationId = this.generateOperationId();
this.logger.info('Starting document structure analysis', {
operationId,
contentLength: markdownContent.length
});
try {
const analysis = await this.performStructuralAnalysis(markdownContent, operationId);
this.logger.info('Document analysis completed', {
operationId,
complexity: analysis.complexity,
keyPointsCount: analysis.keyPoints.length,
visualizationOpportunities: analysis.visualizationOpportunities.length
});
return analysis;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.logger.error('Document analysis failed', {
operationId,
error: errorMessage
});
throw new Error(`Document analysis failed: ${errorMessage}`);
}
}
/**
* Extract key points from document content
*/
async extractKeyPoints(content) {
this.logger.debug('Extracting key points', { contentLength: content.length });
const keyPoints = [];
const lines = content.split('\n');
let position = 0;
for (const line of lines) {
const trimmedLine = line.trim();
// Extract headings as key points
if (trimmedLine.startsWith('#')) {
const level = this.getHeadingLevel(trimmedLine);
const text = trimmedLine.replace(/^#+\s*/, '');
keyPoints.push({
text,
importance: this.calculateImportanceByLevel(level),
category: 'heading',
position
});
}
// Extract bullet points
if (trimmedLine.startsWith('-') || trimmedLine.startsWith('*')) {
const text = trimmedLine.replace(/^[-*]\s*/, '');
if (text.length > 10) { // Filter out short items
keyPoints.push({
text,
importance: 0.6,
category: 'bullet_point',
position
});
}
}
// Extract sentences with emphasis (bold, italic)
if (trimmedLine.includes('**') || trimmedLine.includes('*')) {
const emphasisMatch = trimmedLine.match(/\*\*([^*]+)\*\*|\*([^*]+)\*/);
if (emphasisMatch) {
const text = emphasisMatch[1] || emphasisMatch[2];
keyPoints.push({
text,
importance: 0.7,
category: 'emphasis',
position
});
}
}
position++;
}
// Sort by importance and return top points
return keyPoints
.sort((a, b) => b.importance - a.importance)
.slice(0, 20); // Limit to top 20 key points
}
/**
* Suggest visualization opportunities from content
*/
async suggestVisualizations(content) {
this.logger.debug('Analyzing visualization opportunities', { contentLength: content.length });
const opportunities = [];
// Look for tables
const tableMatches = content.match(/\|.*\|/g);
if (tableMatches && tableMatches.length > 2) {
opportunities.push({
type: 'table',
data: this.extractTableData(content),
confidence: 0.9,
description: 'Structured data detected that would benefit from table formatting'
});
}
// Look for numbered lists (potential process flows)
const numberedListPattern = /^\s*\d+\.\s+/gm;
const numberedMatches = content.match(numberedListPattern);
if (numberedMatches && numberedMatches.length >= 3) {
opportunities.push({
type: 'diagram',
data: this.extractProcessSteps(content),
confidence: 0.7,
description: 'Sequential steps detected that could be visualized as a process flow diagram'
});
}
// Look for comparative data
const comparisonKeywords = ['vs', 'versus', 'compared to', 'before', 'after'];
const hasComparisons = comparisonKeywords.some(keyword => content.toLowerCase().includes(keyword));
if (hasComparisons) {
opportunities.push({
type: 'chart',
data: { type: 'comparison' },
confidence: 0.6,
description: 'Comparative data found that could be visualized as charts'
});
}
// Look for statistics and numbers
const numberPattern = /\b\d+%|\b\d+\.\d+%|\$\d+|\b\d+k|\b\d+M/g;
const numberMatches = content.match(numberPattern);
if (numberMatches && numberMatches.length >= 5) {
opportunities.push({
type: 'infographic',
data: { statistics: numberMatches },
confidence: 0.8,
description: 'Multiple statistics found that could be presented as an infographic'
});
}
return opportunities.sort((a, b) => b.confidence - a.confidence);
}
/**
* Assess document complexity
*/
async assessComplexity(content) {
this.logger.debug('Assessing document complexity', { contentLength: content.length });
const factors = [];
let totalScore = 0;
// Factor 1: Document length
const wordCount = content.split(/\s+/).length;
const lengthScore = Math.min(wordCount / 1000, 1); // Normalize to 0-1
factors.push({
name: 'Document Length',
value: lengthScore,
weight: 0.2,
description: `Document contains ${wordCount} words`
});
totalScore += lengthScore * 0.2;
// Factor 2: Structural complexity
const headingCount = (content.match(/^#+\s/gm) || []).length;
const structureScore = Math.min(headingCount / 20, 1);
factors.push({
name: 'Structural Complexity',
value: structureScore,
weight: 0.3,
description: `Document has ${headingCount} headings and sections`
});
totalScore += structureScore * 0.3;
// Factor 3: Technical content
const technicalTerms = ['API', 'SDK', 'framework', 'architecture', 'implementation', 'configuration'];
const technicalMatches = technicalTerms.filter(term => content.toLowerCase().includes(term.toLowerCase())).length;
const technicalScore = Math.min(technicalMatches / technicalTerms.length, 1);
factors.push({
name: 'Technical Content',
value: technicalScore,
weight: 0.25,
description: `Contains ${technicalMatches} technical concepts`
});
totalScore += technicalScore * 0.25;
// Factor 4: Data complexity
const tableCount = (content.match(/\|.*\|/g) || []).length;
const codeBlockCount = (content.match(/```/g) || []).length / 2;
const dataScore = Math.min((tableCount + codeBlockCount) / 10, 1);
factors.push({
name: 'Data Complexity',
value: dataScore,
weight: 0.25,
description: `Contains ${tableCount} tables and ${codeBlockCount} code blocks`
});
totalScore += dataScore * 0.25;
// Generate recommendations based on complexity
const recommendations = this.generateComplexityRecommendations(totalScore, factors);
return {
score: totalScore,
factors,
recommendations
};
}
// Private helper methods
async performStructuralAnalysis(content, operationId) {
// Extract key points
const keyPoints = await this.extractKeyPoints(content);
// Suggest visualizations
const visualizationOpportunities = await this.suggestVisualizations(content);
// Assess complexity
const complexity = await this.assessComplexity(content);
// Calculate readability score (simplified implementation)
const readabilityScore = this.calculateReadabilityScore(content);
// Calculate structure score
const structureScore = this.calculateStructureScore(content);
// Suggest layouts based on content analysis
const suggestedLayouts = this.suggestLayouts(complexity, visualizationOpportunities);
// Generate compliance flags
const complianceFlags = this.generateComplianceFlags(content);
return {
complexity: this.mapComplexityScore(complexity.score),
suggestedLayouts,
keyPoints,
visualizationOpportunities,
complianceFlags,
readabilityScore,
structureScore
};
}
getHeadingLevel(line) {
const match = line.match(/^#+/);
return match ? match[0].length : 0;
}
calculateImportanceByLevel(level) {
// Higher importance for higher-level headings
switch (level) {
case 1: return 1.0;
case 2: return 0.8;
case 3: return 0.6;
case 4: return 0.4;
default: return 0.2;
}
}
extractTableData(content) {
const tableLines = content.split('\n').filter(line => line.includes('|'));
return {
rows: tableLines.length,
hasHeaders: tableLines.length > 1 && tableLines[1].includes('---')
};
}
extractProcessSteps(content) {
const steps = content.match(/^\s*\d+\.\s+(.+)$/gm) || [];
return {
stepCount: steps.length,
steps: steps.slice(0, 10) // Limit for brevity
};
}
generateComplexityRecommendations(score, factors) {
const recommendations = [];
if (score > 0.8) {
recommendations.push('Consider breaking this document into multiple smaller documents');
recommendations.push('Use executive summary for high-level overview');
recommendations.push('Implement progressive disclosure with collapsible sections');
}
else if (score > 0.6) {
recommendations.push('Add table of contents for better navigation');
recommendations.push('Use more visual elements to break up dense text');
recommendations.push('Consider adding section summaries');
}
else {
recommendations.push('Document complexity is appropriate for target audience');
recommendations.push('Consider adding more detailed explanations if needed');
}
// Factor-specific recommendations
const highFactors = factors.filter(f => f.value > 0.7);
for (const factor of highFactors) {
switch (factor.name) {
case 'Technical Content':
recommendations.push('Include glossary for technical terms');
break;
case 'Data Complexity':
recommendations.push('Use interactive charts and collapsible tables');
break;
case 'Structural Complexity':
recommendations.push('Implement hierarchical navigation');
break;
}
}
return recommendations;
}
calculateReadabilityScore(content) {
// Simplified readability calculation
const sentences = content.split(/[.!?]+/).length;
const words = content.split(/\s+/).length;
const avgWordsPerSentence = words / sentences;
// Lower score for longer sentences (harder to read)
return Math.max(0, Math.min(1, (25 - avgWordsPerSentence) / 25));
}
calculateStructureScore(content) {
const lines = content.split('\n');
const headings = lines.filter(line => line.trim().startsWith('#')).length;
const paragraphs = lines.filter(line => line.trim().length > 50).length;
// Good structure has balanced headings to content ratio
const ratio = headings / Math.max(paragraphs, 1);
return Math.min(1, ratio * 5); // Optimal ratio around 0.2
}
suggestLayouts(complexity, visualizations) {
const layouts = [];
if (complexity.score > 0.8) {
layouts.push('executive-summary', 'technical-detailed', 'appendix-heavy');
}
else if (complexity.score > 0.6) {
layouts.push('technical-standard', 'business-proposal');
}
else {
layouts.push('simple-report', 'presentation-friendly');
}
if (visualizations.length > 3) {
layouts.push('visual-heavy', 'infographic-style');
}
return layouts;
}
generateComplianceFlags(content) {
const flags = [];
// Check for accessibility issues
if (content.includes('![') && !content.includes('alt=')) {
flags.push({
type: 'accessibility',
description: 'Images missing alt text',
severity: 'warning',
autoFixable: true
});
}
// Check for broken links
const linkPattern = /\[([^\]]+)\]\(([^)]+)\)/g;
const links = Array.from(content.matchAll(linkPattern));
for (const link of links) {
if (link[2].startsWith('http') && !this.isValidUrl(link[2])) {
flags.push({
type: 'broken_link',
description: `Potentially broken link: ${link[2]}`,
severity: 'warning',
autoFixable: false
});
}
}
return flags;
}
mapComplexityScore(score) {
if (score < 0.4)
return 'low';
if (score < 0.7)
return 'medium';
return 'high';
}
isValidUrl(url) {
try {
new URL(url);
return true;
}
catch {
return false;
}
}
generateOperationId() {
return `intelligence-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
}
//# sourceMappingURL=document-intelligence.js.map