feat: Complete Issue #65 Template Engine Foundation + Fix CLI Regression

## Issue #65 - Template Engine Foundation (COMPLETED)
- Implement complete TDD8 methodology with 30 comprehensive tests (100% passing)
- Add template variable parser with Unicode and dot notation support
- Add template rendering engine with strict/lenient modes
- Add business document generation (invoices, reports)
- Add CLI integration with `markitect template-render` command
- Add performance optimization (1000+ variables in <0.1s)

## Critical CLI Regression Fix
- Fix broken `markitect --help` due to import path issues in markitect/issues/base.py
- Add proper path resolution for domain module accessibility
- Add 12 comprehensive CLI integration tests to prevent future regressions
- Restore full CLI functionality with 35+ working commands

## Template Engine Architecture
- markitect/template/parser.py - Variable parsing with comprehensive validation
- markitect/template/engine.py - Template rendering with business logic
- markitect/template/__init__.py - Structured package exports
- Comprehensive exception hierarchy for robust error handling

## Test Coverage Excellence
- 30 Issue #65 tests: parser (9), substitution (14), integration (7)
- 12 CLI integration tests for regression prevention
- Business scenario validation with real invoice/report generation
- Performance benchmarking and error handling validation

## CLI Professional Enhancement
- Add template-render command with comprehensive options
- Fix import path issues preventing CLI access
- Add validation, data checking, output options
- Support JSON/YAML data formats with auto-detection

## Business Impact
- Transform MarkiTect from document analysis to business automation platform
- Enable professional invoice and report generation
- Provide robust CLI interface for document workflows
- Establish foundation for Epic #64 advanced template features

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-02 15:33:32 +02:00
parent d0c36befb3
commit bcbe78d04f
12 changed files with 2341 additions and 0 deletions

View File

@@ -3424,5 +3424,116 @@ cli.add_command(tailmatter_stats)
cli.add_command(tailmatter_check)
# Template Rendering Command (Issue #65)
@cli.command(name='template-render')
@click.argument('template_file', type=click.Path(exists=True))
@click.argument('data_file', type=click.Path(exists=True))
@click.option('--output', '-o', type=click.Path(), help='Output file path (default: stdout)')
@click.option('--strict', is_flag=True, default=True, help='Strict mode: fail on missing variables (default: True)')
@click.option('--lenient', is_flag=True, help='Lenient mode: preserve placeholders for missing variables')
@click.option('--validate', is_flag=True, help='Validate template syntax before rendering')
@click.option('--check-data', is_flag=True, help='Check data completeness before rendering')
@click.option('--format', 'data_format', type=click.Choice(['json', 'yaml', 'auto']), default='auto', help='Data file format')
@pass_config
def template_render(config, template_file, data_file, output, strict, lenient, validate, check_data, data_format):
"""
Render a template with data to generate documents.
This command takes a template file containing variables in {{variable}} format
and a data file (JSON or YAML) containing the values to substitute.
Examples:
markitect template-render invoice.md data.json
markitect template-render report.md data.yaml --output report.pdf
markitect template-render template.md data.json --lenient --validate
"""
try:
from .template.engine import TemplateEngine
# Initialize template engine
engine = TemplateEngine()
# Read template file
with open(template_file, 'r', encoding='utf-8') as f:
template_content = f.read()
# Determine data format
if data_format == 'auto':
if data_file.endswith('.json'):
data_format = 'json'
elif data_file.endswith('.yaml') or data_file.endswith('.yml'):
data_format = 'yaml'
else:
data_format = 'json' # Default to JSON
# Read data file
with open(data_file, 'r', encoding='utf-8') as f:
if data_format == 'json':
data = json.load(f)
else: # yaml
data = yaml.safe_load(f)
# Validate template if requested
if validate:
errors = engine.validate_template(template_content)
if errors:
click.echo("Template validation errors:", err=True)
for error in errors:
click.echo(f" - {error}", err=True)
sys.exit(1)
# Check data completeness if requested
if check_data:
completeness = engine.check_data_completeness(template_content, data)
if completeness['missing']:
click.echo("Missing variables in data:", err=True)
for var in completeness['missing']:
click.echo(f" - {var}", err=True)
click.echo(f"Data completeness: {completeness['completeness']:.1%}", err=True)
if strict:
sys.exit(1)
# Determine render mode
render_strict = strict and not lenient
# Render template
try:
result = engine.render(template_content, data, strict=render_strict)
# Output result
if output:
with open(output, 'w', encoding='utf-8') as f:
f.write(result)
click.echo(f"Template rendered successfully to {output}")
else:
click.echo(result)
except Exception as e:
click.echo(f"Rendering failed: {e}", err=True)
sys.exit(1)
except ImportError:
click.echo("Template engine not available. Make sure it's properly installed.", err=True)
sys.exit(1)
except FileNotFoundError as e:
click.echo(f"File not found: {e}", err=True)
sys.exit(1)
except json.JSONDecodeError as e:
click.echo(f"JSON parsing error: {e}", err=True)
sys.exit(1)
except yaml.YAMLError as e:
click.echo(f"YAML parsing error: {e}", err=True)
sys.exit(1)
except Exception as e:
click.echo(f"Unexpected error: {e}", err=True)
if config.get('verbose'):
import traceback
click.echo(traceback.format_exc(), err=True)
sys.exit(1)
# Make cli function available as main entry point
main = cli
if __name__ == '__main__':
main()