-
Notifications
You must be signed in to change notification settings - Fork 6
Add buttons for PR creation and material management features #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
4705714
add simple button to create a PR against the main repository to add n…
likeajumprope d8ef493
Add the edit and delete button
likeajumprope db5318d
add github actions
likeajumprope 1e83b6d
create-pr-from-issue.yml update
likeajumprope 24845e6
update
likeajumprope 6a8252c
update
likeajumprope 39d29c8
Add material: test course
github-actions[bot] 7368969
Merge pull request #6 from likeajumprope/auto/new-material-issue-5
likeajumprope 194e7b6
add normalizing step before recreating the json
likeajumprope 54e9376
Add material: test course
github-actions[bot] 78ffe0b
Merge pull request #8 from likeajumprope/auto/new-material-issue-7
likeajumprope 477f0ca
changes for edit
likeajumprope 1c8f170
update layout and edit
likeajumprope d1252a3
Edit material: Andy's brain book (ID: 58)
github-actions[bot] 3d35d89
Merge pull request #11 from likeajumprope/auto/edit-material-issue-9
likeajumprope 45c9a1c
change constant links to point to the upstream repository
likeajumprope 91f2a20
Merge branch 'main' into dev
likeajumprope d5c41e7
Update .github/scripts/process_issue.py
likeajumprope File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| """ | ||
| Processes a GitHub issue and updates reproinventory_data.yaml and | ||
| frontend/public/data/reproinventory_data.json accordingly. | ||
|
|
||
| Reads: | ||
| /tmp/issue_body.txt - full issue body | ||
| /tmp/issue_title.txt - issue title | ||
|
|
||
| Environment variables: | ||
| ISSUE_LABEL - one of: new-material, edit-material, delete-material | ||
| ISSUE_NUMBER - issue number (for logging) | ||
| """ | ||
|
|
||
| import os | ||
| import re | ||
| import json | ||
| import yaml | ||
|
|
||
| YAML_PATH = "model/reproinventory_data.yaml" | ||
| JSON_PATH = "frontend/public/data/reproinventory_data.json" | ||
|
|
||
| ARRAY_FIELDS = [ | ||
| "tag_team", "level", "platform", "keywords", "instruction_medium", | ||
| "delivery", "language", "programming_language", "neuroimaging_software", | ||
| "imaging_modality", "quadrants", "source", "prerequisite", | ||
| ] | ||
|
|
||
| def normalize_entry(entry): | ||
| """Ensure all array fields are lists, not scalars.""" | ||
| for field in ARRAY_FIELDS: | ||
| val = entry.get(field) | ||
| if val is not None and not isinstance(val, list): | ||
| entry[field] = [val] | ||
| return entry | ||
|
|
||
| label = os.environ["ISSUE_LABEL"] | ||
| issue_number = os.environ["ISSUE_NUMBER"] | ||
|
|
||
| with open("/tmp/issue_body.txt", "r", encoding="utf-8") as f: | ||
| issue_body = f.read() | ||
|
|
||
| with open("/tmp/issue_title.txt", "r", encoding="utf-8") as f: | ||
| issue_title = f.read().strip() | ||
|
|
||
| # Load current data | ||
| with open(YAML_PATH, "r", encoding="utf-8") as f: | ||
| data = [normalize_entry(e) for e in (yaml.safe_load(f) or [])] | ||
|
|
||
|
|
||
| def extract_yaml_block(body): | ||
| """Extract the first ```yaml ... ``` block from the issue body.""" | ||
| match = re.search(r"```yaml\s*\n(.*?)\n```", body, re.DOTALL) | ||
| if not match: | ||
| raise ValueError("No YAML block found in issue body.") | ||
| return yaml.safe_load(match.group(1)) | ||
|
|
||
|
|
||
| if label == "new-material": | ||
| entry = normalize_entry(extract_yaml_block(issue_body)) | ||
|
|
||
| # Assign a new numeric ID | ||
| numeric_ids = [e["id"] for e in data if isinstance(e.get("id"), int)] | ||
| entry["id"] = max(numeric_ids, default=0) + 1 | ||
|
|
||
| data.append(entry) | ||
| print(f"Added new entry with ID {entry['id']}: {entry.get('course_name')}") | ||
|
|
||
| elif label == "edit-material": | ||
| entry = normalize_entry(extract_yaml_block(issue_body)) | ||
| entry_id = entry.get("id") | ||
|
|
||
| replaced = False | ||
| for i, e in enumerate(data): | ||
| if str(e.get("id")) == str(entry_id): | ||
| data[i] = entry | ||
| replaced = True | ||
| break | ||
|
|
||
| if not replaced: | ||
| raise ValueError(f"Entry with ID '{entry_id}' not found in data.") | ||
| print(f"Updated entry ID {entry_id}: {entry.get('course_name')}") | ||
|
|
||
| elif label == "delete-material": | ||
| # Extract ID from issue title: "Delete material: Name (ID: 123)" | ||
| match = re.search(r"ID:\s*(\S+?)\)", issue_title) | ||
| if not match: | ||
| raise ValueError(f"Could not extract ID from issue title: {issue_title!r}") | ||
|
|
||
| raw_id = match.group(1) | ||
| try: | ||
| entry_id = int(raw_id) | ||
| except ValueError: | ||
| entry_id = raw_id | ||
|
|
||
| original_len = len(data) | ||
| data = [e for e in data if str(e.get("id")) != str(entry_id)] | ||
|
|
||
| if len(data) == original_len: | ||
| raise ValueError(f"Entry with ID '{entry_id}' not found in data.") | ||
| print(f"Deleted entry with ID {entry_id}") | ||
|
|
||
| else: | ||
| raise ValueError(f"Unknown label: {label!r}") | ||
|
|
||
| # Write updated YAML | ||
| with open(YAML_PATH, "w", encoding="utf-8") as f: | ||
| yaml.dump(data, f, sort_keys=False, default_flow_style=False, allow_unicode=True) | ||
|
|
||
| # Write updated JSON | ||
| with open(JSON_PATH, "w", encoding="utf-8") as f: | ||
| json.dump(data, f, indent=2, ensure_ascii=False) | ||
|
|
||
| print(f"Successfully processed '{label}' for issue #{issue_number}.") | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| name: Create PR from Issue | ||
|
|
||
| on: | ||
| issues: | ||
| types: [labeled] | ||
|
|
||
| jobs: | ||
| create-pr: | ||
| runs-on: ubuntu-latest | ||
| if: contains(fromJSON('["new-material", "edit-material", "delete-material"]'), github.event.label.name) | ||
|
|
||
| permissions: | ||
| contents: write | ||
| pull-requests: write | ||
|
|
||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.11' | ||
|
|
||
| - name: Install dependencies | ||
| run: pip install pyyaml | ||
|
|
||
| - name: Write issue data to files | ||
| env: | ||
| ISSUE_BODY: ${{ github.event.issue.body }} | ||
| ISSUE_TITLE: ${{ github.event.issue.title }} | ||
| run: | | ||
| printf '%s' "$ISSUE_BODY" > /tmp/issue_body.txt | ||
| printf '%s' "$ISSUE_TITLE" > /tmp/issue_title.txt | ||
|
|
||
| - name: Process issue and update files | ||
| env: | ||
| ISSUE_LABEL: ${{ github.event.label.name }} | ||
| ISSUE_NUMBER: ${{ github.event.issue.number }} | ||
| run: python .github/scripts/process_issue.py | ||
|
|
||
| - name: Create branch and open PR | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| ISSUE_NUMBER: ${{ github.event.issue.number }} | ||
| ISSUE_LABEL: ${{ github.event.label.name }} | ||
| ISSUE_TITLE: ${{ github.event.issue.title }} | ||
| run: | | ||
| BRANCH="auto/${ISSUE_LABEL}-issue-${ISSUE_NUMBER}" | ||
| git config user.name "github-actions[bot]" | ||
| git config user.email "github-actions[bot]@users.noreply.github.com" | ||
| git checkout -b "$BRANCH" | ||
| git add model/reproinventory_data.yaml frontend/public/data/reproinventory_data.json | ||
| if git diff --cached --quiet; then | ||
| echo "No changes detected — nothing to commit." | ||
| exit 1 | ||
| fi | ||
| git commit -m "${ISSUE_TITLE}" | ||
| git push origin "$BRANCH" | ||
| PR_BODY=$(printf "Closes #%s\n\nAutomatically generated from issue #%s." "${ISSUE_NUMBER}" "${ISSUE_NUMBER}") | ||
| gh pr create \ | ||
| --title "${ISSUE_TITLE}" \ | ||
| --body "${PR_BODY}" \ | ||
| --base main \ | ||
| --head "${BRANCH}" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| # ReproInventory - Claude Instructions | ||
|
|
||
| ## Project Overview | ||
|
|
||
| ReproInventory is a web application for browsing neuroimaging and reproducibility training materials. It is part of the ReproNim project. Users can search and filter a catalog of training resources by level, platform, format, programming language, neuroimaging software, and more. | ||
|
|
||
| ## Tech Stack | ||
|
|
||
| - **Frontend:** React 19 + TypeScript + Vite | ||
| - **Styling:** Tailwind CSS v4 | ||
| - **UI Components:** shadcn/ui (built on Radix UI primitives) | ||
| - **Icons:** lucide-react | ||
| - **Data:** Static JSON file served from `frontend/public/data/reproinventory_data.json` | ||
| - **Model/Schema:** YAML-based schema in `model/`, with Python scripts to generate JSON | ||
| - **Deploy:** GitHub Pages via GitHub Actions (pushes to `main` auto-deploy) | ||
|
|
||
| ## Directory Structure | ||
|
|
||
| ``` | ||
| frontend/ # Main React app | ||
| src/ | ||
| components/ # Custom components (e.g. Footer, EditMaterialDialog, AddMaterialDialog) | ||
| components/ui/ # shadcn/ui primitives (accordion, badge, button, card, etc.) | ||
| types/ # TypeScript types generated from the YAML schema | ||
| training-materials-browser.tsx # Main browser/filter UI | ||
| App.tsx # Root component | ||
| public/ | ||
| data/ | ||
| reproinventory_data.json # The training materials dataset | ||
|
|
||
| model/ # Schema and data source of truth | ||
| model.yaml # LinkML schema definition | ||
| reproinventory_data.yaml # Raw training data | ||
| reproinventory_schema.yaml # Schema | ||
| generate_reproinventory_data.py # Generates JSON from YAML | ||
| convert_yaml_to_json.py | ||
|
|
||
| SimpleViewer/ # Legacy Python/Flask viewer (archived, do not modify) | ||
| ``` | ||
|
|
||
| ## Development Commands | ||
|
|
||
| All commands run from the `frontend/` directory: | ||
|
|
||
| ```bash | ||
| npm run dev # Start local dev server | ||
| npm run build # TypeScript check + Vite build | ||
| npm run lint # ESLint | ||
| npm run preview # Preview production build locally | ||
| ``` | ||
|
|
||
| ## Data Model | ||
|
|
||
| The data schema is defined in `model/model.yaml` (LinkML). TypeScript types in `frontend/src/types/reproinventory.ts` are generated from this schema. Key fields on `ReproInventoryEntry`: | ||
|
|
||
| - `id`, `course_name`, `url`, `review`, `notes`, `keywords` | ||
| - `level`, `platform`, `course_length`, `instruction_medium`, `delivery` | ||
| - `language`, `programming_language`, `neuroimaging_software`, `imaging_modality` | ||
| - `open_dataset`, `assessment`, `quadrants`, `tag_team` | ||
|
|
||
| Enum values are strict — always use values that match the schema. | ||
|
|
||
| ## Code Conventions | ||
|
|
||
| - Use the types from `frontend/src/types/reproinventory.ts` for all data model types; do not redefine them locally (note: `AddMaterialDialog.tsx` currently duplicates types — prefer importing from the shared types file in new code). | ||
| - Use `@/` path alias for imports (e.g. `@/components/ui/button`). | ||
| - UI primitives live in `frontend/src/components/ui/` — use these rather than raw HTML elements. | ||
| - Custom components go in `frontend/src/components/`. | ||
| - The dataset is fetched at runtime from `/ReproInventory/data/reproinventory_data.json` (the GitHub Pages base path). | ||
|
|
||
| ## Deployment | ||
|
|
||
| - Pushing to `main` triggers GitHub Actions which builds the frontend and deploys to GitHub Pages. | ||
| - The Vite base path is set for GitHub Pages — keep this in mind when referencing public assets. | ||
| - Do not push broken builds to `main`. | ||
|
|
||
| ## What to Avoid | ||
|
|
||
| - Do not modify files in `SimpleViewer/` — it is a legacy viewer and not actively used. | ||
| - Do not change enum values without also updating `model/model.yaml` and regenerating types. | ||
| - Do not hardcode data that belongs in `reproinventory_data.json` or the YAML source. | ||
| - Do not add dependencies without good reason — the stack is intentionally minimal. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.