From bbf6d7c2a3aad7b6e720e0619a9024dae41feed5 Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Fri, 27 Dec 2024 16:13:38 +0800 Subject: [PATCH 01/16] feat: generate discussion card svg --- .github/scripts/.gitignore | 2 + .github/scripts/requirements.txt | 1 + .github/scripts/update_readme.py | 125 +++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 .github/scripts/.gitignore create mode 100644 .github/scripts/requirements.txt create mode 100644 .github/scripts/update_readme.py diff --git a/.github/scripts/.gitignore b/.github/scripts/.gitignore new file mode 100644 index 0000000..d684fdc --- /dev/null +++ b/.github/scripts/.gitignore @@ -0,0 +1,2 @@ +*.svg +.env/ diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt new file mode 100644 index 0000000..e37c1f7 --- /dev/null +++ b/.github/scripts/requirements.txt @@ -0,0 +1 @@ +PyGithub==2.5.0 diff --git a/.github/scripts/update_readme.py b/.github/scripts/update_readme.py new file mode 100644 index 0000000..12288a6 --- /dev/null +++ b/.github/scripts/update_readme.py @@ -0,0 +1,125 @@ +from github import Github +import os + +GITHUB_TOKEN = os.environ['GITHUB_TOKEN'] +if not GITHUB_TOKEN: + raise ValueError("GITHUB_TOKEN environment variable is not set") + +def generate_github_discussion_svg(title, username, emoji, labels, category, upvotes, comments): + width = 820 + height = 120 + emoji_size = 20 # 16 font size equal 20x19 px + emoji_box_x = 30 + emoji_box_size = 54 + upvote_width = len(str(upvotes)) * 10 + 30 + upvote_x_center = 760 + upvote_rect_x = upvote_x_center - upvote_width / 2 + + # Create SVG content + svg = f""" + + + + + + + + + + + + + {emoji} + + + {title} + + + + """ + x_offset = 0 + for label in labels: + label_width = len(label['text']) * 8 + 5 + svg += f""" + + {label['text']} + """ + x_offset += label_width + 10 + + svg += f""" + + + + {username} started this discussion + + + + + + {upvotes} + + + + + + {comments} + + + """ + + return svg + +def fetch_recent_discussions(): + g = Github(GITHUB_TOKEN) + + #query = """ + #{ + # repository(owner: "brenocq", name: "implot3d") { + # discussions(first: 5, orderBy: {field: UPDATED_AT, direction: DESC}) { + # nodes { + # title + # url + # updatedAt + # } + # } + # } + #} + #""" + + #result = g.graphql(query) + #discussions = result["data"]["repository"]["discussions"]["nodes"] + #for discussion in discussions: + # print(f"{discussion['title']} - {discussion['url']} (Updated at: {discussion['updatedAt']})") + for repo in g.get_user().get_repos(): + print(repo.name) + repo.edit(has_wiki=False) + # to see all the available attributes and methods + if repo.name == "implot3d": + print(dir(repo)) + + g.close() + +fetch_recent_discussions() + +# Example usage +labels = [ + {"text": "type:chore", "color": "#0366d6"}, + {"text": "prio:medium", "color": "#f1c40f"}, + {"text": "status:todo", "color": "#3498db"} +] + +svg_output = generate_github_discussion_svg( + title="Hosted online demo", + username="brenocq", + emoji="💡", + labels=labels, + category="Features and improvements", + upvotes=3, + comments=2 +) + +# Save to file +with open("discussion_card.svg", "w") as f: + f.write(svg_output) + +fetch_recent_discussions() From 5265e7d31b256d786e60ae50a7d5e4d2f74ea524 Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Sat, 28 Dec 2024 11:14:14 +0800 Subject: [PATCH 02/16] chore: fetch discussion card info from GitHub --- .github/scripts/update_readme.py | 186 +++++++++++++++++++++---------- 1 file changed, 130 insertions(+), 56 deletions(-) diff --git a/.github/scripts/update_readme.py b/.github/scripts/update_readme.py index 12288a6..6b7a754 100644 --- a/.github/scripts/update_readme.py +++ b/.github/scripts/update_readme.py @@ -1,11 +1,13 @@ from github import Github import os +import requests +from datetime import datetime GITHUB_TOKEN = os.environ['GITHUB_TOKEN'] if not GITHUB_TOKEN: raise ValueError("GITHUB_TOKEN environment variable is not set") -def generate_github_discussion_svg(title, username, emoji, labels, category, upvotes, comments): +def generate_discussion_svg(title, emoji, labels, category, upvotes, comments, author, created_at, last_comment_by, last_comment_at): width = 820 height = 120 emoji_size = 20 # 16 font size equal 20x19 px @@ -15,6 +17,17 @@ def generate_github_discussion_svg(title, username, emoji, labels, category, upv upvote_x_center = 760 upvote_rect_x = upvote_x_center - upvote_width / 2 + # Format dates + created_at_formatted = datetime.strptime(created_at, "%Y-%m-%dT%H:%M:%SZ").strftime("%d %b %Y") + last_comment_at_formatted = ( + datetime.strptime(last_comment_at, "%Y-%m-%dT%H:%M:%SZ").strftime("%d %b %Y") if last_comment_at else None + ) + + # Build the contributor text + contributor_comment = f'{author} started on {created_at_formatted}.' + if last_comment_by and last_comment_at_formatted: + contributor_comment = contributor_comment + f' Last comment by {last_comment_by} on {last_comment_at_formatted}.' + # Create SVG content svg = f""" @@ -50,7 +63,7 @@ def generate_github_discussion_svg(title, username, emoji, labels, category, upv - {username} started this discussion + {contributor_comment} @@ -69,57 +82,118 @@ def generate_github_discussion_svg(title, username, emoji, labels, category, upv return svg -def fetch_recent_discussions(): - g = Github(GITHUB_TOKEN) - - #query = """ - #{ - # repository(owner: "brenocq", name: "implot3d") { - # discussions(first: 5, orderBy: {field: UPDATED_AT, direction: DESC}) { - # nodes { - # title - # url - # updatedAt - # } - # } - # } - #} - #""" - - #result = g.graphql(query) - #discussions = result["data"]["repository"]["discussions"]["nodes"] - #for discussion in discussions: - # print(f"{discussion['title']} - {discussion['url']} (Updated at: {discussion['updatedAt']})") - for repo in g.get_user().get_repos(): - print(repo.name) - repo.edit(has_wiki=False) - # to see all the available attributes and methods - if repo.name == "implot3d": - print(dir(repo)) - - g.close() - -fetch_recent_discussions() - -# Example usage -labels = [ - {"text": "type:chore", "color": "#0366d6"}, - {"text": "prio:medium", "color": "#f1c40f"}, - {"text": "status:todo", "color": "#3498db"} -] - -svg_output = generate_github_discussion_svg( - title="Hosted online demo", - username="brenocq", - emoji="💡", - labels=labels, - category="Features and improvements", - upvotes=3, - comments=2 -) - -# Save to file -with open("discussion_card.svg", "w") as f: - f.write(svg_output) - -fetch_recent_discussions() +def generate_recent_discussion_svgs(): + url = "https://api.github.com/graphql" + + headers = { + "Authorization": f"Bearer {GITHUB_TOKEN}", + "Content-Type": "application/json" + } + + query = """ + { + repository(owner: "brenocq", name: "implot3d") { + discussions(first: 5, categoryId: "DIC_kwDONQXA0M4ClSCg", orderBy: {field: UPDATED_AT, direction: DESC}) { + nodes { + title + url + createdAt + updatedAt + upvoteCount + comments(first: 10) { + totalCount + nodes { + author { + login + } + createdAt + replies(first: 10) { + totalCount + nodes { + author { + login + } + createdAt + } + } + } + } + labels(first: 5) { + nodes { + name + color + } + } + category { + name + } + author { + login + } + } + } + } + } + """ + + response = requests.post(url, headers=headers, json={"query": query}) + + data = response.json() + + if response.status_code == 200 and 'data' in data: + discussions = data["data"]["repository"]["discussions"]["nodes"] + + for i, discussion in enumerate(discussions): + print(f"Generating SVG for: {discussion['title']}") + + # Calculate total comments (including replies) + total_comments = 0 + last_comment_by = None + last_comment_at = None + for comment in discussion['comments']['nodes']: + total_comments += 1 # Top-level comment + total_comments += comment['replies']['totalCount'] # Add replies + + # Track the last comment + if last_comment_at is None or comment['createdAt'] > last_comment_at: + last_comment_by = comment['author']['login'] + last_comment_at = comment['createdAt'] + + # Track the last comment in case it is a reply + for reply in comment['replies']['nodes']: + if reply['createdAt'] > last_comment_at: + last_comment_by = reply['author']['login'] + last_comment_at = reply['createdAt'] + + # Extract labels + labels = [ + {"text": label['name'], "color": f"#{label['color']}"} + for label in discussion['labels']['nodes'] + ] + + # Generate the SVG for each discussion + svg_output = generate_discussion_svg( + title=discussion['title'], + emoji="💡", + labels=labels, + category=discussion['category']['name'], + upvotes=discussion['upvoteCount'], + comments=total_comments, + author=discussion['author']['login'], + created_at=discussion['createdAt'], + last_comment_by=last_comment_by, + last_comment_at=last_comment_at + ) + + # Save each SVG to a unique file + filename = f"discussion_{i}.svg" + with open(filename, "w") as f: + f.write(svg_output) + + print(f"Saved SVG as {filename}") + print("-" * 60) + else: + print("Error or No Data Returned") + print(f"Response: {data}") + +generate_recent_discussion_svgs() From 872a5a65c2317fe79989fa0f0e23df1d78f8e7ad Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Sat, 28 Dec 2024 18:16:39 +0800 Subject: [PATCH 03/16] chore: upload SVGs to google storage --- .github/scripts/.gitignore | 1 + .github/scripts/requirements.txt | 2 +- .github/scripts/update_readme.py | 18 +++++++++++++++--- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/.github/scripts/.gitignore b/.github/scripts/.gitignore index d684fdc..90c4c3f 100644 --- a/.github/scripts/.gitignore +++ b/.github/scripts/.gitignore @@ -1,2 +1,3 @@ *.svg .env/ +*.json diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index e37c1f7..8eac1cf 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -1 +1 @@ -PyGithub==2.5.0 +google-cloud-storage==2.19.0 diff --git a/.github/scripts/update_readme.py b/.github/scripts/update_readme.py index 6b7a754..041e760 100644 --- a/.github/scripts/update_readme.py +++ b/.github/scripts/update_readme.py @@ -1,8 +1,15 @@ -from github import Github import os import requests from datetime import datetime +from google.cloud import storage +# GCloud +storage_client = storage.Client() +bucket = storage_client.get_bucket('implot3d') + +os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "gcp_key.json" + +# GitHub token GITHUB_TOKEN = os.environ['GITHUB_TOKEN'] if not GITHUB_TOKEN: raise ValueError("GITHUB_TOKEN environment variable is not set") @@ -82,7 +89,7 @@ def generate_discussion_svg(title, emoji, labels, category, upvotes, comments, a return svg -def generate_recent_discussion_svgs(): +def update_svgs(): url = "https://api.github.com/graphql" headers = { @@ -191,9 +198,14 @@ def generate_recent_discussion_svgs(): f.write(svg_output) print(f"Saved SVG as {filename}") + + # Upload SVG to GCloud + blob = bucket.blob(filename) + blob.upload_from_filename(filename) + print(f"Uploaded {filename} to google storage") print("-" * 60) else: print("Error or No Data Returned") print(f"Response: {data}") -generate_recent_discussion_svgs() +update_svgs() From b29e58b4e43f74c1d472485c6c277eef7e70b077 Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Sat, 28 Dec 2024 18:52:08 +0800 Subject: [PATCH 04/16] chore: discussion workflow to update readme --- .github/scripts/update_readme.py | 4 +-- .github/workflows/update_readme.yml | 38 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/update_readme.yml diff --git a/.github/scripts/update_readme.py b/.github/scripts/update_readme.py index 041e760..190ff03 100644 --- a/.github/scripts/update_readme.py +++ b/.github/scripts/update_readme.py @@ -7,8 +7,6 @@ storage_client = storage.Client() bucket = storage_client.get_bucket('implot3d') -os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "gcp_key.json" - # GitHub token GITHUB_TOKEN = os.environ['GITHUB_TOKEN'] if not GITHUB_TOKEN: @@ -31,7 +29,7 @@ def generate_discussion_svg(title, emoji, labels, category, upvotes, comments, a ) # Build the contributor text - contributor_comment = f'{author} started on {created_at_formatted}.' + contributor_comment = f'GITHUB {author} started on {created_at_formatted}.' if last_comment_by and last_comment_at_formatted: contributor_comment = contributor_comment + f' Last comment by {last_comment_by} on {last_comment_at_formatted}.' diff --git a/.github/workflows/update_readme.yml b/.github/workflows/update_readme.yml new file mode 100644 index 0000000..0d25fdd --- /dev/null +++ b/.github/workflows/update_readme.yml @@ -0,0 +1,38 @@ +name: Update README on Discussion Events + +on: + discussion: + types: [created, edited] + discussion_comment: + types: [created, edited] + +jobs: + update-readme: + runs-on: ubuntu-latest + permissions: + discussions: read + + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + + - name: Set Up Python + uses: actions/setup-python@v3 + with: + python-version: '3.x' + + - name: Authenticate using OIDC + uses: google-github-actions/auth@v1 + with: + workload_identity_provider: "projects/392069959223/locations/global/workloadIdentityPools/github-pool/providers/github-provider" + service_account: "github-oidc-uploader@implot3d.iam.gserviceaccount.com" + + - name: Install Dependencies + run: | + pip install -r .github/scripts/requirements.txt + + - name: Update README images + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + python .github/scripts/update_readme.py From 62de33f8262a87edea69158a79c4c5ddddf14518 Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Sat, 28 Dec 2024 18:57:34 +0800 Subject: [PATCH 05/16] chore: test manual triggering --- .github/workflows/update_readme.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/update_readme.yml b/.github/workflows/update_readme.yml index 0d25fdd..d247004 100644 --- a/.github/workflows/update_readme.yml +++ b/.github/workflows/update_readme.yml @@ -5,6 +5,7 @@ on: types: [created, edited] discussion_comment: types: [created, edited] + workflow_dispatch: jobs: update-readme: From cb05e244aa5dfb993570395e393715f5a039400c Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Sat, 28 Dec 2024 18:58:15 +0800 Subject: [PATCH 06/16] chore: test run on every push --- .github/workflows/update_readme.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update_readme.yml b/.github/workflows/update_readme.yml index d247004..919e17a 100644 --- a/.github/workflows/update_readme.yml +++ b/.github/workflows/update_readme.yml @@ -5,7 +5,7 @@ on: types: [created, edited] discussion_comment: types: [created, edited] - workflow_dispatch: + push: jobs: update-readme: From 4c49e01caa0669c784b24b63c31e3f12ee59314f Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Sat, 28 Dec 2024 19:01:38 +0800 Subject: [PATCH 07/16] chore: add OIDC permission --- .github/workflows/update_readme.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/update_readme.yml b/.github/workflows/update_readme.yml index 919e17a..980ab0f 100644 --- a/.github/workflows/update_readme.yml +++ b/.github/workflows/update_readme.yml @@ -12,6 +12,7 @@ jobs: runs-on: ubuntu-latest permissions: discussions: read + id-token: write steps: - name: Checkout Repository From 7e416f8cd5b00ac4bc7e61cbb7e0ce4155d79b96 Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Sat, 28 Dec 2024 19:25:44 +0800 Subject: [PATCH 08/16] chore: test GCP with json --- .github/workflows/update_readme.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/update_readme.yml b/.github/workflows/update_readme.yml index 980ab0f..c56d228 100644 --- a/.github/workflows/update_readme.yml +++ b/.github/workflows/update_readme.yml @@ -12,7 +12,6 @@ jobs: runs-on: ubuntu-latest permissions: discussions: read - id-token: write steps: - name: Checkout Repository @@ -23,11 +22,12 @@ jobs: with: python-version: '3.x' - - name: Authenticate using OIDC - uses: google-github-actions/auth@v1 - with: - workload_identity_provider: "projects/392069959223/locations/global/workloadIdentityPools/github-pool/providers/github-provider" - service_account: "github-oidc-uploader@implot3d.iam.gserviceaccount.com" + - name: Authenticate with GCP (Service Account Key) + env: + GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }} + run: | + echo "$GCP_SA_KEY" > /tmp/gcp-key.json + export GOOGLE_APPLICATION_CREDENTIALS="/tmp/gcp-key.json" - name: Install Dependencies run: | From 9eaf574dbba5f317108f683f0a703ba543289829 Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Sat, 28 Dec 2024 19:30:26 +0800 Subject: [PATCH 09/16] chore: set GCP json path env --- .github/workflows/update_readme.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update_readme.yml b/.github/workflows/update_readme.yml index c56d228..42df3fd 100644 --- a/.github/workflows/update_readme.yml +++ b/.github/workflows/update_readme.yml @@ -27,7 +27,6 @@ jobs: GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }} run: | echo "$GCP_SA_KEY" > /tmp/gcp-key.json - export GOOGLE_APPLICATION_CREDENTIALS="/tmp/gcp-key.json" - name: Install Dependencies run: | @@ -36,5 +35,6 @@ jobs: - name: Update README images env: GITHUB_TOKEN: ${{ github.token }} + GOOGLE_APPLICATION_CREDENTIALS: "/tmp/gcp-key.json" run: | python .github/scripts/update_readme.py From 0f12fc4fe78867a54a6350712b166ca411cc88e0 Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Sun, 29 Dec 2024 07:06:17 +0800 Subject: [PATCH 10/16] chore: trigger readme update on label change --- .github/scripts/update_readme.py | 2 +- .github/workflows/update_readme.yml | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/scripts/update_readme.py b/.github/scripts/update_readme.py index 190ff03..d8e20f1 100644 --- a/.github/scripts/update_readme.py +++ b/.github/scripts/update_readme.py @@ -29,7 +29,7 @@ def generate_discussion_svg(title, emoji, labels, category, upvotes, comments, a ) # Build the contributor text - contributor_comment = f'GITHUB {author} started on {created_at_formatted}.' + contributor_comment = f'{author} started on {created_at_formatted}.' if last_comment_by and last_comment_at_formatted: contributor_comment = contributor_comment + f' Last comment by {last_comment_by} on {last_comment_at_formatted}.' diff --git a/.github/workflows/update_readme.yml b/.github/workflows/update_readme.yml index 42df3fd..193a97a 100644 --- a/.github/workflows/update_readme.yml +++ b/.github/workflows/update_readme.yml @@ -1,11 +1,10 @@ -name: Update README on Discussion Events +name: Update README on: discussion: - types: [created, edited] + types: [created, edited, labeled, unlabeled] discussion_comment: types: [created, edited] - push: jobs: update-readme: From 3e60b290b57fd057a5f6e4ae0f8793de4fdddf3d Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Sun, 29 Dec 2024 07:11:24 +0800 Subject: [PATCH 11/16] chore: title html escape --- .github/scripts/update_readme.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/scripts/update_readme.py b/.github/scripts/update_readme.py index d8e20f1..0925251 100644 --- a/.github/scripts/update_readme.py +++ b/.github/scripts/update_readme.py @@ -1,5 +1,6 @@ import os import requests +import html from datetime import datetime from google.cloud import storage @@ -50,7 +51,7 @@ def generate_discussion_svg(title, emoji, labels, category, upvotes, comments, a {emoji} - {title} + {html.escape(title)} From d2d2eba4b5d8b47f2c5142ca8803f20ac143377a Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Sun, 29 Dec 2024 07:25:31 +0800 Subject: [PATCH 12/16] chore: save discussion url to svg --- .github/scripts/update_readme.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/scripts/update_readme.py b/.github/scripts/update_readme.py index 0925251..ef8e997 100644 --- a/.github/scripts/update_readme.py +++ b/.github/scripts/update_readme.py @@ -13,7 +13,7 @@ if not GITHUB_TOKEN: raise ValueError("GITHUB_TOKEN environment variable is not set") -def generate_discussion_svg(title, emoji, labels, category, upvotes, comments, author, created_at, last_comment_by, last_comment_at): +def generate_discussion_svg(title, emoji, labels, category, upvotes, comments, author, created_at, last_comment_by, last_comment_at, discussion_url): width = 820 height = 120 emoji_size = 20 # 16 font size equal 20x19 px @@ -37,6 +37,7 @@ def generate_discussion_svg(title, emoji, labels, category, upvotes, comments, a # Create SVG content svg = f""" + @@ -188,7 +189,8 @@ def update_svgs(): author=discussion['author']['login'], created_at=discussion['createdAt'], last_comment_by=last_comment_by, - last_comment_at=last_comment_at + last_comment_at=last_comment_at, + discussion_url=discussion['url'] ) # Save each SVG to a unique file From 92bf1cbcde5ce5bd37e1de7e33b02e82c4132c77 Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Sun, 29 Dec 2024 08:35:15 +0800 Subject: [PATCH 13/16] chore: readme roadmap redirect server --- .github/scripts/redirect_readme/.gitignore | 2 + .github/scripts/redirect_readme/Dockerfile | 14 +++++++ .github/scripts/redirect_readme/app.py | 39 +++++++++++++++++++ .../scripts/redirect_readme/requirements.txt | 3 ++ 4 files changed, 58 insertions(+) create mode 100644 .github/scripts/redirect_readme/.gitignore create mode 100644 .github/scripts/redirect_readme/Dockerfile create mode 100644 .github/scripts/redirect_readme/app.py create mode 100644 .github/scripts/redirect_readme/requirements.txt diff --git a/.github/scripts/redirect_readme/.gitignore b/.github/scripts/redirect_readme/.gitignore new file mode 100644 index 0000000..896cdee --- /dev/null +++ b/.github/scripts/redirect_readme/.gitignore @@ -0,0 +1,2 @@ +.env/ +__pycache__/ diff --git a/.github/scripts/redirect_readme/Dockerfile b/.github/scripts/redirect_readme/Dockerfile new file mode 100644 index 0000000..5a941a2 --- /dev/null +++ b/.github/scripts/redirect_readme/Dockerfile @@ -0,0 +1,14 @@ +# Use an official lightweight Python image +FROM python:3.9-slim + +# Set the working directory in the container +WORKDIR /app + +# Copy the current directory contents into the container +COPY . . + +# Install dependencies +RUN pip install -r requirements.txt + +# Run Gunicorn to serve Flask +CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:8080", "app:app"] diff --git a/.github/scripts/redirect_readme/app.py b/.github/scripts/redirect_readme/app.py new file mode 100644 index 0000000..42dc703 --- /dev/null +++ b/.github/scripts/redirect_readme/app.py @@ -0,0 +1,39 @@ +from flask import Flask, redirect, Response +import requests +import re + +app = Flask(__name__) + +# Google Cloud Storage base URL +GCS_BASE_URL = "https://storage.googleapis.com/implot3d" + +# Route to handle discussion redirects +@app.route('/discussion_') +def redirect_to_discussion(discussion_id): + # Construct the URL for the SVG file in GCS + svg_url = f"{GCS_BASE_URL}/discussion_{discussion_id}.svg" + + # Fetch the SVG content from GCS + response = requests.get(svg_url) + + if response.status_code == 200: + svg_content = response.text + + # Extract the GitHub discussion URL from SVG comments + match = re.search(r'', svg_content) + + if match: + discussion_url = match.group(1) + # Redirect to the extracted URL + return redirect(discussion_url, code=302) + else: + # Return 404 if no URL is found in the SVG + return Response("Discussion link not found in SVG.", status=404) + else: + # Return 404 if the SVG does not exist + return Response("SVG not found.", status=404) + +# Default route +@app.route('/') +def home(): + return "Hello from implot3d! This is the redirect service." diff --git a/.github/scripts/redirect_readme/requirements.txt b/.github/scripts/redirect_readme/requirements.txt new file mode 100644 index 0000000..57cae97 --- /dev/null +++ b/.github/scripts/redirect_readme/requirements.txt @@ -0,0 +1,3 @@ +flask==3.1.0 +gunicorn==23.0.0 +requests==2.32.3 From 3fc5d124bb11697e26d6a3243cf379a88a6f2416 Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Sun, 29 Dec 2024 08:38:11 +0800 Subject: [PATCH 14/16] chore: move update_readme script --- .github/scripts/{ => update_readme}/.gitignore | 0 .github/scripts/{ => update_readme}/requirements.txt | 0 .github/scripts/{ => update_readme}/update_readme.py | 0 .github/workflows/update_readme.yml | 4 ++-- 4 files changed, 2 insertions(+), 2 deletions(-) rename .github/scripts/{ => update_readme}/.gitignore (100%) rename .github/scripts/{ => update_readme}/requirements.txt (100%) rename .github/scripts/{ => update_readme}/update_readme.py (100%) diff --git a/.github/scripts/.gitignore b/.github/scripts/update_readme/.gitignore similarity index 100% rename from .github/scripts/.gitignore rename to .github/scripts/update_readme/.gitignore diff --git a/.github/scripts/requirements.txt b/.github/scripts/update_readme/requirements.txt similarity index 100% rename from .github/scripts/requirements.txt rename to .github/scripts/update_readme/requirements.txt diff --git a/.github/scripts/update_readme.py b/.github/scripts/update_readme/update_readme.py similarity index 100% rename from .github/scripts/update_readme.py rename to .github/scripts/update_readme/update_readme.py diff --git a/.github/workflows/update_readme.yml b/.github/workflows/update_readme.yml index 193a97a..df4b78d 100644 --- a/.github/workflows/update_readme.yml +++ b/.github/workflows/update_readme.yml @@ -29,11 +29,11 @@ jobs: - name: Install Dependencies run: | - pip install -r .github/scripts/requirements.txt + pip install -r .github/scripts/update_readme/requirements.txt - name: Update README images env: GITHUB_TOKEN: ${{ github.token }} GOOGLE_APPLICATION_CREDENTIALS: "/tmp/gcp-key.json" run: | - python .github/scripts/update_readme.py + python .github/scripts/update_readme/update_readme.py From 2c62ab891951a69a63ad3673ebd93c1fa8a71457 Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Sun, 29 Dec 2024 08:49:26 +0800 Subject: [PATCH 15/16] chore: add roadmap images to README --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index 1d24127..e033c24 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,23 @@ ImPlot3D is an extension of [Dear ImGui](https://github.com/ocornut/imgui) that - Optional and configurable legends with toggle buttons to quickly show/hide plot items - Default styling based on the current ImGui theme, or completely custom plot styles +## 🚧 Feature Roadmap +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ ## 🛠️ Usage The ImPlot3D API is designed to feel very similar to Dear ImGui and ImPlot. You start by calling `ImPlot3D::BeginPlot()` to initialize a 3D plot, followed by plotting various data using the `PlotX` functions (e.g., `PlotLine()` , `PlotScatter()` , `PlotSurface()` ). Finally, you end the plot with ` ImPlot3D::EndPlot()` . From d80006b017a478b634061963db3035f50ce545b8 Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Sun, 29 Dec 2024 09:47:16 +0800 Subject: [PATCH 16/16] chore: generate discussion status SVGs --- .../scripts/update_readme/update_readme.py | 88 +++++++++++++++++-- README.md | 11 +++ 2 files changed, 94 insertions(+), 5 deletions(-) diff --git a/.github/scripts/update_readme/update_readme.py b/.github/scripts/update_readme/update_readme.py index ef8e997..5630a01 100644 --- a/.github/scripts/update_readme/update_readme.py +++ b/.github/scripts/update_readme/update_readme.py @@ -2,6 +2,7 @@ import requests import html from datetime import datetime +from collections import Counter from google.cloud import storage # GCloud @@ -13,6 +14,36 @@ if not GITHUB_TOKEN: raise ValueError("GITHUB_TOKEN environment variable is not set") +def generate_status_svg(label_text, label_color, count): + width = 140 + height = 120 + label_width = len(label_text) * 8 + 5 + + # Create SVG content + svg = f""" + + + + + + + + + + + + + + {label_text} + + + + {count} + + """ + + return svg + def generate_discussion_svg(title, emoji, labels, category, upvotes, comments, author, created_at, last_comment_by, last_comment_at, discussion_url): width = 820 height = 120 @@ -39,13 +70,13 @@ def generate_discussion_svg(title, emoji, labels, category, upvotes, comments, a - - + + - + @@ -100,7 +131,7 @@ def update_svgs(): query = """ { repository(owner: "brenocq", name: "implot3d") { - discussions(first: 5, categoryId: "DIC_kwDONQXA0M4ClSCg", orderBy: {field: UPDATED_AT, direction: DESC}) { + discussions(first: 100, categoryId: "DIC_kwDONQXA0M4ClSCg", orderBy: {field: UPDATED_AT, direction: DESC}) { nodes { title url @@ -144,13 +175,60 @@ def update_svgs(): """ response = requests.post(url, headers=headers, json={"query": query}) - data = response.json() if response.status_code == 200 and 'data' in data: discussions = data["data"]["repository"]["discussions"]["nodes"] + ############### Generate status SVGs ############### + # Count number of discussions by status + status_counter = Counter({ + 'status:idea': 0, + 'status:todo': 0, + 'status:doing': 0, + 'status:review': 0, + 'status:done': 0 + }) + for discussion in discussions: + labels = [label['name'] for label in discussion['labels']['nodes']] + for status in status_counter.keys(): + if status in labels: + status_counter[status] += 1 + # Generate SVGs for Each Status + status_colors = { + 'status:idea': '#5DADE2', + 'status:todo': '#3498DB', + 'status:doing': '#F1C40F', + 'status:review': '#E67E22', + 'status:done': '#27AE60' + } + + for status, count in status_counter.items(): + print(f"Generating SVG for: {status}") + + svg_status_output = generate_status_svg( + label_text=status, + label_color=status_colors[status], + count=count + ) + + # Save and Upload SVG + filename = f"{status.split(':')[1]}.svg" + with open(filename, "w") as f: + f.write(svg_status_output) + print(f"Saved SVG as {filename}") + + # Upload SVG to GCloud + blob = bucket.blob(filename) + blob.upload_from_filename(filename) + print(f"Uploaded {filename} to google storage") + print("-" * 60) + + ############### Generate discussion SVGs ############### + # Generate SVGs for 5 most recent discussions for i, discussion in enumerate(discussions): + if i >= 5: + break print(f"Generating SVG for: {discussion['title']}") # Calculate total comments (including replies) diff --git a/README.md b/README.md index e033c24..d7fe6d4 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,17 @@ ImPlot3D is an extension of [Dear ImGui](https://github.com/ocornut/imgui) that - Default styling based on the current ImGui theme, or completely custom plot styles ## 🚧 Feature Roadmap +- ✨ The cards below are automatically updated to reflect the discussions. +- 💡 Click a card to explore the discussion! + +
+ + + + + +
+