AdvChkSys/scripts/track_progress/feature_markdown.py
Stan44 1b7601120c Completed progress tracking system with multiple features
[feature:track_progress] Implemented main progress tracking system
[feature:git_integration] Added Git history analysis with commit tag parsing
[feature:template_engine] Created flexible template engine for documentation generation
[feature:feature_tracking] Implemented feature status tracking and updates
[feature:changelog_generation] Added automatic changelog generation from commits
[feature:code_stats] Added code statistics and metrics collection
[feature:roadmap_generation] Implemented roadmap generation

[new-feature:launcher_scripts:Created cross-platform launcher scripts for easy execution]
[new-feature:executable_build:Added Nuitka-based executable build system]
[new-feature:project_installation:Created setup script for easy installation to projects]

[status:track_progress_and_feature_statuses] Completed automatic status document generation
[status:git_integration] Completed parsing Git logs for status updates
[status:doc_generation] Completed auto-generation of status docs and changelog

[changelog:Added comprehensive progress tracking system]
[changelog:Added feature status tracking with Git integration]
[changelog:Added customizable template system for documentation]
[changelog:Added automatic changelog generation from commit messages]
[changelog:Added code statistics and metrics collection]
[changelog:Added cross-platform launcher scripts]
[changelog:Added executable build system using Nuitka]
[changelog:Added project installation script]

[milestone:v1.0] Completed initial release of progress tracking system
2025-05-12 18:37:02 -05:00

318 lines
9.3 KiB
Python

