From 7463fc7246fa59bcc6acd9b9ff8da62f861dea1c Mon Sep 17 00:00:00 2001 From: Tony Wu Date: Tue, 3 Mar 2026 17:13:21 -0500 Subject: [PATCH 1/4] add weekly readme updater --- .github/scripts/extract_exports.py | 105 +++++++++++++++++++ .github/scripts/upgrade_readme.py | 106 ++++++++++++++++++++ .github/workflows/weekly-readme-upgrade.yml | 87 ++++++++++++++++ 3 files changed, 298 insertions(+) create mode 100644 .github/scripts/extract_exports.py create mode 100644 .github/scripts/upgrade_readme.py create mode 100644 .github/workflows/weekly-readme-upgrade.yml diff --git a/.github/scripts/extract_exports.py b/.github/scripts/extract_exports.py new file mode 100644 index 0000000..1db180a --- /dev/null +++ b/.github/scripts/extract_exports.py @@ -0,0 +1,105 @@ +""" +.github/scripts/extract_exports.py + +Walks R/, finds every function tagged with @export, and extracts its full +roxygen2 documentation block. Outputs a JSON blob to GITHUB_OUTPUT so that +upgrade_readme.py can consume it without re-parsing the R files. + +Also deterministically picks a section focus for this week based on the +ISO week number, so the README improves a different area each week in rotation. +""" + +import os +import re +import sys +import json +from datetime import date + +SEARCH_DIR = "R" + +SECTION_ROTATION = [ + "Getting Started / Installation", + "Usage Examples", + "Function Reference Table", + "Motivation & Use Cases", + "FAQ / Common Pitfalls", + "Contributing & Development Setup", +] + + +def extract_function_docs(filepath: str) -> list[dict]: + """ + Returns a list of dicts, one per @export-tagged function: + { "name": str, "file": str, "roxygen": str } + """ + with open(filepath, "r", errors="ignore") as f: + lines = f.readlines() + + results = [] + for i, line in enumerate(lines): + m = re.match(r"^(\w+)\s*(?:<-|=)\s*function\s*\(", line) + if not m: + continue + fn_name = m.group(1) + + # Collect the contiguous roxygen2 block above this line + roxygen_lines = [] + j = i - 1 + while j >= 0 and re.match(r"^#'", lines[j]): + roxygen_lines.insert(0, lines[j].rstrip()) + j -= 1 + + if any("@export" in l for l in roxygen_lines): + results.append({ + "name": fn_name, + "file": filepath, + "roxygen": "\n".join(roxygen_lines), + }) + + return results + + +def main(): + all_exports = [] + + for root, _dirs, files in os.walk(SEARCH_DIR): + for fname in sorted(files): + if not fname.endswith(".R"): + continue + fpath = os.path.join(root, fname) + try: + all_exports.extend(extract_function_docs(fpath)) + except Exception as e: + print(f"⚠️ Skipping {fpath}: {e}", file=sys.stderr) + + if not all_exports: + print( + "❌ No @export-tagged functions found in R/.\n" + " Add #' @export above your exported function definitions.", + file=sys.stderr, + ) + sys.exit(1) + + print(f"✅ Found {len(all_exports)} exported functions:") + for exp in all_exports: + print(f" • {exp['name']} ({exp['file']})") + + # Rotate section focus by ISO week number so it's deterministic + week_number = date.today().isocalendar().week + section_focus = SECTION_ROTATION[week_number % len(SECTION_ROTATION)] + print(f"\n📌 This week's README focus: {section_focus}") + + exports_json = json.dumps(all_exports) + + github_output = os.environ.get("GITHUB_OUTPUT") + if github_output: + with open(github_output, "a") as fh: + fh.write(f"exports_json={exports_json}\n") + fh.write(f"section_focus={section_focus}\n") + else: + print(f"\nEXPORTS_JSON={exports_json}") + print(f"SECTION_FOCUS={section_focus}") + + +if __name__ == "__main__": + main() diff --git a/.github/scripts/upgrade_readme.py b/.github/scripts/upgrade_readme.py new file mode 100644 index 0000000..693e3dc --- /dev/null +++ b/.github/scripts/upgrade_readme.py @@ -0,0 +1,106 @@ +""" +.github/scripts/upgrade_readme.py + +Reads the current README.md and the full list of exported functions (from +EXPORTS_JSON), then asks the AI to make one focused, cohesive improvement +to the README based on the week's section focus. + +Required environment variables: + OPENAI_API_KEY - your OpenAI secret key + EXPORTS_JSON - JSON string produced by extract_exports.py +""" + +import os +import sys +import json +from openai import OpenAI + +README_PATH = "README.md" + +PROMPT_TEMPLATE = """\ +You are a technical writer helping improve the README for an R package. + +## Current README + +```markdown +{readme_content} +``` + +## Exported Functions + +Below are all the exported functions in this package, along with their \ +current roxygen2 documentation: + +{exports_block} + +## Your Task + +Make a single, cohesive improvement to the README focused on: **{section_focus}** + +Guidelines: +- Improve or add the "{section_focus}" section using the exported functions above as your source of truth. +- Write clean, idiomatic R code examples that actually work. +- Keep the tone friendly and practical — help someone get up and running quickly. +- Do not remove or substantially alter any existing sections outside your focus area. +- Do not add placeholder text like "coming soon" or "TODO". +- Return ONLY the full updated README.md contents, with no explanation or markdown fences around it. +""" + + +def format_exports_block(exports: list[dict]) -> str: + blocks = [] + for exp in exports: + blocks.append(f"### `{exp['name']}` ({exp['file']})\n\n{exp['roxygen']}") + return "\n\n---\n\n".join(blocks) + + +def main(): + api_key = os.environ.get("OPENAI_API_KEY") + exports_json = os.environ.get("EXPORTS_JSON") + + missing = [k for k, v in { + "OPENAI_API_KEY": api_key, + "EXPORTS_JSON": exports_json, + }.items() if not v] + + if missing: + print(f"❌ Missing required environment variables: {', '.join(missing)}", file=sys.stderr) + sys.exit(1) + + exports = json.loads(exports_json) + section_focus = os.environ.get("SECTION_FOCUS", "Usage Examples") + + if not os.path.exists(README_PATH): + print(f"❌ {README_PATH} not found in repo root.", file=sys.stderr) + sys.exit(1) + + with open(README_PATH, "r") as f: + readme_content = f.read() + + exports_block = format_exports_block(exports) + + prompt = PROMPT_TEMPLATE.format( + readme_content=readme_content, + exports_block=exports_block, + section_focus=section_focus, + ) + + client = OpenAI(api_key=api_key) + print(f"⏳ Calling OpenAI to improve README (focus: {section_focus}) ...") + + response = client.chat.completions.create( + model="gpt-4o", + messages=[{"role": "user", "content": prompt}], + temperature=0.4, + ) + + updated_readme = response.choices[0].message.content.strip() + + with open(README_PATH, "w") as f: + f.write(updated_readme) + + print(f"✅ README updated successfully.") + + +if __name__ == "__main__": + main() diff --git a/.github/workflows/weekly-readme-upgrade.yml b/.github/workflows/weekly-readme-upgrade.yml new file mode 100644 index 0000000..9bed5e0 --- /dev/null +++ b/.github/workflows/weekly-readme-upgrade.yml @@ -0,0 +1,87 @@ +# .github/workflows/weekly-readme-upgrade.yml +# +# Runs every week, reads all exported R functions + their roxygen2 docs, +# and asks the AI to make one cohesive improvement to the README. +# Each week it rotates focus: examples, getting started, function reference, etc. +# +# SETUP: Same prerequisites as weekly-doc-upgrade.yml +# - MSSTATS_OPENAI_KEY secret +# - Actions read/write permissions + +name: Weekly README Upgrade + +on: + pull_request: + branches: [devel] + +jobs: + readme-upgrade: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install dependencies + run: pip install openai + + - name: Extract exported functions + id: extract + run: python3 .github/scripts/extract_exports.py + + - name: Upgrade README with AI + env: + OPENAI_API_KEY: ${{ secrets.MSSTATS_OPENAI_KEY }} + EXPORTS_JSON: ${{ steps.extract.outputs.exports_json }} + run: python3 .github/scripts/upgrade_readme.py + + - name: Create branch and commit + run: | + BRANCH="ai-docs/readme-$(date +%Y-%m-%d)" + echo "BRANCH=$BRANCH" >> $GITHUB_ENV + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + git checkout -b "$BRANCH" + git add README.md + git commit -m "docs: Weekly AI README upgrade ($(date +%Y-%m-%d))" + git push origin "$BRANCH" + + - name: Ensure labels exist + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh label create "documentation" --color "0075ca" --description "Improvements or additions to documentation" 2>/dev/null || true + + - name: Open Pull Request + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SECTION_FOCUS: ${{ steps.extract.outputs.section_focus }} + run: | + gh pr create \ + --title "docs: Weekly AI README upgrade — ${SECTION_FOCUS}" \ + --body "## 🤖 Automated README Upgrade + + This PR was auto-generated by the weekly README upgrade workflow. + + **This week's focus:** ${SECTION_FOCUS} + + The AI was given the full list of exported functions and their roxygen2 + documentation, and made a single cohesive improvement to \`README.md\`. + + **What to check:** + - [ ] The added content is accurate + - [ ] Code examples actually run + - [ ] The tone and style match the rest of the README + - [ ] No existing content was accidentally removed + + > Merge if it looks good. Each week the bot will focus on a different section." \ + --base main \ + --head "$BRANCH" \ + --label "documentation" From 0adbc4ef4ef43648cc78bf3ee16f84fd11b25f6c Mon Sep 17 00:00:00 2001 From: Tony Wu Date: Tue, 3 Mar 2026 17:15:11 -0500 Subject: [PATCH 2/4] fix main branch to devel --- .github/workflows/weekly-readme-upgrade.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/weekly-readme-upgrade.yml b/.github/workflows/weekly-readme-upgrade.yml index 9bed5e0..1edd967 100644 --- a/.github/workflows/weekly-readme-upgrade.yml +++ b/.github/workflows/weekly-readme-upgrade.yml @@ -82,6 +82,6 @@ jobs: - [ ] No existing content was accidentally removed > Merge if it looks good. Each week the bot will focus on a different section." \ - --base main \ + --base devel \ --head "$BRANCH" \ --label "documentation" From 2b73b74de9ae7ba32c052df81ab2db2fe1133c4c Mon Sep 17 00:00:00 2001 From: Tony Wu Date: Tue, 3 Mar 2026 17:18:57 -0500 Subject: [PATCH 3/4] remove label step --- .github/workflows/weekly-readme-upgrade.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/weekly-readme-upgrade.yml b/.github/workflows/weekly-readme-upgrade.yml index 1edd967..68f7ecb 100644 --- a/.github/workflows/weekly-readme-upgrade.yml +++ b/.github/workflows/weekly-readme-upgrade.yml @@ -53,12 +53,6 @@ jobs: git commit -m "docs: Weekly AI README upgrade ($(date +%Y-%m-%d))" git push origin "$BRANCH" - - name: Ensure labels exist - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh label create "documentation" --color "0075ca" --description "Improvements or additions to documentation" 2>/dev/null || true - - name: Open Pull Request env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 5b8c5636be79553c5d609ed6a277d5f53a88974f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 3 Mar 2026 22:19:29 +0000 Subject: [PATCH 4/4] docs: Weekly AI README upgrade (2026-03-03) --- README.md | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9d21ac2..430763a 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,77 @@ You can install the development version of this package through Github: devtools::install_github("Vitek-Lab/MSstatsBioNet", build_vignettes = TRUE) ``` +## Usage Examples + +Here are some examples to help you get started with MSstatsBioNet: + +### Annotate Protein Information + +Use the `annotateProteinInfoFromIndra` function to annotate a data frame with protein information from Indra. + +```r +library(MSstatsBioNet) + +# Example data frame +df <- data.frame(Protein = c("CLH1_HUMAN")) + +# Annotate protein information +annotated_df <- annotateProteinInfoFromIndra(df, "Uniprot_Mnemonic") +print(head(annotated_df)) +``` + +### Visualize Networks with Cytoscape + +Create an interactive network diagram using `cytoscapeNetwork`. + +```r +# Define nodes and edges +nodes <- data.frame( + id = c("TP53", "MDM2", "CDKN1A"), + logFC = c(1.5, -0.8, 2.1), + stringsAsFactors = FALSE +) +edges <- data.frame( + source = c("TP53", "MDM2"), + target = c("MDM2", "TP53"), + interaction = c("Activation", "Inhibition"), + stringsAsFactors = FALSE +) + +# Render the network +cytoscapeNetwork(nodes, edges) +``` + +### Export Network to HTML + +Export your network visualization to an HTML file using `exportNetworkToHTML`. + +```r +# Export the network to an HTML file +exportNetworkToHTML(nodes, edges, filename = "network.html") +``` + +### Retrieve Subnetwork from INDRA + +Use `getSubnetworkFromIndra` to retrieve a subnetwork of protein interactions from the INDRA database. + +```r +# Load example input data +input <- data.table::fread(system.file( + "extdata/groupComparisonModel.csv", + package = "MSstatsBioNet" +)) + +# Get subnetwork +subnetwork <- getSubnetworkFromIndra(input) +print(head(subnetwork$nodes)) +print(head(subnetwork$edges)) +``` + ## License -This package is distributed under the [Artistic-2.0](https://opensource.org/licenses/Artistic-2.0) license. However, its dependencies may have different licenses. +This package is distributed under the [Artistic-2.0](https://opensource.org/licenses/Artistic-2.0) license. However, its dependencies may have different licenses. -Notably, INDRA is distributed under the [BSD 2-Clause](https://opensource.org/license/bsd-2-clause) license. Furthermore, INDRA's knowledge sources may have different licenses for commercial applications. Please refer to the [INDRA README](https://github.com/sorgerlab/indra?tab=readme-ov-file#indra-modules) for more information on its knowledge sources and their associated licenses. +Notably, INDRA is distributed under the [BSD 2-Clause](https://opensource.org/license/bsd-2-clause) license. Furthermore, INDRA's knowledge sources may have different licenses for commercial applications. Please refer to the [INDRA README](https://github.com/sorgerlab/indra?tab=readme-ov-file#indra-modules) for more information on its knowledge sources and their associated licenses. ## Databases Supported @@ -36,4 +103,4 @@ Notably, INDRA is distributed under the [BSD 2-Clause](https://opensource.org/li ## Visualization Options Supported -- Cytoscape Desktop +- Cytoscape Desktop \ No newline at end of file