mirror of
https://github.com/Zie619/n8n-workflows.git
synced 2025-11-25 03:15:25 +08:00
## 🌐 GitHub Pages Public Search Interface - Complete client-side search application solving Issue #84 - Responsive HTML/CSS/JavaScript with mobile optimization - Real-time search across 2,057+ workflows with instant results - Category filtering across 15 workflow categories - Dark/light theme support with system preference detection - Direct workflow JSON download functionality ## 🤖 GitHub Actions Automation - deploy-pages.yml: Automated deployment to GitHub Pages - update-readme.yml: Weekly automated README statistics updates - Comprehensive workflow indexing and category generation ## 🔍 Enhanced Search & Categorization - Static search index generation for GitHub Pages - Developer-chosen category prioritization system - CalcsLive custom node integration and categorization - Enhanced workflow database with better custom node detection - Fixed README corruption with live database statistics ## 📚 Documentation & Infrastructure - Comprehensive CHANGELOG.md with proper versioning - Enhanced README with accurate statistics and public interface links - Professional documentation solving repository infrastructure needs ## Technical Improvements - Fixed Unicode encoding issues in Python scripts - Enhanced CalcsLive detection with false positive prevention - Improved JSON description preservation and indexing - Mobile-optimized responsive design for all devices 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
213 lines
7.9 KiB
Python
213 lines
7.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Update README.md with current workflow statistics
|
|
Replaces hardcoded numbers with live data from the database.
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
|
|
# Add the parent directory to path for imports
|
|
sys.path.append(str(Path(__file__).parent.parent))
|
|
|
|
from workflow_db import WorkflowDatabase
|
|
|
|
|
|
def get_current_stats():
|
|
"""Get current workflow statistics from the database."""
|
|
db_path = "database/workflows.db"
|
|
|
|
if not os.path.exists(db_path):
|
|
print("Database not found. Run workflow indexing first.")
|
|
return None
|
|
|
|
db = WorkflowDatabase(db_path)
|
|
stats = db.get_stats()
|
|
|
|
# Get categories count
|
|
categories = db.get_service_categories()
|
|
|
|
return {
|
|
'total_workflows': stats['total'],
|
|
'active_workflows': stats['active'],
|
|
'inactive_workflows': stats['inactive'],
|
|
'total_nodes': stats['total_nodes'],
|
|
'unique_integrations': stats['unique_integrations'],
|
|
'categories_count': len(get_category_list(categories)),
|
|
'triggers': stats['triggers'],
|
|
'complexity': stats['complexity'],
|
|
'last_updated': datetime.now().strftime('%Y-%m-%d')
|
|
}
|
|
|
|
|
|
def get_category_list(categories):
|
|
"""Get formatted list of all categories (same logic as search index)."""
|
|
formatted_categories = set()
|
|
|
|
# Map technical categories to display names
|
|
category_mapping = {
|
|
'messaging': 'Communication & Messaging',
|
|
'email': 'Communication & Messaging',
|
|
'cloud_storage': 'Cloud Storage & File Management',
|
|
'database': 'Data Processing & Analysis',
|
|
'project_management': 'Project Management',
|
|
'ai_ml': 'AI Agent Development',
|
|
'social_media': 'Social Media Management',
|
|
'ecommerce': 'E-commerce & Retail',
|
|
'analytics': 'Data Processing & Analysis',
|
|
'calendar_tasks': 'Project Management',
|
|
'forms': 'Data Processing & Analysis',
|
|
'development': 'Technical Infrastructure & DevOps'
|
|
}
|
|
|
|
for category_key in categories.keys():
|
|
display_name = category_mapping.get(category_key, category_key.replace('_', ' ').title())
|
|
formatted_categories.add(display_name)
|
|
|
|
# Add categories from the create_categories.py system
|
|
additional_categories = [
|
|
"Business Process Automation",
|
|
"Web Scraping & Data Extraction",
|
|
"Marketing & Advertising Automation",
|
|
"Creative Content & Video Automation",
|
|
"Creative Design Automation",
|
|
"CRM & Sales",
|
|
"Financial & Accounting"
|
|
]
|
|
|
|
for cat in additional_categories:
|
|
formatted_categories.add(cat)
|
|
|
|
return sorted(list(formatted_categories))
|
|
|
|
|
|
def update_readme_stats(stats):
|
|
"""Update README.md with current statistics."""
|
|
readme_path = "README.md"
|
|
|
|
if not os.path.exists(readme_path):
|
|
print("README.md not found")
|
|
return False
|
|
|
|
with open(readme_path, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
|
|
# Define replacement patterns and their new values
|
|
replacements = [
|
|
# Main collection description
|
|
(r'A professionally organized collection of \*\*[\d,]+\s+n8n workflows\*\*',
|
|
f'A professionally organized collection of **{stats["total_workflows"]:,} n8n workflows**'),
|
|
|
|
# Total workflows in various contexts
|
|
(r'- \*\*[\d,]+\s+workflows\*\* with meaningful',
|
|
f'- **{stats["total_workflows"]:,} workflows** with meaningful'),
|
|
|
|
# Statistics section
|
|
(r'- \*\*Total Workflows\*\*: [\d,]+',
|
|
f'- **Total Workflows**: {stats["total_workflows"]:,}'),
|
|
|
|
(r'- \*\*Active Workflows\*\*: [\d,]+ \([\d.]+%',
|
|
f'- **Active Workflows**: {stats["active_workflows"]:,} ({(stats["active_workflows"]/stats["total_workflows"]*100):.1f}%'),
|
|
|
|
(r'- \*\*Total Nodes\*\*: [\d,]+ \(avg [\d.]+ nodes',
|
|
f'- **Total Nodes**: {stats["total_nodes"]:,} (avg {(stats["total_nodes"]/stats["total_workflows"]):.1f} nodes'),
|
|
|
|
(r'- \*\*Unique Integrations\*\*: [\d,]+ different',
|
|
f'- **Unique Integrations**: {stats["unique_integrations"]:,} different'),
|
|
|
|
# Update complexity/trigger distribution
|
|
(r'- \*\*Complex\*\*: [\d,]+ workflows \([\d.]+%\)',
|
|
f'- **Complex**: {stats["triggers"].get("Complex", 0):,} workflows ({(stats["triggers"].get("Complex", 0)/stats["total_workflows"]*100):.1f}%)'),
|
|
|
|
(r'- \*\*Webhook\*\*: [\d,]+ workflows \([\d.]+%\)',
|
|
f'- **Webhook**: {stats["triggers"].get("Webhook", 0):,} workflows ({(stats["triggers"].get("Webhook", 0)/stats["total_workflows"]*100):.1f}%)'),
|
|
|
|
(r'- \*\*Manual\*\*: [\d,]+ workflows \([\d.]+%\)',
|
|
f'- **Manual**: {stats["triggers"].get("Manual", 0):,} workflows ({(stats["triggers"].get("Manual", 0)/stats["total_workflows"]*100):.1f}%)'),
|
|
|
|
(r'- \*\*Scheduled\*\*: [\d,]+ workflows \([\d.]+%\)',
|
|
f'- **Scheduled**: {stats["triggers"].get("Scheduled", 0):,} workflows ({(stats["triggers"].get("Scheduled", 0)/stats["total_workflows"]*100):.1f}%)'),
|
|
|
|
# Update total in current collection stats
|
|
(r'\*\*Total Workflows\*\*: [\d,]+ automation',
|
|
f'**Total Workflows**: {stats["total_workflows"]:,} automation'),
|
|
|
|
(r'\*\*Active Workflows\*\*: [\d,]+ \([\d.]+% active',
|
|
f'**Active Workflows**: {stats["active_workflows"]:,} ({(stats["active_workflows"]/stats["total_workflows"]*100):.1f}% active'),
|
|
|
|
(r'\*\*Total Nodes\*\*: [\d,]+ \(avg [\d.]+ nodes',
|
|
f'**Total Nodes**: {stats["total_nodes"]:,} (avg {(stats["total_nodes"]/stats["total_workflows"]):.1f} nodes'),
|
|
|
|
(r'\*\*Unique Integrations\*\*: [\d,]+ different',
|
|
f'**Unique Integrations**: {stats["unique_integrations"]:,} different'),
|
|
|
|
# Categories count
|
|
(r'Our system automatically categorizes workflows into [\d]+ service categories',
|
|
f'Our system automatically categorizes workflows into {stats["categories_count"]} service categories'),
|
|
|
|
# Update any "2000+" references
|
|
(r'2000\+', f'{stats["total_workflows"]:,}+'),
|
|
(r'2,000\+', f'{stats["total_workflows"]:,}+'),
|
|
|
|
# Search across X workflows
|
|
(r'Search across [\d,]+ workflows', f'Search across {stats["total_workflows"]:,} workflows'),
|
|
|
|
# Instant search across X workflows
|
|
(r'Instant search across [\d,]+ workflows', f'Instant search across {stats["total_workflows"]:,} workflows'),
|
|
]
|
|
|
|
# Apply all replacements
|
|
updated_content = content
|
|
replacements_made = 0
|
|
|
|
for pattern, replacement in replacements:
|
|
old_content = updated_content
|
|
updated_content = re.sub(pattern, replacement, updated_content)
|
|
if updated_content != old_content:
|
|
replacements_made += 1
|
|
|
|
# Write back to file
|
|
with open(readme_path, 'w', encoding='utf-8') as f:
|
|
f.write(updated_content)
|
|
|
|
print(f"README.md updated with current statistics:")
|
|
print(f" - Total workflows: {stats['total_workflows']:,}")
|
|
print(f" - Active workflows: {stats['active_workflows']:,}")
|
|
print(f" - Total nodes: {stats['total_nodes']:,}")
|
|
print(f" - Unique integrations: {stats['unique_integrations']:,}")
|
|
print(f" - Categories: {stats['categories_count']}")
|
|
print(f" - Replacements made: {replacements_made}")
|
|
|
|
return True
|
|
|
|
|
|
def main():
|
|
"""Main function to update README statistics."""
|
|
try:
|
|
print("Getting current workflow statistics...")
|
|
stats = get_current_stats()
|
|
|
|
if not stats:
|
|
print("Failed to get statistics")
|
|
sys.exit(1)
|
|
|
|
print("Updating README.md...")
|
|
success = update_readme_stats(stats)
|
|
|
|
if success:
|
|
print("README.md successfully updated with latest statistics!")
|
|
else:
|
|
print("Failed to update README.md")
|
|
sys.exit(1)
|
|
|
|
except Exception as e:
|
|
print(f"Error updating README stats: {e}")
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |