Initial upload with CI/CD workflow

This commit is contained in:
2026-01-30 22:12:52 +00:00
parent 591edf97f4
commit 0f6df4f8c4

View File

@@ -0,0 +1,124 @@
"""Markdown export module for CodeSnap."""
from pathlib import Path
from typing import Any, Optional
from ..core.extractor import ExtractedCode
class MarkdownExporter:
"""Exports code summaries in Markdown format."""
def export(
self,
extracted_files: list[ExtractedCode],
file_paths: list[Path],
dependency_data: dict[str, Any],
complexity_data: dict[str, str],
output_path: Optional[Path] = None,
) -> str:
"""Export code summary to Markdown."""
lines: list[str] = []
lines.append("# CodeSnap Analysis Report\n")
lines.append(f"**Analyzed Directory:** {file_paths[0].parent if file_paths else 'N/A'}\n")
lines.append(f"**Files Analyzed:** {len(extracted_files)}\n")
lines.append(f"**Date:** {self._get_timestamp()}\n")
lines.append("\n## Language Breakdown\n")
language_counts: dict[str, int] = {}
for extracted in extracted_files:
language_counts[extracted.language] = language_counts.get(extracted.language, 0) + 1
for lang, count in sorted(language_counts.items(), key=lambda x: x[1], reverse=True):
lines.append(f"- **{lang}:** {count} files\n")
lines.append("\n## File Structure\n")
lines.append("```\n")
lines.append(self._generate_tree(file_paths))
lines.append("```\n")
lines.append("\n## Key Functions and Classes\n")
for extracted in extracted_files:
if extracted.functions or extracted.classes:
lines.append(f"\n### {extracted.file_path.name}\n")
if extracted.classes:
lines.append("**Classes:**\n")
for cls in extracted.classes:
lines.append(f"- `{cls.name}`")
if cls.base_classes:
lines.append(f" (extends: {', '.join(cls.base_classes)})")
lines.append("\n")
if cls.methods:
lines.append(" **Methods:**\n")
for method in cls.methods:
params = ", ".join(method.parameters) if method.parameters else ""
lines.append(f" - `{method.name}({params})`\n")
if extracted.functions:
lines.append("**Functions:**\n")
for func in extracted.functions:
params = ", ".join(func.parameters) if func.parameters else ""
lines.append(f"- `{func.name}({params})`\n")
lines.append("\n## Dependencies\n")
dependencies = dependency_data.get("dependencies", [])
if dependencies:
lines.append("### Import Relationships\n")
for dep in dependencies[:20]:
lines.append(f"- `{dep.source.name}` → `{dep.target.name}`\n")
if len(dependencies) > 20:
lines.append(f"\n... and {len(dependencies) - 20} more dependencies\n")
else:
lines.append("No dependencies detected.\n")
cycles = dependency_data.get("cycles", [])
if cycles:
lines.append("\n### Circular Dependencies\n")
for i, cycle in enumerate(cycles, 1):
cycle_names = "".join(p.name for p in cycle)
lines.append(f"**Cycle {i}:** {cycle_names}\n")
orphaned = dependency_data.get("orphaned", [])
if orphaned:
lines.append("\n### Orphaned Files (no dependencies)\n")
for f in orphaned:
lines.append(f"- {f.name}\n")
lines.append("\n## Complexity Metrics\n")
lines.append("| File | Complexity |\n")
lines.append("|------|------------|\n")
for extracted in extracted_files:
complexity = complexity_data.get(str(extracted.file_path), "unknown")
lines.append(f"| {extracted.file_path.name} | {complexity} |\n")
report = "\n".join(lines)
if output_path:
output_path.write_text(report, encoding="utf-8")
return report
def _generate_tree(self, file_paths: list[Path]) -> str:
"""Generate a tree representation of the file structure."""
if not file_paths:
return ""
root = file_paths[0].parent
lines: list[str] = []
for path in sorted(file_paths):
relative = path.relative_to(root)
parts = list(relative.parts)
indent = " " * (len(parts) - 1)
prefix = "└── " if parts[-1] == relative.parts[-1] else "├── "
lines.append(f"{indent}{prefix}{parts[-1]}\n")
return "".join(lines)
def _get_timestamp(self) -> str:
"""Get current timestamp."""
from datetime import datetime
return datetime.utcnow().strftime("%Y-%m-%d %H:%M UTC")