From ca7cf9a457d9494aae3e2906350b5c90f453db42 Mon Sep 17 00:00:00 2001 From: Joao Rodrigues Date: Tue, 16 Jan 2024 19:56:42 -0500 Subject: [PATCH 1/3] Add nicer website and validation tooling and CI. --- .github/workflows/deploy-page.yml | 29 ++++++ .github/workflows/jekyll-gh-pages.yml | 51 ----------- .github/workflows/validate-url.yml | 19 ++++ .gitignore | 2 + README.md | 25 +++--- docs/_dev_readme.txt | 39 +++++++++ docs/index.md | 83 ++++++++++++++++++ docs/overrides/partials/toc-item.html | 20 +++++ docs/stylesheets/extra.css | 5 ++ mkdocs.yml | 26 ++++++ mkindex.py | 121 ++++++++++++++++++++++++++ requirements.txt | 3 + 12 files changed, 359 insertions(+), 64 deletions(-) create mode 100644 .github/workflows/deploy-page.yml delete mode 100644 .github/workflows/jekyll-gh-pages.yml create mode 100644 .github/workflows/validate-url.yml create mode 100644 .gitignore create mode 100644 docs/_dev_readme.txt create mode 100644 docs/index.md create mode 100644 docs/overrides/partials/toc-item.html create mode 100644 docs/stylesheets/extra.css create mode 100644 mkdocs.yml create mode 100644 mkindex.py create mode 100644 requirements.txt diff --git a/.github/workflows/deploy-page.yml b/.github/workflows/deploy-page.yml new file mode 100644 index 0000000..44b3942 --- /dev/null +++ b/.github/workflows/deploy-page.yml @@ -0,0 +1,29 @@ +name: deploy_page +on: + push: + branches: + - main +permissions: + contents: write +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Configure Git Credentials + run: | + git config user.name github-actions[bot] + git config user.email 41898282+github-actions[bot]@users.noreply.github.com + - uses: actions/setup-python@v4 + with: + python-version: 3.x + - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV + - uses: actions/cache@v3 + with: + key: mkdocs-material-${{ env.cache_id }} + path: .cache + restore-keys: | + mkdocs-material- + - run: python -m pip install -r requirements.txt + - run: python mkindex.py + - run: mkdocs gh-deploy --force diff --git a/.github/workflows/jekyll-gh-pages.yml b/.github/workflows/jekyll-gh-pages.yml deleted file mode 100644 index 9cb771a..0000000 --- a/.github/workflows/jekyll-gh-pages.yml +++ /dev/null @@ -1,51 +0,0 @@ -# Sample workflow for building and deploying a Jekyll site to GitHub Pages -name: Deploy Jekyll with GitHub Pages dependencies preinstalled - -on: - # Runs on pushes targeting the default branch - push: - branches: ["main"] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: read - pages: write - id-token: write - -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. -concurrency: - group: "pages" - cancel-in-progress: false - -jobs: - # Build job - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Setup Pages - uses: actions/configure-pages@v4 - - name: Build with Jekyll - uses: actions/jekyll-build-pages@v1 - with: - source: ./ - destination: ./_site - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - - # Deployment job - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: build - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 diff --git a/.github/workflows/validate-url.yml b/.github/workflows/validate-url.yml new file mode 100644 index 0000000..cc9d62a --- /dev/null +++ b/.github/workflows/validate-url.yml @@ -0,0 +1,19 @@ +name: validate_url +on: # yamllint disable-line rule:truthy + pull_request: + branches: + - main +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Configure Git Credentials + run: | + git config user.name github-actions[bot] + git config user.email 41898282+github-actions[bot]@users.noreply.github.com + - uses: actions/setup-python@v4 + with: + python-version: 3.x + - run: python mkindex.py --validate-only + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cb14e7f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +venv/ diff --git a/README.md b/README.md index e9d935c..00a9b3b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # AI for Grant Writing A curated list of resources for using AI to develop more competitive grant applications. -## Table of Contents - [AI For Grant Writing](#ai-for-grant-writing) - [Useful Services](#useful-services) - [Prompt Resources](#prompt-resources) @@ -15,17 +14,17 @@ A curated list of resources for using AI to develop more competitive grant appli ## Useful Services -|| Checks Spelling and Grammar | Text Generation | Translation | Mock Review | Image Generation | Free Tier | -|--- |--- |--- |--- |--- |--- |--- | -| [ChatGPT](https://chat.openai.com) | x | x | x | x | x | x | -| [Bard](https://bard.google.com/) | x | x | x | x | | x | -| [Grok](https://grok.x.ai/) | x | x | x | x | | | -| [Copilot](https://copilot.microsoft.com/) | x | x | x | x | x | x | -| [Grammarly](https://www.grammarly.com/) | x | x | | x | | x | -| [Curie](https://www.aje.com/curie/) | x | | x | | | | -| [DeepL](https://www.deepl.com/translator) | | | x | | | x | -| [Midjourney](https://www.midjourney.com) | | | | | x | | -| [Firefly](https://www.adobe.com/products/firefly.html) | | | | | x | x | +| | Checks Spelling and Grammar | Text Generation | Translation | Mock Review | Image Generation | Free Tier | +| :--- | :---: | :---: | :---: | :---: | :---: | :---: | +| [ChatGPT](https://chat.openai.com) | x | x | x | x | x | x | +| [Bard](https://bard.google.com/) | x | x | x | x | | x | +| [Grok](https://grok.x.ai/) | x | x | x | x | | | +| [Copilot](https://copilot.microsoft.com/) | x | x | x | x | x | x | +| [Grammarly](https://www.grammarly.com/) | x | x | | x | | x | +| [Curie](https://www.aje.com/curie/) | x | | x | | | | +| [DeepL](https://www.deepl.com/translator) | | | x | | | x | +| [Midjourney](https://www.midjourney.com) | | | | | x | | +| [Firefly](https://www.adobe.com/products/firefly.html) | | | | | x | x | ## Prompt Resources @@ -87,4 +86,4 @@ missing and how I can improve. # Contributing -We encourage and welcome contributions! Please have a look at the [contribution guidelines](CONTRIBUTING.md) first. +We encourage and welcome contributions! Please have a look at the [contribution guidelines](https://github.com/eseckel/ai-for-grant-writing/blob/main/CONTRIBUTING.md) first. diff --git a/docs/_dev_readme.txt b/docs/_dev_readme.txt new file mode 100644 index 0000000..951daad --- /dev/null +++ b/docs/_dev_readme.txt @@ -0,0 +1,39 @@ +# How to build/update the website + +## Requirements +- Python +- Git + +## Steps + +### Cloning the repository +```bash +git clone https://github.com/eseckel/ai-for-grant-writing +``` + +### Installing the framework +```bash +cd ai-for-grant-writing + +# Create a virtual environment not to mess with system Python +python3 -m venv venv +source venv/bin/activate + +# Install requirements +python -m pip install -r requirements.txt + +# Test +mkdocs serve # check browser for https://localhost:8000 + +# To quit mkdocs serve do Ctrl-C +``` + +### Deploying a new version of the website +``` +source venv/bin/activate # You need the environment from the previous step + +# After making changes to README.md +python3 mkindex.py + +mkdocs serve # To check changes +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..e92eec1 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,83 @@ +--- +toc_depth: 3 +--- +# AI for Grant Writing +A curated list of resources for using AI to develop more competitive grant applications. + + + +## Useful Services + +| | Checks Spelling and Grammar | Text Generation | Translation | Mock Review | Image Generation | Free Tier | +| :--- | :---: | :---: | :---: | :---: | :---: | :---: | +| [ChatGPT](https://chat.openai.com) | x | x | x | x | x | x | +| [Bard](https://bard.google.com/) | x | x | x | x | | x | +| [Grok](https://grok.x.ai/) | x | x | x | x | | | +| [Copilot](https://copilot.microsoft.com/) | x | x | x | x | x | x | +| [Grammarly](https://www.grammarly.com/) | x | x | | x | | x | +| [Curie](https://www.aje.com/curie/) | x | | x | | | | +| [DeepL](https://www.deepl.com/translator) | | | x | | | x | +| [Midjourney](https://www.midjourney.com) | | | | | x | | +| [Firefly](https://www.adobe.com/products/firefly.html) | | | | | x | x | + +## Prompt Resources + +### Prompt Collections +- [85+ ChatGPT Prompts for Grant Writing](https://aihabit.net/chatgpt-prompts-for-grant-writing/#google_vignette) +- [ChatGPT Prompts For Academic Research Writing](https://clickup.com/templates/ai-prompts/research-writing) + +### Prompt Engineering +- [Google's Tips to enhance your prompt-engineering abilities](https://cloud.google.com/blog/products/application-development/five-best-practices-for-prompt-engineering) +- [Best practices for prompt engineering with OpenAI API](https://help.openai.com/en/articles/6654000-best-practices-for-prompt-engineering-with-openai-api) +- [Awesome GPT Prompt Engineering ](https://github.com/snwfdhmp/awesome-gpt-prompt-engineering) + +### Quick Prompts + +#### To enhance text clarity +``` +As a non-native English speaker, kindly help me revise the following text for improved understanding +and clarity. Please check for spelling and sentence structure errors and suggest alternatives. +``` + +``` +What suggestions do you have to enhance the clarity of my text? +``` + +``` +Please identify any parts of my writing that may be difficult for a lay audience to understand. +``` + +#### To make text more compelling +``` +Please provide feedback on my writing style and how I can make it more persuasive and compelling +for the grant reviewer. +``` +``` +I’m trying to hook my reader with a strong introduction. Can you suggest a more captivating first +sentence to draw them in from the start? +``` +#### To improve structure and flow of text +``` +I want to improve the overall structure of my Specific Aims. What tips do you have to structure it +more effectively?” +``` +#### To better align with the funding agency’s mission +``` +I’m working on a postdoctoral fellowship application. Can you please review my closing paragraph +and suggest ways to better align it with the American Heart Association’s mission? +``` +#### To better align text with review criteria +``` +I am applying to . Please provide me feedback on how well I am +addressing this review criteria: , and suggestions for what I am +missing and how I can improve. +``` + +## Grant Writing-Specific Resources +- [Best Kept Secrets to Winning Grants](https://www.nature.com/articles/545399a) +- [The Anatomy of a Specific Aims Page](https://biosciencewriters.com/NIH-Grant-Applications-The-Anatomy-of-a-Specific-Aims-Page.aspx) +- [R01 Countdown: Tools for Writing Concise and Compelling Grants](https://purl.stanford.edu/yy394gb6954) + +# Contributing + +We encourage and welcome contributions! Please have a look at the [contribution guidelines](https://github.com/eseckel/ai-for-grant-writing/blob/main/CONTRIBUTING.md) first. diff --git a/docs/overrides/partials/toc-item.html b/docs/overrides/partials/toc-item.html new file mode 100644 index 0000000..05d6797 --- /dev/null +++ b/docs/overrides/partials/toc-item.html @@ -0,0 +1,20 @@ +
  • + + + {{ toc_item.title }} + + + + + {% if toc_item.children %} + + {% endif %} +
  • diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css new file mode 100644 index 0000000..7bab214 --- /dev/null +++ b/docs/stylesheets/extra.css @@ -0,0 +1,5 @@ +/* Hide redundant titles */ +.md-typeset h1, +label.md-nav__title { + display: none; +} diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..1312ad9 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,26 @@ +site_name: AI for Grant Writing +site_url: https://awesome-python.com +site_description: A curated list of resources for using AI to develop more competitive grant applications. +site_author: Elizabeth Seckel +repo_name: eseckel/ai-for-grant-writing +repo_url: https://github.com/eseckel/ai-for-grant-writing +theme: + name: material + icon: + repo: fontawesome/brands/github + palette: + primary: purple + accent: blue grey + features: + - toc.follow + - toc.integrate + - content.code.copy + markdown_extensions: + - tables + custom_dir: docs/overrides +extra_css: + - stylesheets/extra.css +plugins: + - exclude: + glob: + - "*.txt" diff --git a/mkindex.py b/mkindex.py new file mode 100644 index 0000000..a47ca60 --- /dev/null +++ b/mkindex.py @@ -0,0 +1,121 @@ +"""Small script to parse and validate README.md into a proper index.md +""" + +import argparse +import pathlib +import re +import sys +import urllib.request + +from urllib.parse import urlparse + + +INDEX_MD_PATH = pathlib.Path("docs") / "index.md" + + +# URL validation code +class NoRedirection(urllib.request.HTTPErrorProcessor): + def http_response(self, request, response): + return response + https_response = http_response + + +def validate_single_url(url): + """Checks if a url is valid by querying it.""" + + # First do a quick syntax check + try: + r = urlparse(url) + except Exception: + raise Exception(f"Failed to parse URL: {url}") + else: + if not r.scheme or not r.netloc: + raise Exception(f"URL appears to be mal-formatted: {url}") + + # Now do a quick check to see if the page actually exists + try: + opener = urllib.request.build_opener(NoRedirection) + req = urllib.request.Request(url, method="HEAD") + with opener.open(req) as response: + _ = response.read(10) + except Exception: + raise Exception(f"URL cannot be reached: {url}") + + +def validate_all_urls(lines): + """Search and validate URLs in a set of lines.""" + + # Collect headers for markdown link validation + headers = set() + for ln in lines: + if ln.lstrip().startswith("#"): + url = ln.replace("#", "").strip().replace(" ", "-").lower() + headers.add(url) + + re_url = re.compile(r"\[[\w\s']+\]\((.+)\)") + + failed = False + for ix, ln in enumerate(lines, start=1): + m = re_url.search(ln) + if m: + url = m.group(1) + if url.startswith("#"): # Markdown URL + if url[1:] not in headers: + print(f"Markdown link on line {ix} is not valid: {url}") + failed = True + else: + try: + _ = validate_single_url(url) + except Exception as err: + print(f"URL validation failed on line {ix}: {err}") + failed = True + if failed: + sys.exit(1) # abort + + +def filter_gh_toc(lines): + """Filter the TOC for GitHub that should not be rendered in HTML.""" + # TOC is the first listing before we hit any level 2 header (##) + ignore_lists = True + for ln in lines: + if ln.startswith("##"): # level 2, 3, ... headers + ignore_lists = False + elif ln.lstrip().startswith("-"): # is list + if ignore_lists: + continue + yield ln + + +def add_toc_depth_limit(): + yield from ["---", "toc_depth: 3", "---"] + + +if __name__ == "__main__": + + ap = argparse.ArgumentParser(description=__doc__) + ap.add_argument("readme", + type=pathlib.Path, + help="File to process, e.g. README.md") + ap.add_argument("--validate-only", + action="store_true", + help="Only validate URLs.") + args = ap.parse_args() + + # Read input file into memory. + with open(args.readme) as handle: + lines = handle.readlines() + + validate_all_urls(lines) + if args.validate_only: + sys.exit(0) + + # Write to docs/ + with open(INDEX_MD_PATH, "w") as handle: + # Add toc limit + for ln in add_toc_depth_limit(): + print(ln, file=handle) + + # Output index.md file + for ln in filter_gh_toc(lines): + print(ln, file=handle, end="") + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7194131 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +mkdocs==1.5.3 +mkdocs-exclude==1.0.2 +mkdocs-material==9.5.4 From 4f43aeb80085284f9f2a75355a974dd053500fb7 Mon Sep 17 00:00:00 2001 From: Joao Rodrigues Date: Tue, 16 Jan 2024 19:59:23 -0500 Subject: [PATCH 2/3] Fix call to validator. --- .github/workflows/deploy-page.yml | 2 +- .github/workflows/validate-url.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-page.yml b/.github/workflows/deploy-page.yml index 44b3942..37b4aae 100644 --- a/.github/workflows/deploy-page.yml +++ b/.github/workflows/deploy-page.yml @@ -25,5 +25,5 @@ jobs: restore-keys: | mkdocs-material- - run: python -m pip install -r requirements.txt - - run: python mkindex.py + - run: python mkindex.py README.md - run: mkdocs gh-deploy --force diff --git a/.github/workflows/validate-url.yml b/.github/workflows/validate-url.yml index cc9d62a..c6b84f9 100644 --- a/.github/workflows/validate-url.yml +++ b/.github/workflows/validate-url.yml @@ -15,5 +15,5 @@ jobs: - uses: actions/setup-python@v4 with: python-version: 3.x - - run: python mkindex.py --validate-only + - run: python mkindex.py --validate-only README.md From 74486408c629b9ce82f8cf64b4587302bb073aa6 Mon Sep 17 00:00:00 2001 From: Joao Rodrigues Date: Tue, 16 Jan 2024 20:06:31 -0500 Subject: [PATCH 3/3] Improved dev notes. --- docs/{_dev_readme.txt => _dev_notes.md} | 7 +++++++ 1 file changed, 7 insertions(+) rename docs/{_dev_readme.txt => _dev_notes.md} (67%) diff --git a/docs/_dev_readme.txt b/docs/_dev_notes.md similarity index 67% rename from docs/_dev_readme.txt rename to docs/_dev_notes.md index 951daad..3041036 100644 --- a/docs/_dev_readme.txt +++ b/docs/_dev_notes.md @@ -1,5 +1,12 @@ # How to build/update the website +## Notes +- You should not have to use this on a regular basis. The repository is setup to +run the validation for each pull request and the deployment whenever you accept +a pull request or push a commit directly to the main branch. You should use this +to test changes locally. +- Your repository should be setup to serve pages from the gh-pages branch. + ## Requirements - Python - Git