Skip to content

Deploy GitHub Pages #2810

Deploy GitHub Pages

Deploy GitHub Pages #2810

Workflow file for this run

name: Deploy GitHub Pages
on:
push:
branches: [ main, review ]
pull_request:
branches: [ review ]
types: [opened, synchronize, reopened]
schedule:
# Update every hour
- cron: '0 * * * *'
workflow_dispatch:
permissions:
id-token: write
contents: read
pages: write
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install requests python-dateutil
- name: Generate submission data
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python << 'EOF'
import json
import os
import glob
import requests
from pathlib import Path
from datetime import datetime, timezone
def fetch_pr_data(submissions):
"""Fetch PR data from GitHub API and match with submissions to detect track"""
token = os.environ.get('GITHUB_TOKEN')
repo = os.environ.get('GITHUB_REPOSITORY', 'PlotSenseAI/PlotSenseAI-Hackathon-Submissions')
headers = {'Authorization': f'token {token}'} if token else {}
# Create submission ID to track mapping
submission_map = {sub['id']: sub.get('track', 'Unknown') for sub in submissions}
# Fetch open PRs targeting review branch
open_prs_url = f'https://api.github.com/repos/{repo}/pulls?state=open&base=review&per_page=100'
merged_prs_url = f'https://api.github.com/repos/{repo}/pulls?state=closed&base=review&per_page=100'
pr_data = {
'open_prs': [],
'merged_prs': [],
'total_open': 0,
'total_merged': 0,
'ml_track': {'open': 0, 'merged': 0},
'dev_track': {'open': 0, 'merged': 0}
}
def extract_track_from_pr(pr_title, pr_number):
"""Extract track from PR title by matching submission ID or inferring from title"""
import re
# Method 1: Try to find submission ID in format PSH2025-XXX
match = re.search(r'PSH2025-\d{3}', pr_title)
if match:
submission_id = match.group(0)
track = submission_map.get(submission_id)
if track and track != 'Unknown':
return track
# Method 2: Try to fetch PR files to check which folder they're in
try:
files_url = f'https://api.github.com/repos/{repo}/pulls/{pr_number}/files'
files_response = requests.get(files_url, headers=headers, timeout=10)
if files_response.status_code == 200:
files = files_response.json()
for file in files:
filename = file.get('filename', '')
if 'plotsense-2025-ml' in filename:
return 'PlotSense ML'
elif 'plotsense-2025-dev' in filename:
return 'PlotSense Dev'
except Exception as e:
print(f"Could not fetch files for PR #{pr_number}: {e}")
# Method 3: Infer from PR title keywords
title_lower = pr_title.lower()
if 'ml track' in title_lower or 'machine learning' in title_lower or 'plotsense ml' in title_lower:
return 'PlotSense ML'
elif 'dev track' in title_lower or 'development' in title_lower or 'plotsense dev' in title_lower:
return 'PlotSense Dev'
return 'Unknown'
try:
# Get open PRs
response = requests.get(open_prs_url, headers=headers, timeout=10)
if response.status_code == 200:
open_prs = response.json()
for pr in open_prs:
track = extract_track_from_pr(pr['title'], pr['number'])
pr_data['open_prs'].append({
'number': pr['number'],
'title': pr['title'],
'user': pr['user']['login'],
'created_at': pr['created_at'],
'html_url': pr['html_url'],
'state': 'open',
'track': track
})
# Count by track
if track == 'PlotSense ML':
pr_data['ml_track']['open'] += 1
elif track == 'PlotSense Dev':
pr_data['dev_track']['open'] += 1
pr_data['total_open'] = len(pr_data['open_prs'])
# Get merged PRs
response = requests.get(merged_prs_url, headers=headers, timeout=10)
if response.status_code == 200:
closed_prs = response.json()
merged_prs = [pr for pr in closed_prs if pr.get('merged_at')]
for pr in merged_prs:
track = extract_track_from_pr(pr['title'], pr['number'])
pr_data['merged_prs'].append({
'number': pr['number'],
'title': pr['title'],
'user': pr['user']['login'],
'created_at': pr['created_at'],
'merged_at': pr['merged_at'],
'html_url': pr['html_url'],
'state': 'merged',
'track': track
})
# Count by track
if track == 'PlotSense ML':
pr_data['ml_track']['merged'] += 1
elif track == 'PlotSense Dev':
pr_data['dev_track']['merged'] += 1
pr_data['total_merged'] = len(pr_data['merged_prs'])
print(f"Fetched {pr_data['total_open']} open PRs and {pr_data['total_merged']} merged PRs")
print(f"ML Track: {pr_data['ml_track']['open']} open, {pr_data['ml_track']['merged']} merged")
print(f"Dev Track: {pr_data['dev_track']['open']} open, {pr_data['dev_track']['merged']} merged")
except Exception as e:
print(f"Error fetching PR data: {e}")
return pr_data
def collect_submissions():
submissions = []
submission_files = []
for track_dir in ['submissions/plotsense-2025-ml', 'submissions/plotsense-2025-dev']:
if os.path.exists(track_dir):
for file_path in glob.glob(f"{track_dir}/*.json"):
if not file_path.endswith('TEMPLATE.json'):
submission_files.append(file_path)
for file_path in submission_files:
try:
with open(file_path, 'r') as f:
submission = json.load(f)
file_stat = os.stat(file_path)
submission['submitted_date'] = datetime.fromtimestamp(
file_stat.st_mtime, tz=timezone.utc
).isoformat()
submission['file_path'] = file_path
if 'tech_stack' not in submission:
if submission.get('track') == 'PlotSense ML':
submission['tech_stack'] = ['Python', 'PlotSenseAI', 'Pandas', 'Scikit-learn']
else:
submission['tech_stack'] = ['JavaScript', 'PlotSenseAI', 'React', 'Node.js']
submission['status'] = 'pending'
required_fields = ['id', 'track', 'project_name', 'team_name', 'repo_url']
if all(field in submission and submission[field] for field in required_fields):
submission['status'] = 'validated'
submissions.append(submission)
except (json.JSONDecodeError, FileNotFoundError) as e:
print(f"Error processing {file_path}: {e}")
continue
return submissions
def generate_metrics(submissions, pr_data):
# Filter out submissions with Unknown track (used for testing)
submissions = [s for s in submissions if s.get('track') != 'Unknown']
# Filter out PRs with Unknown track from PR data
if pr_data:
pr_data['open_prs'] = [pr for pr in pr_data.get('open_prs', []) if pr.get('track') != 'Unknown']
pr_data['merged_prs'] = [pr for pr in pr_data.get('merged_prs', []) if pr.get('track') != 'Unknown']
pr_data['total_open'] = len(pr_data['open_prs'])
pr_data['total_merged'] = len(pr_data['merged_prs'])
if not submissions and pr_data.get('total_open', 0) == 0 and pr_data.get('total_merged', 0) == 0:
return {
'total_submissions': 0,
'total_teams': 0,
'tracks': {'PlotSense ML': 0, 'PlotSense Dev': 0},
'status_counts': {'validated': 0, 'pending': 0, 'failed': 0},
'tech_stack_usage': {},
'team_sizes': {},
'submission_timeline': [],
'prs': pr_data or {
'open_prs': [],
'merged_prs': [],
'total_open': 0,
'total_merged': 0,
'ml_track': {'open': 0, 'merged': 0},
'dev_track': {'open': 0, 'merged': 0}
}
}
# Calculate total submissions from PRs (open + merged), not JSON files
total_pr_count = pr_data.get('total_open', 0) + pr_data.get('total_merged', 0)
# Count unique teams from PRs
all_prs = pr_data.get('open_prs', []) + pr_data.get('merged_prs', [])
unique_teams = len(set(pr.get('user', '') for pr in all_prs if pr.get('user')))
metrics = {
'total_submissions': total_pr_count,
'total_teams': unique_teams if unique_teams > 0 else len(submissions),
'tracks': {},
'status_counts': {'validated': 0, 'pending': 0, 'failed': 0},
'tech_stack_usage': {},
'team_sizes': {},
'submission_timeline': [],
'prs': pr_data
}
# Count tracks from PRs
for track in ['PlotSense ML', 'PlotSense Dev']:
track_count = len([pr for pr in all_prs if pr.get('track') == track])
metrics['tracks'][track] = track_count
for status in ['validated', 'pending', 'failed']:
metrics['status_counts'][status] = len([s for s in submissions if s.get('status') == status])
for submission in submissions:
if 'tech_stack' in submission:
for tech in submission['tech_stack']:
metrics['tech_stack_usage'][tech] = metrics['tech_stack_usage'].get(tech, 0) + 1
for submission in submissions:
if 'team_members' in submission:
size = len(submission['team_members'])
metrics['team_sizes'][str(size)] = metrics['team_sizes'].get(str(size), 0) + 1
timeline = {}
for submission in submissions:
if 'submitted_date' in submission:
date = submission['submitted_date'][:10]
timeline[date] = timeline.get(date, 0) + 1
metrics['submission_timeline'] = [{'date': date, 'count': count} for date, count in sorted(timeline.items())]
return metrics
# Collect September 2025 submissions first
submissions = collect_submissions()
# Fetch PR data with submission context for track detection
pr_data = fetch_pr_data(submissions)
# Generate metrics with PR data
metrics = generate_metrics(submissions, pr_data)
# Create directory structure for September 2025 hackathon
os.makedirs('docs/data/september-2025', exist_ok=True)
# Save September 2025 data
with open('docs/data/september-2025/submissions.json', 'w') as f:
json.dump(submissions, f, indent=2, default=str)
with open('docs/data/september-2025/metrics.json', 'w') as f:
json.dump(metrics, f, indent=2, default=str)
# Also save to legacy locations for backward compatibility
os.makedirs('docs/data', exist_ok=True)
with open('docs/data/submissions.json', 'w') as f:
json.dump(submissions, f, indent=2, default=str)
with open('docs/data/metrics.json', 'w') as f:
json.dump(metrics, f, indent=2, default=str)
print(f"Processed {len(submissions)} submissions for September 2025")
print(f"Generated metrics: {metrics['total_submissions']} total submissions")
# Future hackathons can be added here with similar logic
# For example:
# future_submissions = collect_future_submissions()
# os.makedirs('docs/data/future-hackathon', exist_ok=True)
# with open('docs/data/future-hackathon/submissions.json', 'w') as f:
# json.dump(future_submissions, f, indent=2, default=str)
EOF
- name: Setup GitHub Pages
if: github.event_name != 'pull_request'
uses: actions/configure-pages@v4
- name: Upload artifacts
if: github.event_name != 'pull_request'
uses: actions/upload-pages-artifact@v3
with:
path: 'docs'
- name: Deploy to GitHub Pages
if: github.event_name != 'pull_request'
id: deployment
uses: actions/deploy-pages@v4