Moved the Agents into their own repo. Kept the agent.pl just for reference
This commit is contained in:
parent
22381be29a
commit
8680a02b13
18132 changed files with 0 additions and 2569420 deletions
|
|
@ -1,277 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Quality Control and Linting for Generated Game Guides
|
||||
|
||||
Validates that generated guides meet all requirements:
|
||||
- No forbidden provider mentions
|
||||
- No HTML entity escaping
|
||||
- All required sections present
|
||||
- Sufficient startup parameters
|
||||
- Proper <PLACEHOLDER> handling
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
class GuideQualityControl:
|
||||
def __init__(self, md_dir="out/md", pdf_dir="out/pdfs"):
|
||||
self.md_dir = Path(md_dir)
|
||||
self.pdf_dir = Path(pdf_dir)
|
||||
|
||||
# Forbidden terms that should not appear in output
|
||||
self.forbidden_terms = [
|
||||
'OGP', 'OpenGamePanel', 'LinuxGSM', 'Nitrado',
|
||||
'GameServers.com', 'G-Portal', 'gameservers.com',
|
||||
'control panel', 'panel setup', 'OGP module'
|
||||
]
|
||||
|
||||
# Required sections that must be present
|
||||
self.required_sections = [
|
||||
'## Overview',
|
||||
'## System Requirements',
|
||||
'## Ports & Networking',
|
||||
'## Startup Parameters',
|
||||
'## Configuration Files',
|
||||
'## Steam Workshop',
|
||||
'## Admin & RCON',
|
||||
'## Saves, Backups & Wipes',
|
||||
'## Performance Tuning',
|
||||
'## Troubleshooting',
|
||||
'## Appendices'
|
||||
]
|
||||
|
||||
self.errors = []
|
||||
self.warnings = []
|
||||
|
||||
def check_forbidden_terms(self, content, file_name):
|
||||
"""Check for forbidden provider mentions"""
|
||||
content_lower = content.lower()
|
||||
found_terms = []
|
||||
|
||||
for term in self.forbidden_terms:
|
||||
if term.lower() in content_lower:
|
||||
# Count occurrences for severity assessment
|
||||
count = content_lower.count(term.lower())
|
||||
found_terms.append(f"{term} ({count}x)")
|
||||
|
||||
if found_terms:
|
||||
self.errors.append(f"{file_name}: Contains forbidden terms: {', '.join(found_terms)}")
|
||||
|
||||
def check_html_entities(self, content, file_name):
|
||||
"""Check for HTML entity escapes"""
|
||||
html_entities = ['<', '>', '&', '"', '&#']
|
||||
found_entities = []
|
||||
|
||||
for entity in html_entities:
|
||||
if entity in content:
|
||||
count = content.count(entity)
|
||||
found_entities.append(f"{entity} ({count}x)")
|
||||
|
||||
if found_entities:
|
||||
self.errors.append(f"{file_name}: Contains HTML entities: {', '.join(found_entities)}")
|
||||
|
||||
def check_placeholder_handling(self, content, file_name):
|
||||
"""Verify <PLACEHOLDER> text is literal, not escaped"""
|
||||
# Check for properly formatted placeholders
|
||||
placeholder_pattern = r'<[A-Z_]+>'
|
||||
placeholders = re.findall(placeholder_pattern, content)
|
||||
|
||||
# Check for escaped placeholders that shouldn't be
|
||||
escaped_pattern = r'<[A-Z_]+>'
|
||||
escaped_placeholders = re.findall(escaped_pattern, content)
|
||||
|
||||
if escaped_placeholders:
|
||||
self.errors.append(f"{file_name}: Contains escaped placeholders: {', '.join(escaped_placeholders)}")
|
||||
|
||||
# Log found placeholders for information
|
||||
if placeholders:
|
||||
self.warnings.append(f"{file_name}: Contains {len(placeholders)} placeholders: {', '.join(placeholders[:3])}{'...' if len(placeholders) > 3 else ''}")
|
||||
|
||||
def check_required_sections(self, content, file_name):
|
||||
"""Verify all required sections are present"""
|
||||
missing_sections = []
|
||||
|
||||
for section in self.required_sections:
|
||||
if section not in content:
|
||||
missing_sections.append(section)
|
||||
|
||||
if missing_sections:
|
||||
self.errors.append(f"{file_name}: Missing required sections: {', '.join(missing_sections)}")
|
||||
|
||||
def check_startup_parameters(self, content, file_name):
|
||||
"""Verify sufficient startup parameters are documented"""
|
||||
# Look for startup parameters table
|
||||
param_pattern = r'\| [+\-][a-zA-Z0-9_.]+'
|
||||
parameters = re.findall(param_pattern, content)
|
||||
|
||||
param_count = len(parameters)
|
||||
if param_count < 10:
|
||||
self.warnings.append(f"{file_name}: Only {param_count} startup parameters found (minimum 10 recommended)")
|
||||
elif param_count < 15:
|
||||
self.warnings.append(f"{file_name}: {param_count} startup parameters found (15+ recommended for exhaustive coverage)")
|
||||
|
||||
def check_port_mappings(self, content, file_name):
|
||||
"""Verify port mapping table is comprehensive"""
|
||||
# Look for port table entries
|
||||
port_pattern = r'\| [A-Za-z ]+ \| \d+ \| [A-Z]+ \|'
|
||||
port_entries = re.findall(port_pattern, content)
|
||||
|
||||
port_count = len(port_entries)
|
||||
if port_count < 3:
|
||||
self.warnings.append(f"{file_name}: Only {port_count} port mappings found (3+ recommended)")
|
||||
|
||||
def check_troubleshooting_depth(self, content, file_name):
|
||||
"""Verify troubleshooting section is comprehensive"""
|
||||
# Look for troubleshooting subsections
|
||||
troubleshooting_match = re.search(r'## Troubleshooting.*?(?=## |\Z)', content, re.DOTALL)
|
||||
|
||||
if troubleshooting_match:
|
||||
troubleshooting_content = troubleshooting_match.group(0)
|
||||
|
||||
# Count subsections (### headers)
|
||||
subsection_pattern = r'###'
|
||||
subsections = re.findall(subsection_pattern, troubleshooting_content)
|
||||
subsection_count = len(subsections)
|
||||
|
||||
if subsection_count < 5:
|
||||
self.warnings.append(f"{file_name}: Troubleshooting has only {subsection_count} subsections (5+ recommended for deep coverage)")
|
||||
|
||||
# Check for common troubleshooting topics
|
||||
required_topics = ['startup', 'connection', 'performance', 'save', 'network']
|
||||
content_lower = troubleshooting_content.lower()
|
||||
missing_topics = [topic for topic in required_topics if topic not in content_lower]
|
||||
|
||||
if missing_topics:
|
||||
self.warnings.append(f"{file_name}: Troubleshooting missing topics: {', '.join(missing_topics)}")
|
||||
else:
|
||||
self.errors.append(f"{file_name}: No troubleshooting section found")
|
||||
|
||||
def validate_markdown_file(self, md_file):
|
||||
"""Validate a single Markdown file"""
|
||||
try:
|
||||
with open(md_file, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
file_name = md_file.name
|
||||
|
||||
# Run all validation checks
|
||||
self.check_forbidden_terms(content, file_name)
|
||||
self.check_html_entities(content, file_name)
|
||||
self.check_placeholder_handling(content, file_name)
|
||||
self.check_required_sections(content, file_name)
|
||||
self.check_startup_parameters(content, file_name)
|
||||
self.check_port_mappings(content, file_name)
|
||||
self.check_troubleshooting_depth(content, file_name)
|
||||
|
||||
except Exception as e:
|
||||
self.errors.append(f"{md_file.name}: Error reading file: {e}")
|
||||
|
||||
def validate_pdf_file(self, pdf_file):
|
||||
"""Basic validation of PDF file"""
|
||||
try:
|
||||
size = pdf_file.stat().st_size
|
||||
|
||||
# Check file size is reasonable (not empty, not too large)
|
||||
if size < 1000: # Less than 1KB
|
||||
self.errors.append(f"{pdf_file.name}: PDF file too small ({size} bytes)")
|
||||
elif size > 50 * 1024 * 1024: # More than 50MB
|
||||
self.warnings.append(f"{pdf_file.name}: PDF file very large ({size // 1024 // 1024} MB)")
|
||||
else:
|
||||
# File size is reasonable
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
self.errors.append(f"{pdf_file.name}: Error checking PDF: {e}")
|
||||
|
||||
def validate_all_guides(self):
|
||||
"""Validate all generated guides"""
|
||||
print("=== Quality Control Validation ===\n")
|
||||
|
||||
if not self.md_dir.exists():
|
||||
self.errors.append(f"Markdown directory does not exist: {self.md_dir}")
|
||||
return False
|
||||
|
||||
if not self.pdf_dir.exists():
|
||||
self.errors.append(f"PDF directory does not exist: {self.pdf_dir}")
|
||||
return False
|
||||
|
||||
# Get all markdown files
|
||||
md_files = list(self.md_dir.glob("*.md"))
|
||||
pdf_files = list(self.pdf_dir.glob("*.pdf"))
|
||||
|
||||
if not md_files:
|
||||
self.errors.append("No Markdown files found for validation")
|
||||
return False
|
||||
|
||||
print(f"Validating {len(md_files)} Markdown files...")
|
||||
|
||||
# Validate each markdown file
|
||||
for md_file in md_files:
|
||||
print(f" Checking: {md_file.name}")
|
||||
self.validate_markdown_file(md_file)
|
||||
|
||||
# Check if corresponding PDF exists
|
||||
pdf_name = md_file.stem + ".pdf"
|
||||
pdf_file = self.pdf_dir / pdf_name
|
||||
|
||||
if pdf_file.exists():
|
||||
self.validate_pdf_file(pdf_file)
|
||||
else:
|
||||
self.errors.append(f"Missing PDF for {md_file.name}: {pdf_name}")
|
||||
|
||||
print(f"\nValidation completed.")
|
||||
return self.print_results()
|
||||
|
||||
def print_results(self):
|
||||
"""Print validation results"""
|
||||
print("\n" + "=" * 50)
|
||||
print("VALIDATION RESULTS")
|
||||
print("=" * 50)
|
||||
|
||||
total_issues = len(self.errors) + len(self.warnings)
|
||||
|
||||
if self.errors:
|
||||
print(f"\n❌ ERRORS ({len(self.errors)}):")
|
||||
for error in self.errors:
|
||||
print(f" • {error}")
|
||||
|
||||
if self.warnings:
|
||||
print(f"\n⚠️ WARNINGS ({len(self.warnings)}):")
|
||||
for warning in self.warnings:
|
||||
print(f" • {warning}")
|
||||
|
||||
if not self.errors and not self.warnings:
|
||||
print("✅ All validations passed! No issues found.")
|
||||
|
||||
print(f"\nSummary: {len(self.errors)} errors, {len(self.warnings)} warnings")
|
||||
|
||||
# Return True if no errors (warnings are acceptable)
|
||||
return len(self.errors) == 0
|
||||
|
||||
def generate_report(self):
|
||||
"""Generate a JSON report of validation results"""
|
||||
report = {
|
||||
"timestamp": Path().cwd().name,
|
||||
"total_errors": len(self.errors),
|
||||
"total_warnings": len(self.warnings),
|
||||
"errors": self.errors,
|
||||
"warnings": self.warnings,
|
||||
"validation_passed": len(self.errors) == 0
|
||||
}
|
||||
|
||||
report_file = self.pdf_dir / "quality_report.json"
|
||||
with open(report_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(report, f, indent=2)
|
||||
|
||||
print(f"Quality report saved: {report_file}")
|
||||
return report
|
||||
|
||||
if __name__ == "__main__":
|
||||
qc = GuideQualityControl()
|
||||
success = qc.validate_all_guides()
|
||||
qc.generate_report()
|
||||
|
||||
sys.exit(0 if success else 1)
|
||||
Loading…
Add table
Add a link
Reference in a new issue