"""
Feature markdown utilities for project status documentation.
"""
import os
from typing import Dict, Any, List, Optional
import re
from datetime import datetime
# Mapping of status to emojis
STATUS_EMOJIS = {
"completed": "",
"in_progress": "🔄",
"planned": "📝",
"error": "☣️",
"blocked": "",
"failed": "",
"warning": "⚠️",
"unknown": "",
"done": "",
"testing": "🧪",
"review": "👀",
"design": "🎨",
"research": "🔍",
"deprecated": "🗑️",
"postponed": "",
}
def emoji_status(status: str) -> str:
"""Return emoji for a status."""
return STATUS_EMOJIS.get(status.lower(), STATUS_EMOJIS["unknown"])
def format_feature_md(feature_name: str, data: dict) -> str:
"""Format a single feature into Markdown."""
status = data.get("status", "unknown")
status_emoji = emoji_status(status)
lines = [f"## {feature_name}"]
lines.append(f"- Status: {status_emoji} {status}")
if "description" in data:
lines.append(f"- Description: {data['description']}")
if "date" in data:
lines.append(f"- Last Update: {data['date']}")
if "author" in data:
lines.append(f"- Updated By: {data['author']}")
if "details" in data:
lines.append(f"- Details: {data['details']}")
return "\n".join(lines)
def build_feature_md(features: dict, title: str = "# Feature Tracking") -> str:
"""Build the full Markdown document from features dictionary."""
lines = [title, ""]
for feature_name, data in features.items():
lines.append(format_feature_md(feature_name, data))
lines.append("") # Add an empty line between features
return "\n".join(lines)
def extract_features(
features_file: str, verbose: bool = False
) -> Dict[str, Dict[str, Any]]:
"""Extract all valid features from the file, focusing on descriptions."""
features = {}
seen_descriptions = set()
if not os.path.exists(features_file):
if verbose:
print(f"File not found: {features_file}")
return features
with open(features_file, "r", encoding="utf-8") as f:
content = f.read()
# First, try to extract features from the specific format
# This format has ### feature_name followed by status, description, date
status_blocks = re.findall(
r"### ([^\n]+)\s+\*\*Status:\*\* ([^\n]+)\s+\*\*Description:\*\* ([^\n]+)\s+\*\*Last Update:\*\* ([^\n]+)",
content,
)
for feature_key, status_line, desc, date in status_blocks:
desc = desc.strip()
date = date.strip()
if desc and len(desc) > 2 and desc not in seen_descriptions:
seen_descriptions.add(desc)
# Extract status without emoji
status_match = re.search(
r"[^a-zA-Z]*([a-zA-Z_]+)", status_line
)
status = (
status_match.group(1).lower()
if status_match
else "planned"
)
# Clean up the feature key
clean_key = re.sub(
r"[^a-zA-Z0-9_]", "", feature_key.replace(" ", "_").lower()
)
if not clean_key or len(clean_key) < 2:
# Generate key from description
words = desc.split()[:3]
clean_key = "_".join(words).lower()
clean_key = re.sub(r"[^a-zA-Z0-9_]", "", clean_key)
features[clean_key] = {
"status": status,
"description": desc,
"date": date,
"author": "",
"details": "",
}
# Also try the format: ### **Status:** status \n **Description:** desc \n **Last Update:** date
alt_blocks = re.findall(
r"### \*\*Status:\*\* ([^\n]+)\s+\*\*Description:\*\* ([^\n]+)\s+\*\*Last Update:\*\* ([^\n]+)",
content,
)
for status_line, desc, date in alt_blocks:
desc = desc.strip()
date = date.strip()
if desc and len(desc) > 2 and desc not in seen_descriptions:
seen_descriptions.add(desc)
# Extract status without emoji
status_match = re.search(
r"[^a-zA-Z]*([a-zA-Z_]+)", status_line
)
status = (
status_match.group(1).lower()
if status_match
else "planned"
)
# Generate key from description
words = desc.split()[:3]
clean_key = "_".join(words).lower()
clean_key = re.sub(r"[^a-zA-Z0-9_]", "", clean_key)
features[clean_key] = {
"status": status,
"description": desc,
"date": date,
"author": "",
"details": "",
}
return features
def enrich_features(
features: Dict[str, Dict[str, Any]]
) -> Dict[str, Dict[str, Any]]:
"""Add additional information to features for display purposes."""
enriched = {}
for key, data in features.items():
enriched[key] = data.copy()
status = data.get("status", "unknown")
enriched[key]["status_emoji"] = emoji_status(status)
# Add title-cased key for display
enriched[key]["display_name"] = key.replace("_", " ").title()
return enriched
def generate_features_md(
features: Dict[str, Dict[str, Any]], project_name: str
) -> str:
"""Generate a clean features markdown from the features dictionary."""
lines = [
"# Feature Tracking",
"",
"## Overview",
"",
f"This document tracks the implementation status of all features in the {project_name} project.",
"",
"## Feature Status Summary",
"",
"| Status | Description |",
"|--------|-------------|",
"| ✅ Completed | Features that are fully implemented and tested |",
"| 🔄 In Progress | Features currently being implemented |",
"| 📝 Planned | Features planned for future implementation |",
"",
"## Features",
"",
]
# Add each feature
for feature_key, feature in sorted(features.items()):
status = feature.get("status", "planned").lower()
status_emoji = emoji_status(status)
description = feature.get("description", "")
date = feature.get("date", "") or datetime.now().strftime("%Y-%m-%d")
author = feature.get("author", "")
details = feature.get("details", "")
lines.append(f"### {feature_key}")
lines.append("")
lines.append(f"**Status:** {status_emoji} {status}")
lines.append(f"**Description:** {description}")
lines.append(f"**Last Update:** {date}")
if author:
lines.append(f"**Owner:** {author}")
if details:
lines.append(f"**Details:** {details}")
lines.append("")
# Add categorized lists
lines.append("## Feature Categories")
lines.append("")
# Completed features
lines.append("### Completed Features")
lines.append("")
completed_count = 0
for feature_key, feature in sorted(features.items()):
if feature.get("status", "").lower() in ["completed", "done"]:
lines.append(
f"- **{feature_key}**: {feature.get('description', '')}"
)
completed_count += 1
if completed_count == 0:
lines.append("*No completed features yet.*")
lines.append("")
# In progress features
lines.append("### In Progress Features")
lines.append("")
in_progress_count = 0
for feature_key, feature in sorted(features.items()):
if feature.get("status", "").lower() == "in_progress":
lines.append(
f"- **{feature_key}**: {feature.get('description', '')}"
)
in_progress_count += 1
if in_progress_count == 0:
lines.append("*No features currently in progress.*")
lines.append("")
# Planned features
lines.append("### Planned Features")
lines.append("")
planned_count = 0
for feature_key, feature in sorted(features.items()):
if feature.get("status", "").lower() == "planned":
lines.append(
f"- **{feature_key}**: {feature.get('description', '')}"
)
planned_count += 1
if planned_count == 0:
lines.append("*No planned features yet.*")
lines.append("")
# Add CSS styling
lines.append(
"""<style>
.feature-card {
margin-bottom: 20px;
padding: 15px;
border-radius: 5px;
border-left: 5px solid #ccc;
background-color: #f8f9fa;
}
.feature-card.completed {
border-left-color: #28a745;
background-color: #f0fff0;
}
.feature-card.in_progress {
border-left-color: #007bff;
background-color: #f0f8ff;
}
.feature-card.planned {
border-left-color: #6c757d;
background-color: #f8f9fa;
}
.feature-header {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
}
.feature-status {
font-weight: bold;
}
.feature-date {
color: #6c757d;
font-size: 0.9em;
}
</style>"""
)
return "\n".join(lines)