Add stage completion certificates and badges system (Priority 3 #2)
Some checks failed
Cross-Node Transaction Testing / transaction-test (push) Has been cancelled
Deploy to Testnet / deploy-testnet (push) Has been cancelled
Multi-Node Stress Testing / stress-test (push) Has been cancelled
Node Failover Simulation / failover-test (push) Has been cancelled

- Created generate_certificates.py:
  - Generates shields.io markdown badges for each completed stage
  - Creates HTML certificates with styled formatting
  - Generates summary certificate for completing all stages
  - Produces Markdown summary with all badges
- Updated master_training_launcher.sh:
  - Added BADGE_DIR and HTML_CERT_DIR variables
  - Enhanced view_certificates() to display badges and HTML cert paths
  - Added option to start HTTP server for viewing HTML certificates
  - Shows badges summary in certificate viewer
- Generated badges and HTML certificates for stages 1-10
This commit is contained in:
aitbc1
2026-05-07 19:25:46 +02:00
parent 2f2c4306ab
commit 2d9a7ec360
2 changed files with 385 additions and 13 deletions

View File

@@ -0,0 +1,331 @@
#!/usr/bin/env python3
"""
Certificate and Badge Generator for AITBC Training
Generates:
1. Markdown badges (shields.io style) for completed stages
2. HTML certificates with proper formatting
3. Summary certificate for completing all stages
"""
import json
import os
import sys
from datetime import datetime, timezone
from pathlib import Path
CERT_DIR = Path(__file__).parent / ".training_state" / "certificates"
BADGE_DIR = Path(__file__).parent / ".training_state" / "badges"
HTML_DIR = Path(__file__).parent / ".training_state" / "html_certificates"
def ensure_dirs():
"""Ensure output directories exist."""
BADGE_DIR.mkdir(parents=True, exist_ok=True)
HTML_DIR.mkdir(parents=True, exist_ok=True)
def load_certificate(stage_num: int) -> dict:
"""Load certificate JSON for a stage."""
cert_file = CERT_DIR / f"stage{stage_num}_certificate.json"
if not cert_file.exists():
return None
with open(cert_file) as f:
return json.load(f)
def generate_markdown_badge(stage_num: int, cert_data: dict) -> str:
"""Generate shields.io markdown badge for a stage."""
badge_label = f"Stage {stage_num}"
badge_message = "Completed"
badge_color = "brightgreen"
# URL encode spaces
badge_url = f"https://img.shields.io/badge/{badge_label.replace(' ', '%20')}-{badge_message.replace(' ', '%20')}-{badge_color}?style=flat-square"
markdown = f"[![Stage {stage_num}]({badge_url})]({CERT_DIR}/stage{stage_num}_certificate.json)"
return markdown
def generate_html_certificate(stage_num: int, cert_data: dict) -> str:
"""Generate HTML certificate for a stage."""
stage_name = cert_data.get("stage_name", f"Stage {stage_num}")
timestamp = cert_data.get("completion_timestamp", datetime.now(timezone.utc).isoformat())
wallet = cert_data.get("wallet_name", "Unknown")
cert_id = cert_data.get("certificate_id", "Unknown")
html = f"""<!DOCTYPE html>
<html>
<head>
<title>Certificate - Stage {stage_num}</title>
<style>
body {{
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}}
.certificate {{
background: white;
padding: 60px;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
max-width: 800px;
text-align: center;
}}
.header {{
font-size: 48px;
font-weight: bold;
color: #667eea;
margin-bottom: 20px;
}}
.subtitle {{
font-size: 24px;
color: #666;
margin-bottom: 40px;
}}
.stage-name {{
font-size: 36px;
font-weight: bold;
color: #333;
margin: 30px 0;
padding: 20px;
background: #f5f5f5;
border-radius: 10px;
}}
.details {{
text-align: left;
margin: 30px 0;
padding: 20px;
background: #f9f9f9;
border-radius: 10px;
}}
.detail-row {{
margin: 10px 0;
font-size: 16px;
}}
.detail-label {{
font-weight: bold;
color: #667eea;
}}
.footer {{
margin-top: 40px;
font-size: 14px;
color: #999;
}}
.badge {{
display: inline-block;
background: #667eea;
color: white;
padding: 5px 15px;
border-radius: 20px;
font-size: 14px;
margin: 5px;
}}
</style>
</head>
<body>
<div class="certificate">
<div class="header">🏆 Certificate of Completion</div>
<div class="subtitle">AITBC Agent Training Program</div>
<div class="stage-name">Stage {stage_num}: {stage_name}</div>
<div class="details">
<div class="detail-row">
<span class="detail-label">Certificate ID:</span> {cert_id}
</div>
<div class="detail-row">
<span class="detail-label">Completed:</span> {timestamp}
</div>
<div class="detail-row">
<span class="detail-label">Wallet:</span> {wallet}
</div>
<div class="detail-row">
<span class="detail-label">Training Program:</span> hermes AITBC Mastery Training
</div>
</div>
<div>
<span class="badge">✓ Stage Completed</span>
<span class="badge">✓ Hands-On Training</span>
<span class="badge">✓ Blockchain Verified</span>
</div>
<div class="footer">
Generated on {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S UTC')}
</div>
</div>
</body>
</html>"""
return html
def generate_summary_certificate(completed_stages: list) -> str:
"""Generate summary HTML certificate for completing all stages."""
stages_html = ""
for stage in sorted(completed_stages):
cert = load_certificate(stage)
stage_name = cert.get("stage_name", f"Stage {stage}") if cert else f"Stage {stage}"
stages_html += f' <div class="stage-badge">Stage {stage}: {stage_name}</div>\n'
html = f"""<!DOCTYPE html>
<html>
<head>
<title>Mastery Certificate - AITBC Training</title>
<style>
body {{
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}}
.certificate {{
background: white;
padding: 60px;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
max-width: 900px;
text-align: center;
}}
.header {{
font-size: 56px;
font-weight: bold;
color: #667eea;
margin-bottom: 20px;
}}
.subtitle {{
font-size: 28px;
color: #666;
margin-bottom: 20px;
}}
.achievement {{
font-size: 24px;
color: #333;
margin: 30px 0;
padding: 20px;
background: linear-gradient(135deg, #f5f5f5 0%, #e8e8e8 100%);
border-radius: 10px;
border: 3px solid #667eea;
}}
.stages {{
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 10px;
margin: 30px 0;
}}
.stage-badge {{
background: #667eea;
color: white;
padding: 10px 20px;
border-radius: 25px;
font-size: 16px;
font-weight: bold;
}}
.footer {{
margin-top: 40px;
font-size: 14px;
color: #999;
}}
</style>
</head>
<body>
<div class="certificate">
<div class="header">🎓 Mastery Certificate</div>
<div class="subtitle">AITBC Agent Training Program</div>
<div class="achievement">
Congratulations! You have completed all {len(completed_stages)} training stages
</div>
<div class="stages">
{stages_html}
</div>
<div class="footer">
Generated on {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S UTC')}<br>
Total Stages Completed: {len(completed_stages)} / 11
</div>
</div>
</body>
</html>"""
return html
def generate_markdown_summary(completed_stages: list) -> str:
"""Generate Markdown summary with all badges."""
markdown = "# AITBC Training Certificates\n\n"
markdown += f"Completed {len(completed_stages)} / 11 stages\n\n"
markdown += "## Badges\n\n"
for stage in sorted(completed_stages):
cert = load_certificate(stage)
if cert:
badge = generate_markdown_badge(stage, cert)
markdown += f"{badge} "
markdown += "\n\n## Stages Completed\n\n"
for stage in sorted(completed_stages):
cert = load_certificate(stage)
if cert:
stage_name = cert.get("stage_name", f"Stage {stage}")
timestamp = cert.get("completion_timestamp", "Unknown")
markdown += f"- **Stage {stage}: {stage_name}** - Completed {timestamp}\n"
return markdown
def main():
"""Main function."""
ensure_dirs()
# Find all completed stages
completed_stages = []
for i in range(11): # Stages 0-10
cert = load_certificate(i)
if cert:
completed_stages.append(i)
# Generate markdown badge
badge_md = generate_markdown_badge(i, cert)
badge_file = BADGE_DIR / f"stage{i}_badge.md"
with open(badge_file, 'w') as f:
f.write(badge_md)
# Generate HTML certificate
html = generate_html_certificate(i, cert)
html_file = HTML_DIR / f"stage{i}_certificate.html"
with open(html_file, 'w') as f:
f.write(html)
print(f"✓ Generated badge and HTML certificate for Stage {i}")
# Generate summary certificate if all stages completed
if len(completed_stages) == 11:
summary_html = generate_summary_certificate(completed_stages)
summary_file = HTML_DIR / "mastery_certificate.html"
with open(summary_file, 'w') as f:
f.write(summary_html)
print(f"✓ Generated Mastery Certificate: {summary_file}")
# Generate Markdown summary
if completed_stages:
md_summary = generate_markdown_summary(completed_stages)
summary_md_file = BADGE_DIR / "training_summary.md"
with open(summary_md_file, 'w') as f:
f.write(md_summary)
print(f"✓ Generated Markdown summary: {summary_md_file}")
print(f"\nTotal completed stages: {len(completed_stages)} / 11")
print(f"Badges directory: {BADGE_DIR}")
print(f"HTML certificates: {HTML_DIR}")
if __name__ == "__main__":
main()

View File

@@ -34,6 +34,8 @@ START_TIME=$(date +%s)
PROGRESS_FILE="$SCRIPT_DIR/.training_progress"
STATE_DIR="$SCRIPT_DIR/.training_state"
CERT_DIR="$STATE_DIR/certificates"
BADGE_DIR="$STATE_DIR/badges"
HTML_CERT_DIR="$STATE_DIR/html_certificates"
# Skill update flag (default: disabled)
ENABLE_SKILL_UPDATE="${ENABLE_SKILL_UPDATE:-false}"
@@ -397,16 +399,16 @@ capture_learnings() {
# View certificates
view_certificates() {
print_header "Stage Completion Certificates"
# Ensure CERT_DIR exists
# Ensure directories exist
if [ ! -d "$CERT_DIR" ]; then
mkdir -p "$CERT_DIR"
fi
# Collect certificate files into array
local cert_files=()
local cert_count=0
if [ -d "$CERT_DIR" ]; then
for cert_file in "$CERT_DIR"/stage*_certificate.json; do
if [ -f "$cert_file" ]; then
@@ -415,42 +417,81 @@ view_certificates() {
fi
done
fi
if [ $cert_count -eq 0 ]; then
print_warning "No certificates found yet"
echo "Complete stages to earn certificates"
return 0
fi
echo -e "${BOLD}📜 Certificates Earned:${NC}"
echo
# Display certificates with index
for i in "${!cert_files[@]}"; do
local cert_file="${cert_files[$i]}"
local stage_num=$(echo "$cert_file" | grep -o 'stage[0-10]' | grep -o '[0-10]')
local stage_num=$(echo "$cert_file" | grep -o 'stage[0-9]*' | grep -o '[0-9]*')
local stage_name=$(get_stage_name $stage_num)
local timestamp=$(python3 -c "import json; print(json.load(open('$cert_file'))['completion_timestamp'])" 2>/dev/null || echo "Unknown")
echo -e " ${GREEN}$(($i+1))${NC}. Stage $stage_num: $stage_name"
echo " Completed: $timestamp"
echo " File: $cert_file"
# Show badge path if exists
local badge_file="$BADGE_DIR/stage${stage_num}_badge.md"
if [ -f "$badge_file" ]; then
echo " Badge: $badge_file"
fi
# Show HTML cert path if exists
local html_file="$HTML_CERT_DIR/stage${stage_num}_certificate.html"
if [ -f "$html_file" ]; then
echo " HTML: $html_file"
fi
echo
done
echo -e "${BOLD}Total certificates: $cert_count${NC}"
# Show badges summary if available
if [ -d "$BADGE_DIR" ] && [ -f "$BADGE_DIR/training_summary.md" ]; then
echo
echo -e "${BOLD}🏅 Badges Summary:${NC}"
cat "$BADGE_DIR/training_summary.md"
fi
echo
echo -n "View certificate details? Enter number [1-$cert_count] or N: "
read -r view_choice
if [[ "$view_choice" =~ ^[0-10]+$ ]] && [ "$view_choice" -ge 1 ] && [ "$view_choice" -le "$cert_count" ]; then
if [[ "$view_choice" =~ ^[0-9]+$ ]] && [ "$view_choice" -ge 1 ] && [ "$view_choice" -le "$cert_count" ]; then
local idx=$(($view_choice - 1))
local cert_file="${cert_files[$idx]}"
if [ -f "$cert_file" ]; then
echo
echo -e "${BOLD}Certificate Details:${NC}"
cat "$cert_file" | python3 -m json.tool 2>/dev/null || cat "$cert_file"
# Offer to open HTML certificate
local stage_num=$(echo "$cert_file" | grep -o 'stage[0-9]*' | grep -o '[0-9]*')
local html_file="$HTML_CERT_DIR/stage${stage_num}_certificate.html"
if [ -f "$html_file" ]; then
echo
echo -n "Open HTML certificate in browser? [y/N]: "
read -r open_choice
if [[ "$open_choice" =~ ^[Yy]$ ]]; then
if command -v xdg-open &> /dev/null; then
xdg-open "$html_file" &> /dev/null &
elif command -v python3 &> /dev/null; then
echo "Starting HTTP server for certificates..."
cd "$HTML_CERT_DIR" && python3 -m http.server 8888 &
echo "View certificates at: http://localhost:8888"
echo "Press Ctrl+C to stop the server when done"
fi
fi
fi
fi
fi
}