UNPKG

local-leetcode-trainer

Version:

A complete local LeetCode practice environment with multi-language support - use your IDE, collaborate with AI, submit with confidence

381 lines (321 loc) 11.8 kB
/** * Fallback Data Validator * Validates and ensures quality of enhanced fallback problem data */ class FallbackValidator { /** * Validate problem data completeness and quality */ static validateProblemData(problem) { const validation = { isValid: true, errors: [], warnings: [], completenessScore: 0, qualityMetrics: {} }; // Validate required fields this.validateRequiredFields(problem, validation); // Validate content quality this.validateContentQuality(problem, validation); // Validate function signatures this.validateFunctionSignatures(problem, validation); // Validate test cases this.validateTestCases(problem, validation); // Calculate completeness score validation.completenessScore = this.calculateCompletenessScore(problem); // Generate quality metrics validation.qualityMetrics = this.generateQualityMetrics(problem); return validation; } /** * Validate required fields are present */ static validateRequiredFields(problem, validation) { const requiredFields = ['id', 'title', 'name', 'difficulty', 'description']; for (const field of requiredFields) { if (!problem[field]) { validation.errors.push(`Missing required field: ${field}`); validation.isValid = false; } } // Validate difficulty value if (problem.difficulty && !['easy', 'medium', 'hard'].includes(problem.difficulty)) { validation.errors.push(`Invalid difficulty: ${problem.difficulty}`); validation.isValid = false; } // Validate ID is a number if (problem.id && typeof problem.id !== 'number') { validation.errors.push(`ID must be a number: ${problem.id}`); validation.isValid = false; } } /** * Validate content quality */ static validateContentQuality(problem, validation) { // Description quality if (problem.description) { if (problem.description.length < 50) { validation.warnings.push('Description is too short (< 50 characters)'); } if (problem.description.length < 100) { validation.warnings.push('Description could be more detailed (< 100 characters)'); } } // Examples validation if (!problem.examples || problem.examples.length === 0) { validation.errors.push('No examples provided'); validation.isValid = false; } else { if (problem.examples.length < 2) { validation.warnings.push('Should have at least 2 examples'); } for (let i = 0; i < problem.examples.length; i++) { const example = problem.examples[i]; if (!example.input || !example.output) { validation.errors.push(`Example ${i + 1} missing input or output`); validation.isValid = false; } } } // Constraints validation if (!problem.constraints || problem.constraints.length === 0) { validation.warnings.push('No constraints provided'); } // Topics validation if (!problem.topics || problem.topics.length === 0) { validation.warnings.push('No topics specified'); } } /** * Validate function signatures */ static validateFunctionSignatures(problem, validation) { const requiredLanguages = ['javascript', 'python', 'java', 'cpp']; if (!problem.functionSignatures) { validation.errors.push('No function signatures provided'); validation.isValid = false; return; } for (const language of requiredLanguages) { const signature = problem.functionSignatures[language]; if (!signature) { validation.warnings.push(`Missing function signature for ${language}`); continue; } // Validate signature structure if (!signature.name) { validation.errors.push(`Missing function name for ${language}`); validation.isValid = false; } if (!signature.params || !Array.isArray(signature.params)) { validation.errors.push(`Invalid parameters for ${language}`); validation.isValid = false; } if (!signature.returnType) { validation.errors.push(`Missing return type for ${language}`); validation.isValid = false; } // Validate parameter structure if (signature.params) { for (let i = 0; i < signature.params.length; i++) { const param = signature.params[i]; if (!param.name || !param.type) { validation.errors.push(`Invalid parameter ${i + 1} for ${language}`); validation.isValid = false; } } } } } /** * Validate test cases */ static validateTestCases(problem, validation) { if (!problem.testCases || problem.testCases.length === 0) { validation.warnings.push('No test cases provided'); return; } if (problem.testCases.length < 5) { validation.warnings.push('Should have at least 5 test cases'); } // Check for different test case categories const categories = new Set(problem.testCases.map(tc => tc.category)); if (!categories.has('basic')) { validation.warnings.push('Missing basic test cases'); } if (!categories.has('edge')) { validation.warnings.push('Missing edge test cases'); } if (!categories.has('stress')) { validation.warnings.push('Missing stress test cases'); } // Validate individual test cases for (let i = 0; i < problem.testCases.length; i++) { const testCase = problem.testCases[i]; if (!testCase.input || !Array.isArray(testCase.input)) { validation.errors.push(`Test case ${i + 1} has invalid input`); validation.isValid = false; } if (!testCase.description) { validation.warnings.push(`Test case ${i + 1} missing description`); } if (!testCase.category) { validation.warnings.push(`Test case ${i + 1} missing category`); } } } /** * Calculate completeness score (0-100) */ static calculateCompletenessScore(problem) { let score = 0; // Basic fields (40 points) if (problem.description && problem.description.length > 100) score += 10; if (problem.examples && problem.examples.length >= 2) score += 10; if (problem.constraints && problem.constraints.length > 0) score += 10; if (problem.topics && problem.topics.length > 0) score += 10; // Function signatures (20 points) const languages = ['javascript', 'python', 'java', 'cpp']; const signaturesComplete = languages.every(lang => problem.functionSignatures?.[lang]?.name && problem.functionSignatures?.[lang]?.params && problem.functionSignatures?.[lang]?.returnType ); if (signaturesComplete) score += 20; // Test cases (30 points) if (problem.testCases && problem.testCases.length >= 5) score += 10; if (problem.testCases && problem.testCases.some(tc => tc.category === 'edge')) score += 10; if (problem.testCases && problem.testCases.some(tc => tc.category === 'stress')) score += 10; // Hints (10 points) if (problem.hints && problem.hints.length >= 2) score += 10; return score; } /** * Generate quality metrics */ static generateQualityMetrics(problem) { return { descriptionLength: problem.description?.length || 0, exampleCount: problem.examples?.length || 0, constraintCount: problem.constraints?.length || 0, topicCount: problem.topics?.length || 0, testCaseCount: problem.testCases?.length || 0, hintCount: problem.hints?.length || 0, languageSupport: Object.keys(problem.functionSignatures || {}).length, hasFollowUp: !!problem.followUp, hasCompanies: !!(problem.companies && problem.companies.length > 0) }; } /** * Validate all problems in the enhanced fallback database */ static validateAllFallbackProblems(problems) { const results = { totalProblems: Object.keys(problems).length, validProblems: 0, invalidProblems: 0, averageCompleteness: 0, problemResults: {}, overallErrors: [], overallWarnings: [] }; let totalCompleteness = 0; for (const [slug, problem] of Object.entries(problems)) { const validation = this.validateProblemData(problem); results.problemResults[slug] = validation; if (validation.isValid) { results.validProblems++; } else { results.invalidProblems++; results.overallErrors.push(`${slug}: ${validation.errors.join(', ')}`); } totalCompleteness += validation.completenessScore; if (validation.warnings.length > 0) { results.overallWarnings.push(`${slug}: ${validation.warnings.join(', ')}`); } } results.averageCompleteness = totalCompleteness / results.totalProblems; return results; } /** * Generate validation report */ static generateValidationReport(validationResults) { const report = []; report.push('# Fallback Data Validation Report\n'); report.push(`**Total Problems:** ${validationResults.totalProblems}`); report.push(`**Valid Problems:** ${validationResults.validProblems}`); report.push(`**Invalid Problems:** ${validationResults.invalidProblems}`); report.push(`**Average Completeness:** ${validationResults.averageCompleteness.toFixed(1)}%\n`); if (validationResults.overallErrors.length > 0) { report.push('## Errors\n'); for (const error of validationResults.overallErrors) { report.push(`- ${error}`); } report.push(''); } if (validationResults.overallWarnings.length > 0) { report.push('## Warnings\n'); for (const warning of validationResults.overallWarnings) { report.push(`- ${warning}`); } report.push(''); } // Top performing problems const sortedProblems = Object.entries(validationResults.problemResults) .sort(([,a], [,b]) => b.completenessScore - a.completenessScore) .slice(0, 5); report.push('## Top Quality Problems\n'); for (const [slug, result] of sortedProblems) { report.push(`- **${slug}**: ${result.completenessScore}% complete`); } return report.join('\n'); } /** * Fix common validation issues automatically */ static autoFixProblem(problem) { const fixed = { ...problem }; // Ensure required arrays exist if (!fixed.examples) fixed.examples = []; if (!fixed.constraints) fixed.constraints = []; if (!fixed.topics) fixed.topics = []; if (!fixed.companies) fixed.companies = []; if (!fixed.testCases) fixed.testCases = []; if (!fixed.hints) fixed.hints = []; // Ensure function signatures exist if (!fixed.functionSignatures) { fixed.functionSignatures = {}; } // Add basic function signatures if missing const languages = ['javascript', 'python', 'java', 'cpp']; for (const lang of languages) { if (!fixed.functionSignatures[lang]) { fixed.functionSignatures[lang] = { name: this.generateFunctionName(fixed.title || 'solution'), params: [{ name: 'param', type: 'any' }], returnType: 'any' }; } } return fixed; } /** * Generate function name from problem title */ static generateFunctionName(title) { return title .replace(/[^a-zA-Z0-9\s]/g, '') .split(' ') .map((word, index) => { if (index === 0) { return word.toLowerCase(); } return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); }) .join(''); } } module.exports = { FallbackValidator };