Skip to content

Commit f8ad542

Browse files
committed
Phase 2: AF3 execution, job monitoring, Mol* viewer, progress notes
- Added run_af3.py: sbatch script generation for gpu-micro.q - Added check_job_status.py: monitor SLURM jobs via sacct - Added check_completion.yml: cron workflow (every 15min) - Added end_competition.yml: manual trigger to publish all results - Added end_competition.py: generate public gallery - Added setup_runner.sh: self-hosted runner installation - Added templates/viewer.html: Mol* with pLDDT coloring - Added PROGRESS.md: session notes + GitHub Classroom plan - Updated prepare_af3_input.py: correct AF3 format (dialect, version, id) - Updated process_submission.yml: save status.json with job tracking
1 parent 03ba8c6 commit f8ad542

File tree

10 files changed

+1587
-10
lines changed

10 files changed

+1587
-10
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
name: Check Job Completion
2+
3+
on:
4+
# Run every 15 minutes
5+
schedule:
6+
- cron: '*/15 * * * *'
7+
# Allow manual trigger
8+
workflow_dispatch:
9+
10+
jobs:
11+
check-jobs:
12+
runs-on: [self-hosted, af3-runner]
13+
14+
permissions:
15+
issues: write
16+
contents: write
17+
18+
steps:
19+
- name: Checkout repository
20+
uses: actions/checkout@v4
21+
22+
- name: Find processing submissions
23+
id: find
24+
run: |
25+
# Find all submissions with processing status
26+
PROCESSING_DIRS=""
27+
for dir in results/submission_*; do
28+
if [ -d "$dir" ]; then
29+
STATUS_FILE="$dir/status.json"
30+
if [ -f "$STATUS_FILE" ]; then
31+
STATUS=$(python3 -c "import json; print(json.load(open('$STATUS_FILE')).get('status', 'unknown'))" 2>/dev/null || echo "unknown")
32+
if [ "$STATUS" = "processing" ] || [ "$STATUS" = "submitted" ]; then
33+
PROCESSING_DIRS="$PROCESSING_DIRS $dir"
34+
fi
35+
fi
36+
fi
37+
done
38+
echo "dirs=$PROCESSING_DIRS" >> $GITHUB_OUTPUT
39+
40+
- name: Check each job
41+
if: steps.find.outputs.dirs != ''
42+
env:
43+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
44+
RESULTS_URL: ${{ vars.RESULTS_URL || 'https://YOUR_GITHUB_PAGES_URL/results' }}
45+
run: |
46+
for dir in ${{ steps.find.outputs.dirs }}; do
47+
echo "Checking $dir..."
48+
49+
# Read status file
50+
STATUS_FILE="$dir/status.json"
51+
if [ ! -f "$STATUS_FILE" ]; then
52+
continue
53+
fi
54+
55+
JOB_ID=$(python3 -c "import json; print(json.load(open('$STATUS_FILE')).get('slurm_job_id', ''))" 2>/dev/null)
56+
ISSUE_NUM=$(python3 -c "import json; print(json.load(open('$STATUS_FILE')).get('issue_number', ''))" 2>/dev/null)
57+
58+
if [ -z "$JOB_ID" ] || [ -z "$ISSUE_NUM" ]; then
59+
echo "Missing job_id or issue_number in $STATUS_FILE"
60+
continue
61+
fi
62+
63+
# Check job status
64+
python3 scripts/check_job_status.py \
65+
--submission-dir "$dir" \
66+
--job-id "$JOB_ID" \
67+
--results-dir "public_results" \
68+
--output-format json > job_result.json 2>&1 || true
69+
70+
COMPLETED=$(python3 -c "import json; print(json.load(open('job_result.json')).get('completed', False))")
71+
SUCCESS=$(python3 -c "import json; print(json.load(open('job_result.json')).get('success', False))")
72+
TOKEN=$(python3 -c "import json; print(json.load(open('job_result.json')).get('result_token', '') or '')")
73+
74+
if [ "$COMPLETED" = "True" ]; then
75+
if [ "$SUCCESS" = "True" ] && [ -n "$TOKEN" ]; then
76+
# Success - notify participant with private link
77+
VIEWER_URL="${RESULTS_URL}/../viewer.html?token=${TOKEN}"
78+
79+
gh issue comment "$ISSUE_NUM" --body "## Results Ready
80+
81+
Your AlphaFold3 prediction has completed successfully.
82+
83+
**View your structure:** [Open in Mol* Viewer](${VIEWER_URL})
84+
85+
**Download files:**
86+
- [Structure (CIF)](${RESULTS_URL}/${TOKEN}/)
87+
88+
> Note: This link is unique to your submission. Please save it for your records.
89+
90+
Results will remain private until the competition ends."
91+
92+
gh issue edit "$ISSUE_NUM" --remove-label "processing" --add-label "completed"
93+
94+
else
95+
# Failed
96+
ERROR=$(python3 -c "import json; print(json.load(open('job_result.json')).get('error', 'Unknown error'))")
97+
98+
gh issue comment "$ISSUE_NUM" --body "## Prediction Failed
99+
100+
Unfortunately, your AlphaFold3 prediction encountered an error:
101+
102+
\`\`\`
103+
${ERROR}
104+
\`\`\`
105+
106+
Please check your sequence and try submitting again. Common issues:
107+
- Sequence too long (>5000 residues)
108+
- Invalid amino acid characters
109+
- Server resource limitations
110+
111+
If the problem persists, please contact the organizers."
112+
113+
gh issue edit "$ISSUE_NUM" --remove-label "processing" --add-label "failed"
114+
fi
115+
fi
116+
117+
rm -f job_result.json
118+
done
119+
120+
- name: Commit status updates
121+
run: |
122+
git config user.name "github-actions[bot]"
123+
git config user.email "github-actions[bot]@users.noreply.github.com"
124+
git add -A results/ public_results/ || true
125+
git diff --staged --quiet || git commit -m "Update job statuses [skip ci]"
126+
git push || true
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: End Competition
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
confirm:
7+
description: 'Type "END" to confirm ending the competition'
8+
required: true
9+
type: string
10+
11+
jobs:
12+
end-competition:
13+
if: github.event.inputs.confirm == 'END'
14+
runs-on: [self-hosted, af3-runner]
15+
16+
permissions:
17+
contents: write
18+
pages: write
19+
issues: write
20+
21+
steps:
22+
- name: Checkout repository
23+
uses: actions/checkout@v4
24+
25+
- name: Publish all results
26+
run: |
27+
python3 scripts/end_competition.py \
28+
--results-dir results \
29+
--public-dir docs \
30+
--results-url results
31+
32+
- name: Update all issues
33+
env:
34+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
35+
run: |
36+
# Get all issues with 'submission' label
37+
gh issue list --label submission --state all --json number,title --limit 1000 | \
38+
python3 -c "
39+
import json, sys
40+
issues = json.load(sys.stdin)
41+
for issue in issues:
42+
print(issue['number'])
43+
" | while read ISSUE_NUM; do
44+
gh issue comment "$ISSUE_NUM" --body "## Competition Ended
45+
46+
The protein design competition has concluded. All results are now public.
47+
48+
View the final results and all submissions at the competition gallery:
49+
**[View All Results](https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/)**
50+
51+
Thank you for participating!"
52+
53+
gh issue edit "$ISSUE_NUM" --add-label "competition-ended"
54+
done
55+
56+
- name: Commit and push
57+
run: |
58+
git config user.name "github-actions[bot]"
59+
git config user.email "github-actions[bot]@users.noreply.github.com"
60+
git add docs/
61+
git commit -m "End competition - publish all results"
62+
git push
63+
64+
- name: Summary
65+
run: |
66+
echo "## Competition Ended" >> $GITHUB_STEP_SUMMARY
67+
echo "" >> $GITHUB_STEP_SUMMARY
68+
echo "All results have been published to GitHub Pages." >> $GITHUB_STEP_SUMMARY
69+
echo "" >> $GITHUB_STEP_SUMMARY
70+
echo "View at: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/" >> $GITHUB_STEP_SUMMARY

.github/workflows/process_submission.yml

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ on:
55
types: [labeled]
66

77
jobs:
8-
validate-and-prepare:
8+
validate-and-run:
99
if: github.event.label.name == 'submission'
1010
runs-on: [self-hosted, af3-runner]
1111

@@ -17,11 +17,6 @@ jobs:
1717
- name: Checkout repository
1818
uses: actions/checkout@v4
1919

20-
- name: Set up Python
21-
uses: actions/setup-python@v5
22-
with:
23-
python-version: '3.11'
24-
2520
- name: Create submission directory
2621
run: |
2722
SUBMISSION_ID="submission_${{ github.event.issue.number }}"
@@ -35,18 +30,41 @@ jobs:
3530
ISSUE_BODY: ${{ github.event.issue.body }}
3631
ISSUE_NUMBER: ${{ github.event.issue.number }}
3732
run: |
38-
python scripts/parse_submission.py \
33+
python3 scripts/parse_submission.py \
3934
--issue-body "$ISSUE_BODY" \
4035
--issue-number "$ISSUE_NUMBER" \
4136
--output-dir "$SUBMISSION_DIR"
4237
4338
- name: Prepare AF3 input
4439
id: prepare
4540
run: |
46-
python scripts/prepare_af3_input.py \
41+
python3 scripts/prepare_af3_input.py \
4742
--submission-dir "$SUBMISSION_DIR" \
4843
--submission-id "$SUBMISSION_ID"
4944
45+
- name: Submit AF3 job to SLURM
46+
id: submit
47+
run: |
48+
python3 scripts/run_af3.py \
49+
--submission-dir "$SUBMISSION_DIR" \
50+
--submission-id "$SUBMISSION_ID" \
51+
--mode full \
52+
--submit 2>&1 | tee submit_output.txt
53+
54+
# Extract job ID from sbatch output
55+
JOB_ID=$(grep -oP 'Submitted batch job \K\d+' submit_output.txt || echo "unknown")
56+
echo "SLURM_JOB_ID=${JOB_ID}" >> $GITHUB_ENV
57+
58+
# Save status file for tracking
59+
cat > "$SUBMISSION_DIR/status.json" << EOF
60+
{
61+
"status": "processing",
62+
"slurm_job_id": "${JOB_ID}",
63+
"issue_number": ${{ github.event.issue.number }},
64+
"submitted_at": "$(date -Iseconds)"
65+
}
66+
EOF
67+
5068
- name: Comment on success
5169
if: success()
5270
uses: actions/github-script@v7
@@ -56,7 +74,13 @@ jobs:
5674
issue_number: context.issue.number,
5775
owner: context.repo.owner,
5876
repo: context.repo.repo,
59-
body: `## Submission Received\n\nYour sequence has been validated and queued for AlphaFold3 processing.\n\n**Submission ID:** \`${{ env.SUBMISSION_ID }}\`\n\nYou will receive your results privately once processing is complete.`
77+
body: `## Submission Received\n\nYour sequence has been validated and submitted to AlphaFold3.\n\n**Submission ID:** \`${{ env.SUBMISSION_ID }}\`\n**SLURM Job ID:** \`${{ env.SLURM_JOB_ID }}\`\n\nYou will receive your results privately once processing is complete.`
78+
})
79+
github.rest.issues.addLabels({
80+
issue_number: context.issue.number,
81+
owner: context.repo.owner,
82+
repo: context.repo.repo,
83+
labels: ['processing']
6084
})
6185
6286
- name: Comment on failure

0 commit comments

Comments
 (0)