forked from NeuromatchAcademy/nmaci
-
Notifications
You must be signed in to change notification settings - Fork 1
Convert nmaci to a pip-installable Python package #7
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
Open
OleBialas
wants to merge
9
commits into
neuromatch:main
Choose a base branch
from
OleBialas:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
e7ca770
restructure
OleBialas a72c81d
use argparse instead of sys.argv
OleBialas b117e84
add cli entrypoint
OleBialas a81d6d3
replace old scripts with shims
OleBialas d9ab00a
update tests
OleBialas 1eb1bf9
update ci
OleBialas e9499d3
fix workflow
OleBialas 64c0dba
fix ci, update readme and contributors
OleBialas 82a8ce5
fix Path | str annotation for Python 3.9 compatibility
OleBialas 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
Some comments aren't visible on the classic Files Changed page.
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
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
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 |
|---|---|---|
| @@ -1,9 +1,45 @@ | ||
| # nmaci | ||
| [](https://github.com/neuromatch/nmaci/actions/workflows/ci.yaml) | ||
| [](https://github.com/OleBialas/nmaci/actions/workflows/ci.yaml) | ||
| <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section --> | ||
| [](#contributors-) | ||
| [](#contributors-) | ||
|
Comment on lines
+2
to
+4
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lets also remove these changes. We should create a new PR with updated docs that reference the main repo |
||
| <!-- ALL-CONTRIBUTORS-BADGE:END --> | ||
| Automated tools for building and verifying NMA tutorial materials | ||
| Automated tools for building and verifying NMA tutorial materials. | ||
|
|
||
| ## Installation | ||
|
|
||
| ```bash | ||
| pip install git+https://github.com/OleBialas/nmaci@main | ||
| ``` | ||
|
|
||
| ## Usage | ||
|
|
||
| ``` | ||
| nmaci <command> [args] | ||
| ``` | ||
|
|
||
| | Command | Description | | ||
| |---|---| | ||
| | `process-notebooks` | Execute notebooks, extract solutions, create student/instructor versions | | ||
| | `verify-exercises` | Check exercise cells match solution cells | | ||
| | `lint-tutorial` | Run flake8/pyflakes over notebook code cells | | ||
| | `generate-readmes` | Auto-generate tutorial `README.md` files | | ||
| | `generate-book` | Build Jupyter Book from `materials.yml` | | ||
| | `generate-book-dl` | Build Jupyter Book (Deep Learning variant) | | ||
| | `generate-book-precourse` | Build Jupyter Book (Precourse variant) | | ||
| | `select-notebooks` | Filter which notebooks to process | | ||
| | `make-pr-comment` | Generate PR comment with Colab badges and lint report | | ||
| | `find-unreferenced` | Identify unused solution images/scripts | | ||
| | `extract-links` | Extract video/slide links from notebooks | | ||
| | `parse-html` | Check HTML build output for errors | | ||
|
|
||
| ## Development | ||
|
|
||
| ```bash | ||
| git clone https://github.com/OleBialas/nmaci | ||
| cd nmaci | ||
| uv sync --extra dev | ||
| uv run pytest tests/ | ||
| ``` | ||
|
|
||
| ## Contributors ✨ | ||
|
|
||
|
|
@@ -16,6 +52,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d | |
| <tbody> | ||
| <tr> | ||
| <td align="center" valign="top" width="14.28%"><a href="https://github.com/iamzoltan"><img src="https://avatars.githubusercontent.com/u/21369773?v=4?s=100" width="100px;" alt="Zoltan"/><br /><sub><b>Zoltan</b></sub></a><br /><a href="https://github.com/neuromatch/nmaci/commits?author=iamzoltan" title="Code">💻</a> <a href="https://github.com/neuromatch/nmaci/commits?author=iamzoltan" title="Tests">⚠️</a> <a href="#maintenance-iamzoltan" title="Maintenance">🚧</a></td> | ||
| <td align="center" valign="top" width="14.28%"><a href="https://github.com/OleBialas"><img src="https://avatars.githubusercontent.com/u/38684453?v=4?s=100" width="100px;" alt="Ole Bialas"/><br /><sub><b>Ole Bialas</b></sub></a><br /><a href="https://github.com/neuromatch/nmaci/commits?author=OleBialas" title="Code">💻</a> <a href="#maintenance-OleBialas" title="Maintenance">🚧</a></td> | ||
| </tr> | ||
| </tbody> | ||
| </table> | ||
|
|
||
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,30 @@ | ||
| [build-system] | ||
| requires = ["hatchling"] | ||
| build-backend = "hatchling.build" | ||
|
|
||
| [project] | ||
| name = "nmaci" | ||
| version = "0.1.0" | ||
| requires-python = ">=3.9" | ||
| dependencies = [ | ||
| "nbformat", | ||
| "nbconvert", | ||
| "notebook", | ||
| "pillow", | ||
| "flake8", | ||
| "fuzzywuzzy[speedup]", | ||
| "pyyaml", | ||
| "beautifulsoup4", | ||
| "decorator==5.0.9", | ||
| "Jinja2==3.0.0", | ||
| "jupyter-client", | ||
| ] | ||
|
|
||
| [project.optional-dependencies] | ||
| dev = ["pytest"] | ||
|
|
||
| [project.scripts] | ||
| nmaci = "nmaci.cli:main" | ||
|
|
||
| [tool.hatch.build.targets.wheel] | ||
| packages = ["src/nmaci"] |
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 |
|---|---|---|
| @@ -1,138 +1,4 @@ | ||
| """ | ||
| Neuromatch Academy | ||
|
|
||
| Extract slide and video links from notebooks | ||
| """ | ||
| import argparse | ||
| import ast | ||
| import collections | ||
| import json | ||
| import os | ||
| from urllib.request import urlopen, Request | ||
| from urllib.error import HTTPError | ||
| from nmaci.extract_links import main | ||
| import sys | ||
|
|
||
| import nbformat | ||
|
|
||
|
|
||
| def bilibili_url(video_id): | ||
| return f"https://www.bilibili.com/video/{video_id}" | ||
|
|
||
|
|
||
| def youtube_url(video_id): | ||
| return f"https://youtube.com/watch?v={video_id}" | ||
|
|
||
|
|
||
| def osf_url(link_id): | ||
| return f"https://osf.io/download/{link_id}" | ||
|
|
||
| def tutorial_order(fname): | ||
| fname = os.path.basename(fname) | ||
| try: | ||
| first, last = fname.split("_") | ||
| except ValueError: | ||
| return (99, 99, fname) | ||
| if first.startswith("Bonus"): | ||
| week, day = 9, 9 | ||
| else: | ||
| try: | ||
| week, day = int(first[1]), int(first[3]) | ||
| except ValueError: | ||
| week, day = 9, 9 | ||
| if last.startswith("Intro"): | ||
| order = 0 | ||
| elif last.startswith("Tutorial"): | ||
| order = int(last[8]) | ||
| elif last.startswith("Outro"): | ||
| order = 10 | ||
| elif last.startswith("DaySummary"): | ||
| order = 20 | ||
| else: | ||
| order = 30 | ||
| return (week, day, order) | ||
|
|
||
| def main(arglist): | ||
| """Process IPython notebooks from a list of files.""" | ||
| args = parse_args(arglist) | ||
|
|
||
| nb_paths = [arg for arg in args.files | ||
| if arg.endswith(".ipynb") and | ||
| 'student' not in arg and | ||
| 'instructor' not in arg] | ||
| if not nb_paths: | ||
| print("No notebook files found") | ||
| sys.exit(0) | ||
|
|
||
| videos = collections.defaultdict(list) | ||
| slides = collections.defaultdict(list) | ||
|
|
||
| for nb_path in sorted(nb_paths, key=tutorial_order): | ||
| # Load the notebook structure | ||
| with open(nb_path) as f: | ||
| nb = nbformat.read(f, nbformat.NO_CONVERT) | ||
|
|
||
| # Extract components of the notebook path | ||
| nb_dir, nb_fname = os.path.split(nb_path) | ||
| nb_name, _ = os.path.splitext(nb_fname) | ||
|
|
||
| # Loop through the cells and find video and slide ids | ||
| for cell in nb.get("cells", []): | ||
| for line in cell.get("source", "").split("\n"): | ||
| l = line.strip() | ||
| if l.startswith("video_ids = "): | ||
| rhs = l.split("=")[1].strip() | ||
| video_dict = dict(ast.literal_eval(rhs)) | ||
| try: | ||
| if args.noyoutube: | ||
| url = bilibili_url(video_dict["Bilibili"]) | ||
| else: | ||
| url = youtube_url(video_dict["Youtube"]) | ||
| except KeyError: | ||
| print(f"Malformed video id in {nb_name}? '{rhs}'") | ||
| continue | ||
| if url not in videos[nb_name]: | ||
| videos[nb_name].append(url) | ||
| elif l.startswith("link_id = "): | ||
| rhs = l.split("=")[1].strip() | ||
| url = osf_url(ast.literal_eval(rhs)) | ||
| # Slides are sometimes used in multiple notebooks, so we | ||
| # just store the filename and the link | ||
| if url not in slides: | ||
| api_request = f"https://api.osf.io/v2/files/{ast.literal_eval(rhs)}/" | ||
| httprequest = Request(api_request, | ||
| headers={"Accept": "application/json"}) | ||
| try: | ||
| with urlopen(httprequest) as response: | ||
| data = json.load(response) | ||
| filename = data["data"]["attributes"]["name"] | ||
| except HTTPError as e: | ||
| sys.stderr.write(str(e) + "\n") | ||
| sys.stderr.write(f"Skipping slide {url}\n") | ||
| continue | ||
| if 'DaySummary' in nb_name: | ||
| filename = os.path.splitext(filename.replace("_", ""))[0] + '_DaySummary.pdf' | ||
| slides[url] = filename | ||
|
|
||
| print(json.dumps({"videos": videos, "slides": slides}, indent=4)) | ||
|
|
||
|
|
||
| def parse_args(arglist): | ||
| """Handle the command-line arguments.""" | ||
| parser = argparse.ArgumentParser( | ||
| description="Process neuromatch tutorial notebooks" | ||
| ) | ||
| parser.add_argument( | ||
| "--noyoutube", | ||
| action="store_true", | ||
| help="Extract Bilibili links instead of youtube", | ||
| ) | ||
| parser.add_argument( | ||
| "files", | ||
| nargs="+", | ||
| help="File name(s) to process. Will filter for .ipynb extension.", | ||
| ) | ||
| return parser.parse_args(arglist) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main(sys.argv[1:]) |
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 |
|---|---|---|
| @@ -1,24 +1,4 @@ | ||
| """Print names of derivative files that are no longer used in the notebooks.""" | ||
| from glob import glob | ||
|
|
||
| from nmaci.find_unreferenced_content import main | ||
| import sys | ||
| if __name__ == "__main__": | ||
|
|
||
| day_paths = glob("tutorials/W?D?_*") | ||
| for day_path in sorted(day_paths): | ||
|
|
||
| # Read all of the text for this day's student notebooks into one string | ||
| student_notebooks = glob(f"{day_path}/student/*.ipynb") | ||
| notebook_text = "" | ||
| for nb_path in student_notebooks: | ||
| with open(nb_path) as f: | ||
| notebook_text += f.read() | ||
|
|
||
| # Find solution images and scripts | ||
| solution_pattern = "W?D?_*_Solution*" | ||
| static_paths = glob(f"{day_path}/static/{solution_pattern}") | ||
| script_paths = glob(f"{day_path}/solutions/{solution_pattern}") | ||
|
|
||
| # Print paths that are not referenced in the notebooks | ||
| for path in sorted(static_paths + script_paths): | ||
| if path not in notebook_text: | ||
| print(path) | ||
| main(sys.argv[1:]) |
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lets update this to 3.10