From 1896e57a35e34e656c560416855a9890f0644a4c Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Thu, 1 May 2025 21:53:17 -0500 Subject: [PATCH 01/45] Reduce json diff to simple passthrough --- src/github_helper/api/__init__.py | 3 --- src/github_helper/api/_audit.py | 23 +++++++++-------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/github_helper/api/__init__.py b/src/github_helper/api/__init__.py index 8b521be..e97dd41 100644 --- a/src/github_helper/api/__init__.py +++ b/src/github_helper/api/__init__.py @@ -349,7 +349,6 @@ async def fetch_json(name): url = f"https://{prefix}pypi.org/pypi/{name}/json" _logger.debug(url) try: - # TODO: probably need to check that project exists first jq_dir = ( r".releases // {} | " r"to_entries | map(" @@ -528,11 +527,9 @@ async def audit_rulesets(self, repo): _audit.remove_excluded_keys(current_rulset, excluded_keys) _audit.remove_excluded_keys(expected_ruleset, excluded_keys) - diffs = [] diffs = await _audit.json_diff( current_rulset, expected_ruleset, - diffs, ) for diff in diffs: diff["template"] = template diff --git a/src/github_helper/api/_audit.py b/src/github_helper/api/_audit.py index edbd86a..af4aaf1 100644 --- a/src/github_helper/api/_audit.py +++ b/src/github_helper/api/_audit.py @@ -1,6 +1,5 @@ from pathlib import Path - -import jsondiff as jd +from typing import Any from github_helper._services.gh import GHError from github_helper._utils import load_json @@ -56,15 +55,11 @@ def remove_excluded_keys(ruleset, excluded_keys): ruleset.pop(key) -async def json_diff(original, target, diffs): - json_diffs = jd.diff(original, target, marshal=True) - if not json_diffs: - return [] - else: - for diff_key in json_diffs: - if diff_key == "$delete": - for i in json_diffs[diff_key]: - diffs.append({"status": f"add'l. key: {i}"}) - else: - diffs.append({"status": diff_key}) - return diffs +async def json_diff(original: Any, target: Any, algo="jsondiff"): + match algo: + case "jsondiff": + import jsondiff + + return jsondiff.diff(original, target, marshal=True) + case _: + raise NotImplementedError(f"{algo} is not implemented.") From 2fb5ebd9c4e5e551fe5f1debcc7c7853d929cd59 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Thu, 1 May 2025 22:02:04 -0500 Subject: [PATCH 02/45] Factor diffs out fo _audit to cmp --- src/github_helper/api/__init__.py | 3 ++- src/github_helper/api/_audit.py | 11 ----------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/github_helper/api/__init__.py b/src/github_helper/api/__init__.py index e97dd41..f4dbbe6 100644 --- a/src/github_helper/api/__init__.py +++ b/src/github_helper/api/__init__.py @@ -11,6 +11,7 @@ import logistro import orjson +from github_helper import cmp from github_helper._services import gh as srv from github_helper._services import repos as repo_srv from github_helper._services import ssh_srv @@ -527,7 +528,7 @@ async def audit_rulesets(self, repo): _audit.remove_excluded_keys(current_rulset, excluded_keys) _audit.remove_excluded_keys(expected_ruleset, excluded_keys) - diffs = await _audit.json_diff( + diffs = await cmp.json_diff( current_rulset, expected_ruleset, ) diff --git a/src/github_helper/api/_audit.py b/src/github_helper/api/_audit.py index af4aaf1..dd9c884 100644 --- a/src/github_helper/api/_audit.py +++ b/src/github_helper/api/_audit.py @@ -1,5 +1,4 @@ from pathlib import Path -from typing import Any from github_helper._services.gh import GHError from github_helper._utils import load_json @@ -53,13 +52,3 @@ def remove_excluded_keys(ruleset, excluded_keys): for key in excluded_keys: if key in ruleset: ruleset.pop(key) - - -async def json_diff(original: Any, target: Any, algo="jsondiff"): - match algo: - case "jsondiff": - import jsondiff - - return jsondiff.diff(original, target, marshal=True) - case _: - raise NotImplementedError(f"{algo} is not implemented.") From 20969f809c89f25adb334684e2ab428d24349178 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Thu, 1 May 2025 22:03:21 -0500 Subject: [PATCH 03/45] Change name of audit-repos to audit-rulesets --- src/github_helper/_cli.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/github_helper/_cli.py b/src/github_helper/_cli.py index 1834d22..9a1bcbb 100644 --- a/src/github_helper/_cli.py +++ b/src/github_helper/_cli.py @@ -180,12 +180,12 @@ def _get_cli_args(): required=True, ) - audit_repo = subparsers.add_parser( - "audit-repo", + audit_rulesets = subparsers.add_parser( + "audit-rulesets", description="", help="Audit repo rulesets against template.", ) - audit_repo.add_argument( + audit_rulesets.add_argument( "-r", "--repo", help="Name of repository required.", @@ -269,7 +269,7 @@ async def _run_cli_async(): # noqa: C901, PLR0912, PLR0915 complex case "audit-releases": data, sadness = await gh.audit_releases(repo) data = await adpt.transform_audit_releases_data(data) - case "audit-repo": + case "audit-rulesets": data, sadness = await gh.audit_rulesets(repo) data = await adpt.transform_audit_rulesets_data(data) case "audit-versions": From f070b7108cbf88286cef18a50b32e9f6f7b00441 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Thu, 1 May 2025 22:04:40 -0500 Subject: [PATCH 04/45] Fix up test.sh --- {src/integration_test => integration_test}/test.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename {src/integration_test => integration_test}/test.sh (95%) diff --git a/src/integration_test/test.sh b/integration_test/test.sh similarity index 95% rename from src/integration_test/test.sh rename to integration_test/test.sh index a571973..4a8227a 100644 --- a/src/integration_test/test.sh +++ b/integration_test/test.sh @@ -37,7 +37,7 @@ run_basic_commands() { "releases -r $REPO" "pypi -r $REPO" "audit-releases -r $REPO" - "audit-repo -r $REPO" + "audit-rulesets -r $REPO" "audit-versions -r $REPO" ) @@ -59,7 +59,7 @@ run_pretty_commands() { "releases -r $REPO" "pypi -r $REPO" "audit-releases -r $REPO" - "audit-repo -r $REPO" + "audit-rulesets -r $REPO" "audit-versions -r $REPO" ) @@ -81,7 +81,7 @@ run_pretty_json_commands() { "releases -r $REPO" "pypi -r $REPO" "audit-releases -r $REPO" - "audit-repo -r $REPO" + "audit-rulesets -r $REPO" "audit-versions -r $REPO" ) From db374414da36ac88e97e552eed29693f8af83778 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Fri, 2 May 2025 18:29:52 -0500 Subject: [PATCH 05/45] Improve remove_excluded_keys --- src/github_helper/api/_audit.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/github_helper/api/_audit.py b/src/github_helper/api/_audit.py index 2c98561..ae2aad3 100644 --- a/src/github_helper/api/_audit.py +++ b/src/github_helper/api/_audit.py @@ -49,7 +49,9 @@ async def load_template_ruleset(path): return await load_json(path=template_path) -def remove_excluded_keys(ruleset, excluded_keys): - for key in excluded_keys: - if key in ruleset: - ruleset.pop(key) +def remove_key(d, dotted_keys): + for dotted_key in dotted_keys: + keys = dotted_key.split(".") + for key in keys[:-1]: + d = d.get(key, {}) + d.pop(keys[-1], None) From 47e9b58140d1a8245f06eacc6b6c41f6de3a376c Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Fri, 2 May 2025 19:24:54 -0500 Subject: [PATCH 06/45] Better organize call --- src/github_helper/api/__init__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/github_helper/api/__init__.py b/src/github_helper/api/__init__.py index f4dbbe6..ecfd033 100644 --- a/src/github_helper/api/__init__.py +++ b/src/github_helper/api/__init__.py @@ -482,17 +482,19 @@ async def audit_rulesets(self, repo): "repo" and owner is assumed to be the current user. """ - rulesets_jq = jq.compile("map({(.name): .id}) | add") - config_path = _TEMPLATE_PATH / "audit-config.json" - config = await load_json(config_path) _ = await self.get_user() owner, repo = self._split_full_name(repo) repo_full_name = f"{owner}/{repo}" + + config_path = _TEMPLATE_PATH / "audit-config.json" + config = await load_json(config_path) + required_ruleset_templates = _audit.get_required_rulesets( config, repo_full_name, ) + rulesets_jq = jq.compile("map({(.name): .id}) | add") endpoint = f"repos/{owner}/{repo}/rulesets" _logger.debug(f"Calling API: {endpoint}") retval, out, err = await srv.gh_api(endpoint) From e2fd5bdaedb04c8673d25196084338d5c8a5a4c8 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Fri, 2 May 2025 19:26:20 -0500 Subject: [PATCH 07/45] Refactor table generation for audit_ruleset --- src/github_helper/_adapters/api_to_cli.py | 9 +++- src/github_helper/api/__init__.py | 55 +++++++++++++---------- 2 files changed, 39 insertions(+), 25 deletions(-) diff --git a/src/github_helper/_adapters/api_to_cli.py b/src/github_helper/_adapters/api_to_cli.py index 4fc4db9..0d42037 100644 --- a/src/github_helper/_adapters/api_to_cli.py +++ b/src/github_helper/_adapters/api_to_cli.py @@ -1,4 +1,5 @@ import urllib.parse +from pprint import pformat import logistro @@ -170,8 +171,12 @@ async def transform_audit_releases_data(self, releases_data): async def transform_audit_rulesets_data(self, audit_rulesets_data): if self._json: return to_json.format_json(audit_rulesets_data, pretty=self._pretty) - for rule in audit_rulesets_data: - rule["template"] = rule["template"][:24] + + audit_rulesets_data["diffs"] = [ + pformat(d) if not isinstance(d, str) else d + for d in audit_rulesets_data.get("diffs") + ] + return to_table.format_table(audit_rulesets_data, pretty=self._pretty) async def transform_audit_versions_data(self, version_data): diff --git a/src/github_helper/api/__init__.py b/src/github_helper/api/__init__.py index ecfd033..81b2503 100644 --- a/src/github_helper/api/__init__.py +++ b/src/github_helper/api/__init__.py @@ -514,28 +514,37 @@ async def audit_rulesets(self, repo): "_links", "target", ] - result = [ - {"template": t, "status": "missing ruleset"} - for t in required_ruleset_templates - if t not in active_rulesets - ] - for template, ruleset_id in active_rulesets.items(): - json_file = f"{template}.json" - if template not in required_ruleset_templates: - result.append({"template": template, "status": "additional ruleset"}) - continue - current_rulset = await self._get_ruleset(owner, repo, ruleset_id) - expected_ruleset = await _audit.load_template_ruleset(json_file) - _audit.remove_excluded_keys(current_rulset, excluded_keys) - _audit.remove_excluded_keys(expected_ruleset, excluded_keys) - - diffs = await cmp.json_diff( - current_rulset, - expected_ruleset, + added = active_rulesets.keys() - required_ruleset_templates + missing = required_ruleset_templates - active_rulesets.keys() + overlap = required_ruleset_templates & active_rulesets.keys() + + diffs = {} + for ruleset in overlap: + gh_id = active_rulesets[ruleset] + current = await self._get_ruleset(owner, repo, gh_id) + expected = await _audit.load_template_ruleset(f"{ruleset}.json") + _audit.remove_keys(current, excluded_keys) + _audit.remove_keys(expected, excluded_keys) + + diffs[ruleset] = await cmp.json_diff( + current, + expected, ) - for diff in diffs: - diff["template"] = template - result = result + diffs - sadness = len(result) - return result, sadness + + # this doesn't really work + ret = { + "enabled rulesets": ( + list(added) + len(missing) * [None] + list(diffs.keys()) + ), + "desired rulesets": ( + len(added) * [None] + list(missing) + list(diffs.keys()) + ), + "diffs": ( + len(added) * ["extra"] + + len(missing) * ["missing"] + + list(diffs.values()) + ), + } + # links? + return ret, 0 From 9e241dd67cff08a6fd84de8a824764004d7077da Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Fri, 2 May 2025 19:26:28 -0500 Subject: [PATCH 08/45] Fix function name --- src/github_helper/api/_audit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/github_helper/api/_audit.py b/src/github_helper/api/_audit.py index ae2aad3..2a7c936 100644 --- a/src/github_helper/api/_audit.py +++ b/src/github_helper/api/_audit.py @@ -49,7 +49,7 @@ async def load_template_ruleset(path): return await load_json(path=template_path) -def remove_key(d, dotted_keys): +def remove_keys(d, dotted_keys): for dotted_key in dotted_keys: keys = dotted_key.split(".") for key in keys[:-1]: From 4ceb36009275b91f0abf34189f033b51954b4bfa Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Mon, 12 May 2025 15:18:44 -0400 Subject: [PATCH 09/45] Readd missing cmp.json_diff section and cmp/ --- src/github_helper/cmp/__init__.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/github_helper/cmp/__init__.py diff --git a/src/github_helper/cmp/__init__.py b/src/github_helper/cmp/__init__.py new file mode 100644 index 0000000..f30f7d1 --- /dev/null +++ b/src/github_helper/cmp/__init__.py @@ -0,0 +1,14 @@ +"""Functions for doing comparisons of objects.""" + +from typing import Any + + +async def json_diff(original: Any, target: Any, algo="jsondiff"): + """Conduct a tree diff using selected algorithm.""" + match algo: + case "jsondiff": + import jsondiff + + return jsondiff.diff(original, target, marshal=True) + case _: + raise NotImplementedError(f"{algo} is not implemented.") From 8a1176fe24557d547ba83b026157f29feb3c1e56 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Mon, 12 May 2025 14:26:32 -0500 Subject: [PATCH 10/45] Refactor _check_options --- src/github_helper/_adapters/api_to_cli.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/github_helper/_adapters/api_to_cli.py b/src/github_helper/_adapters/api_to_cli.py index 0d42037..a9cecf9 100644 --- a/src/github_helper/_adapters/api_to_cli.py +++ b/src/github_helper/_adapters/api_to_cli.py @@ -13,11 +13,12 @@ class GHAdapter: """Allows the CLI to transform the data as required.""" def _check_options(self, formatters): + allowed_commands = {"repos", "audit-rulesets"} formats = {k for k, v in formatters.items() if v} invalid_formats = {"html", "url"} & formats if ( (self._command == "auth-status" and formats) - or (self._command != "repos" and invalid_formats) + or (self._command not in allowed_commands and invalid_formats) or (self._command == "audit-releases" and self._json) ): raise NotImplementedError( From f22deb73857c7f551c226e829b555bfc9b18151c Mon Sep 17 00:00:00 2001 From: David Angarita Date: Mon, 12 May 2025 14:28:45 -0500 Subject: [PATCH 11/45] Create base html for rulesets --- .../_adapters/to_html/_styles/rulesets.css | 0 .../_adapters/to_html/rulesets.py | 20 +++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/github_helper/_adapters/to_html/_styles/rulesets.css create mode 100644 src/github_helper/_adapters/to_html/rulesets.py diff --git a/src/github_helper/_adapters/to_html/_styles/rulesets.css b/src/github_helper/_adapters/to_html/_styles/rulesets.css new file mode 100644 index 0000000..e69de29 diff --git a/src/github_helper/_adapters/to_html/rulesets.py b/src/github_helper/_adapters/to_html/rulesets.py new file mode 100644 index 0000000..54753df --- /dev/null +++ b/src/github_helper/_adapters/to_html/rulesets.py @@ -0,0 +1,20 @@ +from pathlib import Path + +import logistro +from htmy import html + +from github_helper._adapters.to_html import _components +from github_helper._utils import load_file + +_logger = logistro.getLogger(__name__) + +_HTML_DIR = Path(__file__).resolve().parent +_STYLES_PATH = _HTML_DIR / "_styles" + + +async def rulesets(): + _logger.debug("Building table.") + styles = html.style(await load_file(_STYLES_PATH / "rulesets.css")) + _logger.debug("Building page.") + content = [] + return await _components.render_page([styles], content) From 3ea92737d74d3b72660f797db7a926cb832e11c1 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Mon, 12 May 2025 14:30:48 -0500 Subject: [PATCH 12/45] Create repos file for template --- src/github_helper/_adapters/to_html/repos.py | 198 +++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 src/github_helper/_adapters/to_html/repos.py diff --git a/src/github_helper/_adapters/to_html/repos.py b/src/github_helper/_adapters/to_html/repos.py new file mode 100644 index 0000000..5f1be96 --- /dev/null +++ b/src/github_helper/_adapters/to_html/repos.py @@ -0,0 +1,198 @@ +from collections.abc import MutableMapping +from dataclasses import dataclass +from pathlib import Path + +import logistro +from htmy import Component, Context, component, html + +from github_helper._adapters.to_html import _components +from github_helper._utils import load_file + +_logger = logistro.getLogger(__name__) + +github_com = r"https://www.github.com" +_HTML_DIR = Path(__file__).resolve().parent +_STYLES_PATH = _HTML_DIR / "_styles" +_JS_PATH = _HTML_DIR / "_js" + + +@dataclass(frozen=True, kw_only=True, slots=True) +class RepoRow: + repo: MutableMapping + + def _error_printer(self, s): + return html.span(str(type(s).__name__), title=str(s)) + + async def htmy(self, context: Context) -> Component: # noqa: ARG002 + repo = self.repo + permission_msg = { + 0: "Can read and clone this repository.", + 1: "Can pull and also manage issues and pull requests.", + 2: "Can read, clone, and push to this repository", + 3: "Can also manage issues, pull requests, and some repository settings.", + 4: "Full access to the repository, including settings and collaborators.", + } + + _logger.debug(f"Building html row for {repo['name']}") + return html.tr( + html.td(html.span("📌" if repo["pinned"] else "")), + html.td( + html.a( + repo["owner"], + href=f"{github_com}/{repo['owner']}", + target="_blank", + ), + class_="owner", + ), + html.td(html.span("/")), + html.td( + html.a( + repo["name"], + href=f"{github_com}/{repo['owner']}/{repo['name']}", + target="_blank", + ), + class_="repo", + ), + html.td(html.span(repo["version"]), class_="text-center"), + html.td(html.span("⑂" if repo["fork"] else "")), + html.td( + html.span(repo["description"] or ""), + class_="description", + ), + html.td( + html.a( + "🔧", + href=f"{github_com}/{repo['owner']}/{repo['name']}/settings/access", + target="_blank", + ), + *[ + self._error_printer(s) + if isinstance(s, Exception) + else html.a( + s["user"], + html.sup( + str(s["permission"]), + class_="badge", + ), + href=f"{github_com}/{s['user']}", + class_="collaborator", + target="_blank", + title=permission_msg[s["permission"]], + ) + if isinstance(s, dict) + else s + for s in ( + sorted( + repo["collaborators"], + key=lambda d: d["permission"], + reverse=True, + ) + if repo["collaborators"] + and isinstance(repo["collaborators"][0], dict) + else repo["collaborators"] + ) + ], + class_="collaborators", + ), + html.td( + html.a( + "🔧", + href=f"{github_com}/{repo['owner']}/{repo['name']}", + target="_blank", + ), + *[html.span(s, class_=f"topic {s}") for s in repo["topics"]], + class_="topics", + ), + class_=( + "repo-row " + f"{repo['visibility']} " + f"{'archived' if repo['archived'] else ''}" + ), + ) + + +@component +def repo_rows(repos, context: Context) -> Component: # noqa: ARG001 + return [RepoRow(repo=repo) for repo in repos] + + +async def repos_template(repos_data): + _logger.debug("Building table.") + styles = html.style(await load_file(_STYLES_PATH / "repos.css")) + table = html.table(repo_rows(repos_data), class_="mx-auto") + modal_iframe = _components.modal( + "my-modal", + "closeModal()", + html.iframe(src="", height="500", class_="w-full"), + ) + _logger.debug("Building page.") + scripts = [ + html.script( + html.SafeStr( + "\n".join( + [ + await load_file(_JS_PATH / "repos.js"), + await load_file(_JS_PATH / "modal.js"), + ], + ), + ), + ), + ] + content = [ + html.div( + html.label( + html.input_( + type_="checkbox", + id_="toggle-public", + checked=True, + ), + " Show Public", + ), + html.label( + html.input_( + type_="checkbox", + id_="toggle-private", + checked=True, + style="margin-left:1rem;", + ), + " Show Private", + ), + html.label( + html.input_( + type_="checkbox", + id_="toggle-archive", + checked=True, + style="margin-left:1rem;", + ), + " Show Archived", + ), + html.br(), + html.label( + " Owner", + html.input_( + type_="text", + id_="owner-filter", + name="owner-filter", + placeholder="Owner", + class_="rounded shadow-sm sm:text-sm p-1", + ), + ), + html.label( + " Repo", + html.input_( + type_="text", + id_="repo-filter", + name="repo-filter", + placeholder="Repo", + class_="rounded shadow-sm sm:text-sm p-1", + ), + ), + style="margin-bottom: 1rem;", + class_="mx-auto", + id_="controls", + ), + table, + modal_iframe, + *scripts, + ] + return await _components.render_page([styles], content) From fbc3bfd10a38691045da4407bdb29c7ab790ac96 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Mon, 12 May 2025 14:31:34 -0500 Subject: [PATCH 13/45] Change function name --- src/github_helper/_adapters/to_html/rulesets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/github_helper/_adapters/to_html/rulesets.py b/src/github_helper/_adapters/to_html/rulesets.py index 54753df..285f0e4 100644 --- a/src/github_helper/_adapters/to_html/rulesets.py +++ b/src/github_helper/_adapters/to_html/rulesets.py @@ -12,7 +12,7 @@ _STYLES_PATH = _HTML_DIR / "_styles" -async def rulesets(): +async def rulesets_template(): _logger.debug("Building table.") styles = html.style(await load_file(_STYLES_PATH / "rulesets.css")) _logger.debug("Building page.") From c408ea6e17a87365ae890d04d19161ab8bfc2e66 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Mon, 12 May 2025 14:32:51 -0500 Subject: [PATCH 14/45] Move repo logic from init to repos file --- .../_adapters/to_html/__init__.py | 200 +----------------- 1 file changed, 3 insertions(+), 197 deletions(-) diff --git a/src/github_helper/_adapters/to_html/__init__.py b/src/github_helper/_adapters/to_html/__init__.py index 760d41b..b0231b4 100644 --- a/src/github_helper/_adapters/to_html/__init__.py +++ b/src/github_helper/_adapters/to_html/__init__.py @@ -1,198 +1,4 @@ -from collections.abc import MutableMapping -from dataclasses import dataclass -from pathlib import Path +from .repos import repos_template +from .rulesets import rulesets_template -import logistro -from htmy import Component, Context, component, html - -from github_helper._adapters.to_html import _components -from github_helper._utils import load_file - -_logger = logistro.getLogger(__name__) - -github_com = r"https://www.github.com" -_HTML_DIR = Path(__file__).resolve().parent -_STYLES_PATH = _HTML_DIR / "_styles" -_JS_PATH = _HTML_DIR / "_js" - - -@dataclass(frozen=True, kw_only=True, slots=True) -class RepoRow: - repo: MutableMapping - - def _error_printer(self, s): - return html.span(str(type(s).__name__), title=str(s)) - - async def htmy(self, context: Context) -> Component: # noqa: ARG002 - repo = self.repo - permission_msg = { - 0: "Can read and clone this repository.", - 1: "Can pull and also manage issues and pull requests.", - 2: "Can read, clone, and push to this repository", - 3: "Can also manage issues, pull requests, and some repository settings.", - 4: "Full access to the repository, including settings and collaborators.", - } - - _logger.debug(f"Building html row for {repo['name']}") - return html.tr( - html.td(html.span("📌" if repo["pinned"] else "")), - html.td( - html.a( - repo["owner"], - href=f"{github_com}/{repo['owner']}", - target="_blank", - ), - class_="owner", - ), - html.td(html.span("/")), - html.td( - html.a( - repo["name"], - href=f"{github_com}/{repo['owner']}/{repo['name']}", - target="_blank", - ), - class_="repo", - ), - html.td(html.span(repo["version"]), class_="text-center"), - html.td(html.span("⑂" if repo["fork"] else "")), - html.td( - html.span(repo["description"] or ""), - class_="description", - ), - html.td( - html.a( - "🔧", - href=f"{github_com}/{repo['owner']}/{repo['name']}/settings/access", - target="_blank", - ), - *[ - self._error_printer(s) - if isinstance(s, Exception) - else html.a( - s["user"], - html.sup( - str(s["permission"]), - class_="badge", - ), - href=f"{github_com}/{s['user']}", - class_="collaborator", - target="_blank", - title=permission_msg[s["permission"]], - ) - if isinstance(s, dict) - else s - for s in ( - sorted( - repo["collaborators"], - key=lambda d: d["permission"], - reverse=True, - ) - if repo["collaborators"] - and isinstance(repo["collaborators"][0], dict) - else repo["collaborators"] - ) - ], - class_="collaborators", - ), - html.td( - html.a( - "🔧", - href=f"{github_com}/{repo['owner']}/{repo['name']}", - target="_blank", - ), - *[html.span(s, class_=f"topic {s}") for s in repo["topics"]], - class_="topics", - ), - class_=( - "repo-row " - f"{repo['visibility']} " - f"{'archived' if repo['archived'] else ''}" - ), - ) - - -@component -def repo_rows(repos, context: Context) -> Component: # noqa: ARG001 - return [RepoRow(repo=repo) for repo in repos] - - -async def repos(repos_data): - _logger.debug("Building table.") - styles = html.style(await load_file(_STYLES_PATH / "repos.css")) - table = html.table(repo_rows(repos_data), class_="mx-auto") - modal_iframe = _components.modal( - "my-modal", - "closeModal()", - html.iframe(src="", height="500", class_="w-full"), - ) - _logger.debug("Building page.") - scripts = [ - html.script( - html.SafeStr( - "\n".join( - [ - await load_file(_JS_PATH / "repos.js"), - await load_file(_JS_PATH / "modal.js"), - ], - ), - ), - ), - ] - content = [ - html.div( - html.label( - html.input_( - type_="checkbox", - id_="toggle-public", - checked=True, - ), - " Show Public", - ), - html.label( - html.input_( - type_="checkbox", - id_="toggle-private", - checked=True, - style="margin-left:1rem;", - ), - " Show Private", - ), - html.label( - html.input_( - type_="checkbox", - id_="toggle-archive", - checked=True, - style="margin-left:1rem;", - ), - " Show Archived", - ), - html.br(), - html.label( - " Owner", - html.input_( - type_="text", - id_="owner-filter", - name="owner-filter", - placeholder="Owner", - class_="rounded shadow-sm sm:text-sm p-1", - ), - ), - html.label( - " Repo", - html.input_( - type_="text", - id_="repo-filter", - name="repo-filter", - placeholder="Repo", - class_="rounded shadow-sm sm:text-sm p-1", - ), - ), - style="margin-bottom: 1rem;", - class_="mx-auto", - id_="controls", - ), - table, - modal_iframe, - *scripts, - ] - return await _components.render_page([styles], content) +__all__ = ["repos_template", "rulesets_template"] From 29ba06102a97067617a2c4125e49ca3a323fdae9 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Mon, 12 May 2025 14:34:20 -0500 Subject: [PATCH 15/45] Rename repo call for repo template --- src/github_helper/_adapters/api_to_cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/github_helper/_adapters/api_to_cli.py b/src/github_helper/_adapters/api_to_cli.py index a9cecf9..8e4f1fc 100644 --- a/src/github_helper/_adapters/api_to_cli.py +++ b/src/github_helper/_adapters/api_to_cli.py @@ -76,7 +76,7 @@ async def transform_scopes_data(self, scopes_data): async def transform_repos_data(self, repos_data): if self._html: - generated_html = str(await to_html.repos(repos_data)) + generated_html = str(await to_html.repos_template(repos_data)) if not self._url: return generated_html encoded = urllib.parse.quote(generated_html) From 7e5ddc3da5d913e768240bfe9cdd91cf0b0c8a2f Mon Sep 17 00:00:00 2001 From: David Angarita Date: Mon, 12 May 2025 14:35:50 -0500 Subject: [PATCH 16/45] Implement html for rulesets --- src/github_helper/_adapters/api_to_cli.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/github_helper/_adapters/api_to_cli.py b/src/github_helper/_adapters/api_to_cli.py index 8e4f1fc..0583b23 100644 --- a/src/github_helper/_adapters/api_to_cli.py +++ b/src/github_helper/_adapters/api_to_cli.py @@ -170,6 +170,12 @@ async def transform_audit_releases_data(self, releases_data): ) async def transform_audit_rulesets_data(self, audit_rulesets_data): + if self._html: + generated_html = str( + await to_html.rulesets_template(audit_rulesets_data), + ) + return generated_html + if self._json: return to_json.format_json(audit_rulesets_data, pretty=self._pretty) From 03f88d8741421b155f06ad298a4209065db6b63c Mon Sep 17 00:00:00 2001 From: David Angarita Date: Mon, 12 May 2025 14:36:58 -0500 Subject: [PATCH 17/45] Create RulesetRow class --- .../_adapters/to_html/rulesets.py | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/github_helper/_adapters/to_html/rulesets.py b/src/github_helper/_adapters/to_html/rulesets.py index 285f0e4..0676282 100644 --- a/src/github_helper/_adapters/to_html/rulesets.py +++ b/src/github_helper/_adapters/to_html/rulesets.py @@ -1,7 +1,9 @@ +from collections.abc import MutableMapping +from dataclasses import dataclass from pathlib import Path import logistro -from htmy import html +from htmy import Component, Context, component, html from github_helper._adapters.to_html import _components from github_helper._utils import load_file @@ -12,9 +14,27 @@ _STYLES_PATH = _HTML_DIR / "_styles" -async def rulesets_template(): +@dataclass(frozen=True, kw_only=True, slots=True) +class RulesetRow: + ruleset: MutableMapping + + def _error_printer(self, s): + return html.span(str(type(s).__name__), title=str(s)) + + async def htmy(self, context: Context) -> Component: # noqa: ARG002 + ruleset = self.ruleset + return html.tr(html.td(ruleset.get("status"))) + + +@component +def rulesets_rows(rulesets, context: Context) -> Component: # noqa: ARG001 + return [RulesetRow(ruleset=r) for r in rulesets] + + +async def rulesets_template(rulesets_data): _logger.debug("Building table.") styles = html.style(await load_file(_STYLES_PATH / "rulesets.css")) + table = html.table(rulesets_rows(rulesets_data), class_="mx-auto") _logger.debug("Building page.") - content = [] + content = [table] return await _components.render_page([styles], content) From 728b0030fc77bb825992afd60078e856630bde3d Mon Sep 17 00:00:00 2001 From: David Angarita Date: Mon, 12 May 2025 14:39:59 -0500 Subject: [PATCH 18/45] Create common styles --- .../_adapters/to_html/_styles/common.css | 16 ++++++++++++++++ .../_adapters/to_html/_styles/repos.css | 17 ----------------- src/github_helper/_adapters/to_html/repos.py | 7 +++++-- src/github_helper/_adapters/to_html/rulesets.py | 7 +++++-- 4 files changed, 26 insertions(+), 21 deletions(-) create mode 100644 src/github_helper/_adapters/to_html/_styles/common.css diff --git a/src/github_helper/_adapters/to_html/_styles/common.css b/src/github_helper/_adapters/to_html/_styles/common.css new file mode 100644 index 0000000..dd1eb2a --- /dev/null +++ b/src/github_helper/_adapters/to_html/_styles/common.css @@ -0,0 +1,16 @@ +body { + overflow-x: auto; + width: 100%; +} + +table { + width: max-content; +} + +tr.even { + background-color: #f0f0f0; +} + +tr.odd { + background-color: #ffffff; +} diff --git a/src/github_helper/_adapters/to_html/_styles/repos.css b/src/github_helper/_adapters/to_html/_styles/repos.css index 77dd063..0cea605 100644 --- a/src/github_helper/_adapters/to_html/_styles/repos.css +++ b/src/github_helper/_adapters/to_html/_styles/repos.css @@ -26,23 +26,6 @@ tr.repo-row td a:active { color: black; } -body { - overflow-x: auto; - width: 100%; -} - -table { - width: max-content; -} - -tr.even { - background-color: #f0f0f0; -} - -tr.odd { - background-color: #ffffff; -} - tr.repo-row.private { font-weight: 350; } diff --git a/src/github_helper/_adapters/to_html/repos.py b/src/github_helper/_adapters/to_html/repos.py index 5f1be96..43e74bf 100644 --- a/src/github_helper/_adapters/to_html/repos.py +++ b/src/github_helper/_adapters/to_html/repos.py @@ -118,7 +118,10 @@ def repo_rows(repos, context: Context) -> Component: # noqa: ARG001 async def repos_template(repos_data): _logger.debug("Building table.") - styles = html.style(await load_file(_STYLES_PATH / "repos.css")) + styles = [ + html.style(await load_file(_STYLES_PATH / "common.css")), + html.style(await load_file(_STYLES_PATH / "repos.css")), + ] table = html.table(repo_rows(repos_data), class_="mx-auto") modal_iframe = _components.modal( "my-modal", @@ -195,4 +198,4 @@ async def repos_template(repos_data): modal_iframe, *scripts, ] - return await _components.render_page([styles], content) + return await _components.render_page([*styles], content) diff --git a/src/github_helper/_adapters/to_html/rulesets.py b/src/github_helper/_adapters/to_html/rulesets.py index 0676282..5028116 100644 --- a/src/github_helper/_adapters/to_html/rulesets.py +++ b/src/github_helper/_adapters/to_html/rulesets.py @@ -33,8 +33,11 @@ def rulesets_rows(rulesets, context: Context) -> Component: # noqa: ARG001 async def rulesets_template(rulesets_data): _logger.debug("Building table.") - styles = html.style(await load_file(_STYLES_PATH / "rulesets.css")) + styles = [ + html.style(await load_file(_STYLES_PATH / "common.css")), + html.style(await load_file(_STYLES_PATH / "rulesets.css")), + ] table = html.table(rulesets_rows(rulesets_data), class_="mx-auto") _logger.debug("Building page.") content = [table] - return await _components.render_page([styles], content) + return await _components.render_page([*styles], content) From 69bc1b45f2784e2b03e927339b2c74392a4f4230 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Mon, 12 May 2025 14:40:45 -0500 Subject: [PATCH 19/45] Add html url option for rulesets --- src/github_helper/_adapters/api_to_cli.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/github_helper/_adapters/api_to_cli.py b/src/github_helper/_adapters/api_to_cli.py index 0583b23..4cefa5a 100644 --- a/src/github_helper/_adapters/api_to_cli.py +++ b/src/github_helper/_adapters/api_to_cli.py @@ -174,7 +174,10 @@ async def transform_audit_rulesets_data(self, audit_rulesets_data): generated_html = str( await to_html.rulesets_template(audit_rulesets_data), ) - return generated_html + if not self._url: + return generated_html + encoded = urllib.parse.quote(generated_html) + return f"data:text/html;charset=utf-8,{encoded}" if self._json: return to_json.format_json(audit_rulesets_data, pretty=self._pretty) From a967cabed77396487bc41e5a8f9cf4d4790c7861 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Mon, 12 May 2025 15:02:53 -0500 Subject: [PATCH 20/45] Add ipdb dev dependency --- pyproject.toml | 1 + uv.lock | 191 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 8292b15..e02d7d9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,7 @@ dev = [ "poethepoet>=0.30.0", "types-tabulate>=0.9.0.20241207", "types-aiofiles>=24.1.0.20250326", + "ipdb>=0.13.13", ] #docs = [ diff --git a/uv.lock b/uv.lock index 3e9226d..f4bb3dd 100644 --- a/uv.lock +++ b/uv.lock @@ -95,6 +95,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 }, ] +[[package]] +name = "asttokens" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918 }, +] + [[package]] name = "async-lru" version = "2.0.5" @@ -131,6 +140,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/71/52/990d8b2a888be9fe1022eb89884849547e6341a8bec0072605fd70483db8/colored-2.3.0-py3-none-any.whl", hash = "sha256:2f7f455dc2ed490531b0b8e0ecf8f8513bb28a150d9f12f0e48b023641a55185", size = 18333 }, ] +[[package]] +name = "decorator" +version = "5.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190 }, +] + [[package]] name = "execnet" version = "2.1.1" @@ -140,6 +158,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/43/09/2aea36ff60d16dd8879bdb2f5b3ee0ba8d08cbbdcdfe870e695ce3784385/execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc", size = 40612 }, ] +[[package]] +name = "executing" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/50/a9d80c47ff289c611ff12e63f7c5d13942c65d68125160cefd768c73e6e4/executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755", size = 978693 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", size = 26702 }, +] + [[package]] name = "frozenlist" version = "1.6.0" @@ -219,6 +246,7 @@ dependencies = [ [package.dev-dependencies] dev = [ + { name = "ipdb" }, { name = "mypy" }, { name = "poethepoet" }, { name = "pytest" }, @@ -244,6 +272,7 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ + { name = "ipdb", specifier = ">=0.13.13" }, { name = "mypy", specifier = ">=1.14.1" }, { name = "poethepoet", specifier = ">=0.30.0" }, { name = "pytest", specifier = ">=8.3.4" }, @@ -284,6 +313,64 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 }, ] +[[package]] +name = "ipdb" +version = "0.13.13" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "decorator" }, + { name = "ipython" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/1b/7e07e7b752017f7693a0f4d41c13e5ca29ce8cbcfdcc1fd6c4ad8c0a27a0/ipdb-0.13.13.tar.gz", hash = "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726", size = 17042 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/4c/b075da0092003d9a55cf2ecc1cae9384a1ca4f650d51b00fc59875fe76f6/ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4", size = 12130 }, +] + +[[package]] +name = "ipython" +version = "9.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "decorator" }, + { name = "ipython-pygments-lexers" }, + { name = "jedi" }, + { name = "matplotlib-inline" }, + { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit" }, + { name = "pygments" }, + { name = "stack-data" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/02/63a84444a7409b3c0acd1de9ffe524660e0e5d82ee473e78b45e5bfb64a4/ipython-9.2.0.tar.gz", hash = "sha256:62a9373dbc12f28f9feaf4700d052195bf89806279fc8ca11f3f54017d04751b", size = 4424394 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/ce/5e897ee51b7d26ab4e47e5105e7368d40ce6cfae2367acdf3165396d50be/ipython-9.2.0-py3-none-any.whl", hash = "sha256:fef5e33c4a1ae0759e0bba5917c9db4eb8c53fee917b6a526bd973e1ca5159f6", size = 604277 }, +] + +[[package]] +name = "ipython-pygments-lexers" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074 }, +] + +[[package]] +name = "jedi" +version = "0.19.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 }, +] + [[package]] name = "jq" version = "1.8.0" @@ -342,6 +429,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/51/3f/afe76f8e2246ffbc867440cbcf90525264df0e658f8a5ca1f872b3f6192a/markdown-3.8-py3-none-any.whl", hash = "sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc", size = 106210 }, ] +[[package]] +name = "matplotlib-inline" +version = "0.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899 }, +] + [[package]] name = "multidict" version = "6.4.3" @@ -481,6 +580,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, ] +[[package]] +name = "parso" +version = "0.8.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650 }, +] + [[package]] name = "pastel" version = "0.2.1" @@ -490,6 +598,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/aa/18/a8444036c6dd65ba3624c63b734d3ba95ba63ace513078e1580590075d21/pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364", size = 5955 }, ] +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, +] + [[package]] name = "pluggy" version = "1.5.0" @@ -512,6 +632,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/da/d1/61431afe22577083fcb50614bc5e5aa73aa0ab35e3fc2ae49708a59ff70b/poethepoet-0.34.0-py3-none-any.whl", hash = "sha256:c472d6f0fdb341b48d346f4ccd49779840c15b30dfd6bc6347a80d6274b5e34e", size = 85851 }, ] +[[package]] +name = "prompt-toolkit" +version = "3.0.51" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bb/6e/9d084c929dfe9e3bfe0c6a47e31f78a25c54627d64a66e884a8bf5474f1c/prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed", size = 428940 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/4f/5249960887b1fbe561d9ff265496d170b55a735b76724f10ef19f9e40716/prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07", size = 387810 }, +] + [[package]] name = "propcache" version = "0.3.1" @@ -569,6 +701,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b8/d3/c3cb8f1d6ae3b37f83e1de806713a9b3642c5895f0215a62e1a4bd6e5e34/propcache-0.3.1-py3-none-any.whl", hash = "sha256:9a8ecf38de50a7f518c21568c80f985e776397b902f1ce0b01f799aba1608b40", size = 12376 }, ] +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, +] + +[[package]] +name = "pygments" +version = "2.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, +] + [[package]] name = "pytest" version = "8.3.5" @@ -641,6 +800,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, ] +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, +] + [[package]] name = "tabulate" version = "0.9.0" @@ -650,6 +823,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 }, ] +[[package]] +name = "traitlets" +version = "5.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, +] + [[package]] name = "types-aiofiles" version = "24.1.0.20250326" @@ -677,6 +859,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 }, ] +[[package]] +name = "wcwidth" +version = "0.2.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, +] + [[package]] name = "yarl" version = "1.20.0" From a60592d620fe5a4e7f83343a33c8b1a474f06bf7 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Mon, 12 May 2025 15:31:02 -0500 Subject: [PATCH 21/45] Add logic base for rulesets table --- .../_adapters/to_html/rulesets.py | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/github_helper/_adapters/to_html/rulesets.py b/src/github_helper/_adapters/to_html/rulesets.py index 5028116..46b0293 100644 --- a/src/github_helper/_adapters/to_html/rulesets.py +++ b/src/github_helper/_adapters/to_html/rulesets.py @@ -5,6 +5,7 @@ import logistro from htmy import Component, Context, component, html +from github_helper._adapters import to_json from github_helper._adapters.to_html import _components from github_helper._utils import load_file @@ -23,12 +24,32 @@ def _error_printer(self, s): async def htmy(self, context: Context) -> Component: # noqa: ARG002 ruleset = self.ruleset - return html.tr(html.td(ruleset.get("status"))) + return html.tr( + html.td(ruleset["enabled_ruleset"] or ""), + html.td(ruleset["desired_ruleset"] or ""), + html.td( + html.pre( + to_json.format_json( + ruleset["diff"], + pretty=True, + ), + ), + ), + ) @component def rulesets_rows(rulesets, context: Context) -> Component: # noqa: ARG001 - return [RulesetRow(ruleset=r) for r in rulesets] + return [ + RulesetRow( + ruleset={ + "enabled_ruleset": rulesets["enabled rulesets"][r], + "desired_ruleset": rulesets["desired rulesets"][r], + "diff": rulesets["diffs"][r], + }, + ) + for r in range(len(rulesets["enabled rulesets"])) + ] async def rulesets_template(rulesets_data): From 2140ef64e20affc429fe88ffe1a9af9f108544d6 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Tue, 13 May 2025 15:46:38 -0500 Subject: [PATCH 22/45] Add table headers --- src/github_helper/_adapters/to_html/rulesets.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/github_helper/_adapters/to_html/rulesets.py b/src/github_helper/_adapters/to_html/rulesets.py index 46b0293..74ce89d 100644 --- a/src/github_helper/_adapters/to_html/rulesets.py +++ b/src/github_helper/_adapters/to_html/rulesets.py @@ -58,7 +58,17 @@ async def rulesets_template(rulesets_data): html.style(await load_file(_STYLES_PATH / "common.css")), html.style(await load_file(_STYLES_PATH / "rulesets.css")), ] - table = html.table(rulesets_rows(rulesets_data), class_="mx-auto") + table = html.table( + html.thead( + html.tr( + html.th("Enabled Rulesets", colspan=1), + html.th("Desired Rulesets", colspan=1), + html.th("Diffs", colspan=1), + ), + ), + html.tbody(rulesets_rows(rulesets_data)), + class_="mx-auto", + ) _logger.debug("Building page.") content = [table] return await _components.render_page([*styles], content) From 4c7517b0b562e94e56813a865821885656decf51 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Tue, 13 May 2025 16:53:37 -0500 Subject: [PATCH 23/45] Add styles for table and theads --- .../_adapters/to_html/_styles/rulesets.css | 7 +++++++ src/github_helper/_adapters/to_html/rulesets.py | 16 ++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/github_helper/_adapters/to_html/_styles/rulesets.css b/src/github_helper/_adapters/to_html/_styles/rulesets.css index e69de29..2eeadaa 100644 --- a/src/github_helper/_adapters/to_html/_styles/rulesets.css +++ b/src/github_helper/_adapters/to_html/_styles/rulesets.css @@ -0,0 +1,7 @@ +.table { + @apply table-auto w-11/12 mx-auto my-8 border border-gray-300 shadow-md rounded-md overflow-hidden text-sm; +} + +.table-header { + @apply text-center px-4 py-2 text-sm font-semibold text-gray-700 bg-gray-100 border-r; +} diff --git a/src/github_helper/_adapters/to_html/rulesets.py b/src/github_helper/_adapters/to_html/rulesets.py index 74ce89d..a4636f3 100644 --- a/src/github_helper/_adapters/to_html/rulesets.py +++ b/src/github_helper/_adapters/to_html/rulesets.py @@ -56,19 +56,23 @@ async def rulesets_template(rulesets_data): _logger.debug("Building table.") styles = [ html.style(await load_file(_STYLES_PATH / "common.css")), - html.style(await load_file(_STYLES_PATH / "rulesets.css")), + html.style( + await load_file(_STYLES_PATH / "rulesets.css"), + type="text/tailwindcss", + ), ] table = html.table( html.thead( html.tr( - html.th("Enabled Rulesets", colspan=1), - html.th("Desired Rulesets", colspan=1), - html.th("Diffs", colspan=1), + html.th("Enabled Rulesets", class_="table-header"), + html.th("Desired Rulesets", class_="table-header"), + html.th("Diffs", class_="table-header"), ), ), - html.tbody(rulesets_rows(rulesets_data)), - class_="mx-auto", + html.tbody(rulesets_rows(rulesets_data), class_="algo"), + class_="table", ) + _logger.debug("Building page.") content = [table] return await _components.render_page([*styles], content) From 2c079dfc12b6861ee4e4fda28f317cb7cc9950a3 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Tue, 13 May 2025 17:47:09 -0500 Subject: [PATCH 24/45] Remove test class --- src/github_helper/_adapters/to_html/rulesets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/github_helper/_adapters/to_html/rulesets.py b/src/github_helper/_adapters/to_html/rulesets.py index a4636f3..dae8f6f 100644 --- a/src/github_helper/_adapters/to_html/rulesets.py +++ b/src/github_helper/_adapters/to_html/rulesets.py @@ -69,7 +69,7 @@ async def rulesets_template(rulesets_data): html.th("Diffs", class_="table-header"), ), ), - html.tbody(rulesets_rows(rulesets_data), class_="algo"), + html.tbody(rulesets_rows(rulesets_data)), class_="table", ) From cf76abf65c90e139b5c7832a4bb3c131559b6b1b Mon Sep 17 00:00:00 2001 From: David Angarita Date: Tue, 13 May 2025 17:50:39 -0500 Subject: [PATCH 25/45] Add table col class --- src/github_helper/_adapters/to_html/_styles/rulesets.css | 4 ++++ src/github_helper/_adapters/to_html/rulesets.py | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/github_helper/_adapters/to_html/_styles/rulesets.css b/src/github_helper/_adapters/to_html/_styles/rulesets.css index 2eeadaa..1c68c4b 100644 --- a/src/github_helper/_adapters/to_html/_styles/rulesets.css +++ b/src/github_helper/_adapters/to_html/_styles/rulesets.css @@ -5,3 +5,7 @@ .table-header { @apply text-center px-4 py-2 text-sm font-semibold text-gray-700 bg-gray-100 border-r; } + +.table-col { + @apply px-4 py-2 align-top border-t border-gray-200 border-r; +} diff --git a/src/github_helper/_adapters/to_html/rulesets.py b/src/github_helper/_adapters/to_html/rulesets.py index dae8f6f..f16be32 100644 --- a/src/github_helper/_adapters/to_html/rulesets.py +++ b/src/github_helper/_adapters/to_html/rulesets.py @@ -25,8 +25,8 @@ def _error_printer(self, s): async def htmy(self, context: Context) -> Component: # noqa: ARG002 ruleset = self.ruleset return html.tr( - html.td(ruleset["enabled_ruleset"] or ""), - html.td(ruleset["desired_ruleset"] or ""), + html.td(ruleset["enabled_ruleset"] or "", class_="table-col"), + html.td(ruleset["desired_ruleset"] or "", class_="table-col"), html.td( html.pre( to_json.format_json( From e4c098ceca4e10a43baafa8a4dfab1a0e26c11d9 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Tue, 13 May 2025 17:51:22 -0500 Subject: [PATCH 26/45] Add table col code class --- .../_adapters/to_html/_styles/rulesets.css | 4 ++++ src/github_helper/_adapters/to_html/rulesets.py | 11 ++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/github_helper/_adapters/to_html/_styles/rulesets.css b/src/github_helper/_adapters/to_html/_styles/rulesets.css index 1c68c4b..9e1eec1 100644 --- a/src/github_helper/_adapters/to_html/_styles/rulesets.css +++ b/src/github_helper/_adapters/to_html/_styles/rulesets.css @@ -9,3 +9,7 @@ .table-col { @apply px-4 py-2 align-top border-t border-gray-200 border-r; } + +.table-col--code { + @apply bg-gray-900 text-green-300 text-xs p-3 rounded-md overflow-x-auto font-mono border border-gray-700; +} diff --git a/src/github_helper/_adapters/to_html/rulesets.py b/src/github_helper/_adapters/to_html/rulesets.py index f16be32..338504b 100644 --- a/src/github_helper/_adapters/to_html/rulesets.py +++ b/src/github_helper/_adapters/to_html/rulesets.py @@ -29,11 +29,12 @@ async def htmy(self, context: Context) -> Component: # noqa: ARG002 html.td(ruleset["desired_ruleset"] or "", class_="table-col"), html.td( html.pre( - to_json.format_json( - ruleset["diff"], - pretty=True, - ), - ), + to_json.format_json(ruleset["diff"], pretty=True), + class_="table-col--code", + ) + if isinstance(ruleset["diff"], dict) + else ruleset["diff"], + class_="table-col", ), ) From d2736bddbb366ed87e3d9e46cc41f4ebf19a2bf6 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Tue, 13 May 2025 18:19:26 -0500 Subject: [PATCH 27/45] Change table-col-code styles --- src/github_helper/_adapters/to_html/_styles/rulesets.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/github_helper/_adapters/to_html/_styles/rulesets.css b/src/github_helper/_adapters/to_html/_styles/rulesets.css index 9e1eec1..ea2d5b4 100644 --- a/src/github_helper/_adapters/to_html/_styles/rulesets.css +++ b/src/github_helper/_adapters/to_html/_styles/rulesets.css @@ -11,5 +11,5 @@ } .table-col--code { - @apply bg-gray-900 text-green-300 text-xs p-3 rounded-md overflow-x-auto font-mono border border-gray-700; + @apply bg-gray-50 text-gray-800 text-xs p-3 rounded-md overflow-x-auto font-mono border border-gray-300; } From a79af0588dc6957436eb18bf4ee895b6c2b63b4c Mon Sep 17 00:00:00 2001 From: David Angarita Date: Tue, 13 May 2025 19:01:35 -0500 Subject: [PATCH 28/45] Implement highlighjs library for json --- .../_adapters/to_html/rulesets.py | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/github_helper/_adapters/to_html/rulesets.py b/src/github_helper/_adapters/to_html/rulesets.py index 338504b..8ad3084 100644 --- a/src/github_helper/_adapters/to_html/rulesets.py +++ b/src/github_helper/_adapters/to_html/rulesets.py @@ -13,6 +13,7 @@ _HTML_DIR = Path(__file__).resolve().parent _STYLES_PATH = _HTML_DIR / "_styles" +_HLJS_URL = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1" @dataclass(frozen=True, kw_only=True, slots=True) @@ -29,8 +30,13 @@ async def htmy(self, context: Context) -> Component: # noqa: ARG002 html.td(ruleset["desired_ruleset"] or "", class_="table-col"), html.td( html.pre( - to_json.format_json(ruleset["diff"], pretty=True), - class_="table-col--code", + html.code( + to_json.format_json( + ruleset["diff"], + pretty=True, + ), + class_="language-json", + ), ) if isinstance(ruleset["diff"], dict) else ruleset["diff"], @@ -61,6 +67,10 @@ async def rulesets_template(rulesets_data): await load_file(_STYLES_PATH / "rulesets.css"), type="text/tailwindcss", ), + html.link( + href=f"{_HLJS_URL}/styles/default.min.css", + rel="stylesheet", + ), ] table = html.table( html.thead( @@ -73,7 +83,10 @@ async def rulesets_template(rulesets_data): html.tbody(rulesets_rows(rulesets_data)), class_="table", ) - + scripts = [ + html.script(src=f"{_HLJS_URL}/highlight.min.js"), + html.script(html.SafeStr("hljs.highlightAll();")), + ] _logger.debug("Building page.") - content = [table] + content = [table, *scripts] return await _components.render_page([*styles], content) From 01fdebd3344d7d31ec8c66b27b195962c4587c38 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Wed, 14 May 2025 10:20:11 -0500 Subject: [PATCH 29/45] Add headers for html table --- src/github_helper/_adapters/to_html/repos.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/github_helper/_adapters/to_html/repos.py b/src/github_helper/_adapters/to_html/repos.py index 43e74bf..1fa64bc 100644 --- a/src/github_helper/_adapters/to_html/repos.py +++ b/src/github_helper/_adapters/to_html/repos.py @@ -122,7 +122,19 @@ async def repos_template(repos_data): html.style(await load_file(_STYLES_PATH / "common.css")), html.style(await load_file(_STYLES_PATH / "repos.css")), ] - table = html.table(repo_rows(repos_data), class_="mx-auto") + table = html.table( + html.thead( + html.tr( + html.th("Repository", colspan=4), + html.th("Head Tag", colspan=2), + html.th("Description", colspan=1), + html.th("Collaborators", colspan=1), + html.th("Topics", colspan=1), + ), + ), + html.tbody(repo_rows(repos_data)), + class_="mx-auto", + ) modal_iframe = _components.modal( "my-modal", "closeModal()", From 19fc384b54bacb6561ea6ab359597a7c294213bd Mon Sep 17 00:00:00 2001 From: David Angarita Date: Wed, 14 May 2025 10:23:16 -0500 Subject: [PATCH 30/45] Make the search bar sticky --- src/github_helper/_adapters/to_html/repos.py | 90 ++++++++++---------- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/src/github_helper/_adapters/to_html/repos.py b/src/github_helper/_adapters/to_html/repos.py index 1fa64bc..2e3bf56 100644 --- a/src/github_helper/_adapters/to_html/repos.py +++ b/src/github_helper/_adapters/to_html/repos.py @@ -155,56 +155,58 @@ async def repos_template(repos_data): ] content = [ html.div( - html.label( - html.input_( - type_="checkbox", - id_="toggle-public", - checked=True, + html.div( + html.label( + html.input_( + type_="checkbox", + id_="toggle-public", + checked=True, + ), + " Show Public", ), - " Show Public", - ), - html.label( - html.input_( - type_="checkbox", - id_="toggle-private", - checked=True, - style="margin-left:1rem;", + html.label( + html.input_( + type_="checkbox", + id_="toggle-private", + checked=True, + style="margin-left:1rem;", + ), + " Show Private", ), - " Show Private", - ), - html.label( - html.input_( - type_="checkbox", - id_="toggle-archive", - checked=True, - style="margin-left:1rem;", + html.label( + html.input_( + type_="checkbox", + id_="toggle-archive", + checked=True, + style="margin-left:1rem;", + ), + " Show Archived", ), - " Show Archived", - ), - html.br(), - html.label( - " Owner", - html.input_( - type_="text", - id_="owner-filter", - name="owner-filter", - placeholder="Owner", - class_="rounded shadow-sm sm:text-sm p-1", + html.br(), + html.label( + " Owner", + html.input_( + type_="text", + id_="owner-filter", + name="owner-filter", + placeholder="Owner", + class_="rounded p-1", + ), ), - ), - html.label( - " Repo", - html.input_( - type_="text", - id_="repo-filter", - name="repo-filter", - placeholder="Repo", - class_="rounded shadow-sm sm:text-sm p-1", + html.label( + " Repo", + html.input_( + type_="text", + id_="repo-filter", + name="repo-filter", + placeholder="Repo", + class_="rounded p-1", + ), ), + class_="mx-auto", + id_="controls", ), - style="margin-bottom: 1rem;", - class_="mx-auto", - id_="controls", + class_="sticky top-0 w-full bg-slate-300 p-2 z-10", ), table, modal_iframe, From d61f1f6e53b89123f52eee9287635cc9bf709d4f Mon Sep 17 00:00:00 2001 From: David Angarita Date: Wed, 14 May 2025 10:25:06 -0500 Subject: [PATCH 31/45] Add better styles for filters --- src/github_helper/_adapters/to_html/repos.py | 83 ++++++++++---------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/src/github_helper/_adapters/to_html/repos.py b/src/github_helper/_adapters/to_html/repos.py index 2e3bf56..fd2baab 100644 --- a/src/github_helper/_adapters/to_html/repos.py +++ b/src/github_helper/_adapters/to_html/repos.py @@ -156,52 +156,55 @@ async def repos_template(repos_data): content = [ html.div( html.div( - html.label( - html.input_( - type_="checkbox", - id_="toggle-public", - checked=True, + html.div( + html.label( + html.input_( + type_="checkbox", + id_="toggle-public", + checked=True, + ), + " Show Public", ), - " Show Public", - ), - html.label( - html.input_( - type_="checkbox", - id_="toggle-private", - checked=True, - style="margin-left:1rem;", + html.label( + html.input_( + type_="checkbox", + id_="toggle-private", + checked=True, + ), + " Show Private", ), - " Show Private", - ), - html.label( - html.input_( - type_="checkbox", - id_="toggle-archive", - checked=True, - style="margin-left:1rem;", + html.label( + html.input_( + type_="checkbox", + id_="toggle-archive", + checked=True, + ), + " Show Archived", ), - " Show Archived", + class_="flex justify-between", ), - html.br(), - html.label( - " Owner", - html.input_( - type_="text", - id_="owner-filter", - name="owner-filter", - placeholder="Owner", - class_="rounded p-1", + html.div( + html.label( + " Owner", + html.input_( + type_="text", + id_="owner-filter", + name="owner-filter", + placeholder="Owner", + class_="rounded p-1", + ), ), - ), - html.label( - " Repo", - html.input_( - type_="text", - id_="repo-filter", - name="repo-filter", - placeholder="Repo", - class_="rounded p-1", + html.label( + " Repo", + html.input_( + type_="text", + id_="repo-filter", + name="repo-filter", + placeholder="Repo", + class_="rounded p-1", + ), ), + class_="flex justify-between", ), class_="mx-auto", id_="controls", From 1a3166e4850addcc3e03c0da82b52cbb813c7103 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Wed, 14 May 2025 10:26:17 -0500 Subject: [PATCH 32/45] Add encoding for load file --- src/github_helper/_utils/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/github_helper/_utils/__init__.py b/src/github_helper/_utils/__init__.py index bef6096..5e2b38f 100644 --- a/src/github_helper/_utils/__init__.py +++ b/src/github_helper/_utils/__init__.py @@ -16,7 +16,7 @@ async def load_file(path): if not Path(path).is_file(): raise FileNotFoundError(f"{path} not exist") - async with aiofiles.open(path) as f: + async with aiofiles.open(path, encoding="utf-8") as f: file = await f.read() return file From df5ef0c32997a6eff9a74005ffc3dd545604c120 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Wed, 14 May 2025 10:27:48 -0500 Subject: [PATCH 33/45] Add hover to badges --- src/github_helper/_adapters/to_html/repos.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/github_helper/_adapters/to_html/repos.py b/src/github_helper/_adapters/to_html/repos.py index fd2baab..d50acf5 100644 --- a/src/github_helper/_adapters/to_html/repos.py +++ b/src/github_helper/_adapters/to_html/repos.py @@ -75,7 +75,7 @@ async def htmy(self, context: Context) -> Component: # noqa: ARG002 class_="badge", ), href=f"{github_com}/{s['user']}", - class_="collaborator", + class_="collaborator bg-white hover:bg-slate-200", target="_blank", title=permission_msg[s["permission"]], ) @@ -101,7 +101,7 @@ async def htmy(self, context: Context) -> Component: # noqa: ARG002 target="_blank", ), *[html.span(s, class_=f"topic {s}") for s in repo["topics"]], - class_="topics", + class_="topics bg-white hover:bg-slate-200", ), class_=( "repo-row " From 42dd9a7eacb80ed4b39a00e3fe946016a016157d Mon Sep 17 00:00:00 2001 From: David Angarita Date: Wed, 14 May 2025 10:29:40 -0500 Subject: [PATCH 34/45] Create sort btn for repos --- src/github_helper/_adapters/to_html/repos.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/github_helper/_adapters/to_html/repos.py b/src/github_helper/_adapters/to_html/repos.py index d50acf5..50fa920 100644 --- a/src/github_helper/_adapters/to_html/repos.py +++ b/src/github_helper/_adapters/to_html/repos.py @@ -125,7 +125,21 @@ async def repos_template(repos_data): table = html.table( html.thead( html.tr( - html.th("Repository", colspan=4), + html.th( + html.div( + html.span("Repository"), + html.span( + "⬆️", + id_="sort-button", + class_="cursor-pointer", + data_order="asc", + onclick="sortByRepo()", + title="Sort by asc", + ), + class_="flex justify-evenly", + ), + colspan=4, + ), html.th("Head Tag", colspan=2), html.th("Description", colspan=1), html.th("Collaborators", colspan=1), From 251d314301bf70921a536d2bfb64ded2f6d3dafe Mon Sep 17 00:00:00 2001 From: David Angarita Date: Wed, 14 May 2025 10:31:32 -0500 Subject: [PATCH 35/45] Remove bg color for search bar --- src/github_helper/_adapters/to_html/repos.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/github_helper/_adapters/to_html/repos.py b/src/github_helper/_adapters/to_html/repos.py index 50fa920..eddae4e 100644 --- a/src/github_helper/_adapters/to_html/repos.py +++ b/src/github_helper/_adapters/to_html/repos.py @@ -205,7 +205,7 @@ async def repos_template(repos_data): id_="owner-filter", name="owner-filter", placeholder="Owner", - class_="rounded p-1", + class_="rounded shadow-sm p-1", ), ), html.label( @@ -215,7 +215,7 @@ async def repos_template(repos_data): id_="repo-filter", name="repo-filter", placeholder="Repo", - class_="rounded p-1", + class_="rounded shadow-sm p-1", ), ), class_="flex justify-between", @@ -223,7 +223,7 @@ async def repos_template(repos_data): class_="mx-auto", id_="controls", ), - class_="sticky top-0 w-full bg-slate-300 p-2 z-10", + class_="sticky top-0 w-full p-2 z-10", ), table, modal_iframe, From 045850e777a20cb80eeece3d0446515f903950a0 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Wed, 14 May 2025 10:34:06 -0500 Subject: [PATCH 36/45] Create sort script for repos --- .../_adapters/to_html/_js/repos.js | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/github_helper/_adapters/to_html/_js/repos.js b/src/github_helper/_adapters/to_html/_js/repos.js index e5d439a..c2b797d 100644 --- a/src/github_helper/_adapters/to_html/_js/repos.js +++ b/src/github_helper/_adapters/to_html/_js/repos.js @@ -3,6 +3,7 @@ const privateCheckbox = document.getElementById('toggle-private'); const archivedCheckbox = document.getElementById('toggle-archive'); const ownerInput = document.getElementById('owner-filter'); const repoInput = document.getElementById('repo-filter'); +const tbody = document.querySelector("tbody"); function filterAll() { console.log("Filtering All.") @@ -53,3 +54,24 @@ archivedCheckbox.addEventListener('change', filterAll); ownerInput.addEventListener('input', filterAll); repoInput.addEventListener('input', filterAll); filterAll(); + +const sortByRepo = () => { + const repoFilter = document.getElementById("sort-button"); + const order = repoFilter.dataset.order === "asc" ? "desc" : "asc"; + repoFilter.dataset.order = order; + repoFilter.textContent = order === "asc" ? "⬆️" : "⬇️"; + repoFilter.title = `Sort by ${order === "asc" ? "asc" : "desc"}`; + + const rows = Array.from(tbody.querySelectorAll("tr")); + + rows.sort((a, b) => { + const nameA = a.querySelector(".repo").textContent.trim().toLowerCase(); + const nameB = b.querySelector(".repo").textContent.trim().toLowerCase(); + return order === "asc" + ? nameA.localeCompare(nameB) + : nameB.localeCompare(nameA); + }); + + rows.forEach(row => tbody.appendChild(row)); + updateVisibleRowClasses(); +} From aa4ca32303a98cbc3666b587d212ab4cc52d4438 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Wed, 14 May 2025 10:37:27 -0500 Subject: [PATCH 37/45] Make sort function reusable --- .../_adapters/to_html/_js/repos.js | 17 +++++++++-------- src/github_helper/_adapters/to_html/repos.py | 4 ++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/github_helper/_adapters/to_html/_js/repos.js b/src/github_helper/_adapters/to_html/_js/repos.js index c2b797d..095769f 100644 --- a/src/github_helper/_adapters/to_html/_js/repos.js +++ b/src/github_helper/_adapters/to_html/_js/repos.js @@ -55,18 +55,19 @@ ownerInput.addEventListener('input', filterAll); repoInput.addEventListener('input', filterAll); filterAll(); -const sortByRepo = () => { - const repoFilter = document.getElementById("sort-button"); - const order = repoFilter.dataset.order === "asc" ? "desc" : "asc"; - repoFilter.dataset.order = order; - repoFilter.textContent = order === "asc" ? "⬆️" : "⬇️"; - repoFilter.title = `Sort by ${order === "asc" ? "asc" : "desc"}`; +const sortBy = (btnId, filter) => { + const filterBtn = document.getElementById(btnId); + const order = filterBtn.dataset.order === "asc" ? "desc" : "asc"; + filterBtn.dataset.order = order; + isAsc = order === "asc" + filterBtn.textContent = `${isAsc ? "⬆️" : "⬇️"}`; + filterBtn.title = `Sort by ${isAsc ? "asc" : "desc"}`; const rows = Array.from(tbody.querySelectorAll("tr")); rows.sort((a, b) => { - const nameA = a.querySelector(".repo").textContent.trim().toLowerCase(); - const nameB = b.querySelector(".repo").textContent.trim().toLowerCase(); + const nameA = a.querySelector(filter).textContent.trim().toLowerCase(); + const nameB = b.querySelector(filter).textContent.trim().toLowerCase(); return order === "asc" ? nameA.localeCompare(nameB) : nameB.localeCompare(nameA); diff --git a/src/github_helper/_adapters/to_html/repos.py b/src/github_helper/_adapters/to_html/repos.py index eddae4e..0ac17e8 100644 --- a/src/github_helper/_adapters/to_html/repos.py +++ b/src/github_helper/_adapters/to_html/repos.py @@ -130,10 +130,10 @@ async def repos_template(repos_data): html.span("Repository"), html.span( "⬆️", - id_="sort-button", + id_="sort-repo", class_="cursor-pointer", data_order="asc", - onclick="sortByRepo()", + onclick="sortBy('sort-repo', '.repo')", title="Sort by asc", ), class_="flex justify-evenly", From 5b2b601f1a8af5716c1e48af2b084d67ded907d9 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Wed, 14 May 2025 10:39:02 -0500 Subject: [PATCH 38/45] Add bg color to search bar --- src/github_helper/_adapters/to_html/repos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/github_helper/_adapters/to_html/repos.py b/src/github_helper/_adapters/to_html/repos.py index 0ac17e8..eebd847 100644 --- a/src/github_helper/_adapters/to_html/repos.py +++ b/src/github_helper/_adapters/to_html/repos.py @@ -223,7 +223,7 @@ async def repos_template(repos_data): class_="mx-auto", id_="controls", ), - class_="sticky top-0 w-full p-2 z-10", + class_="sticky top-0 w-full bg-white p-2 z-10", ), table, modal_iframe, From fd177a34ca3c4ce60d4d1882ef3fa77854d00cf6 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Wed, 14 May 2025 10:43:43 -0500 Subject: [PATCH 39/45] Create utils script --- .../_adapters/to_html/_js/repos.js | 22 ------------------- .../_adapters/to_html/_js/utils.js | 21 ++++++++++++++++++ src/github_helper/_adapters/to_html/repos.py | 3 ++- 3 files changed, 23 insertions(+), 23 deletions(-) create mode 100644 src/github_helper/_adapters/to_html/_js/utils.js diff --git a/src/github_helper/_adapters/to_html/_js/repos.js b/src/github_helper/_adapters/to_html/_js/repos.js index 095769f..3d20d77 100644 --- a/src/github_helper/_adapters/to_html/_js/repos.js +++ b/src/github_helper/_adapters/to_html/_js/repos.js @@ -54,25 +54,3 @@ archivedCheckbox.addEventListener('change', filterAll); ownerInput.addEventListener('input', filterAll); repoInput.addEventListener('input', filterAll); filterAll(); - -const sortBy = (btnId, filter) => { - const filterBtn = document.getElementById(btnId); - const order = filterBtn.dataset.order === "asc" ? "desc" : "asc"; - filterBtn.dataset.order = order; - isAsc = order === "asc" - filterBtn.textContent = `${isAsc ? "⬆️" : "⬇️"}`; - filterBtn.title = `Sort by ${isAsc ? "asc" : "desc"}`; - - const rows = Array.from(tbody.querySelectorAll("tr")); - - rows.sort((a, b) => { - const nameA = a.querySelector(filter).textContent.trim().toLowerCase(); - const nameB = b.querySelector(filter).textContent.trim().toLowerCase(); - return order === "asc" - ? nameA.localeCompare(nameB) - : nameB.localeCompare(nameA); - }); - - rows.forEach(row => tbody.appendChild(row)); - updateVisibleRowClasses(); -} diff --git a/src/github_helper/_adapters/to_html/_js/utils.js b/src/github_helper/_adapters/to_html/_js/utils.js new file mode 100644 index 0000000..95ba008 --- /dev/null +++ b/src/github_helper/_adapters/to_html/_js/utils.js @@ -0,0 +1,21 @@ +const sortTableBy = (btnId, filter) => { + const filterBtn = document.getElementById(btnId); + const order = filterBtn.dataset.order === "asc" ? "desc" : "asc"; + filterBtn.dataset.order = order; + isAsc = order === "asc" + filterBtn.textContent = `${isAsc ? "⬆️" : "⬇️"}`; + filterBtn.title = `Sort by ${isAsc ? "asc" : "desc"}`; + + const rows = Array.from(tbody.querySelectorAll("tr")); + + rows.sort((a, b) => { + const nameA = a.querySelector(filter).textContent.trim().toLowerCase(); + const nameB = b.querySelector(filter).textContent.trim().toLowerCase(); + return order === "asc" + ? nameA.localeCompare(nameB) + : nameB.localeCompare(nameA); + }); + + rows.forEach(row => tbody.appendChild(row)); + updateVisibleRowClasses(); +} diff --git a/src/github_helper/_adapters/to_html/repos.py b/src/github_helper/_adapters/to_html/repos.py index eebd847..c2bfbb6 100644 --- a/src/github_helper/_adapters/to_html/repos.py +++ b/src/github_helper/_adapters/to_html/repos.py @@ -133,7 +133,7 @@ async def repos_template(repos_data): id_="sort-repo", class_="cursor-pointer", data_order="asc", - onclick="sortBy('sort-repo', '.repo')", + onclick="sortTableBy('sort-repo', '.repo')", title="Sort by asc", ), class_="flex justify-evenly", @@ -162,6 +162,7 @@ async def repos_template(repos_data): [ await load_file(_JS_PATH / "repos.js"), await load_file(_JS_PATH / "modal.js"), + await load_file(_JS_PATH / "utils.js"), ], ), ), From 68b005a13851d7fa6f2a7cdd7345512a444b447b Mon Sep 17 00:00:00 2001 From: David Angarita Date: Wed, 14 May 2025 10:45:34 -0500 Subject: [PATCH 40/45] Fix topic styles --- src/github_helper/_adapters/to_html/repos.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/github_helper/_adapters/to_html/repos.py b/src/github_helper/_adapters/to_html/repos.py index c2bfbb6..e7d21d5 100644 --- a/src/github_helper/_adapters/to_html/repos.py +++ b/src/github_helper/_adapters/to_html/repos.py @@ -100,8 +100,8 @@ async def htmy(self, context: Context) -> Component: # noqa: ARG002 href=f"{github_com}/{repo['owner']}/{repo['name']}", target="_blank", ), - *[html.span(s, class_=f"topic {s}") for s in repo["topics"]], - class_="topics bg-white hover:bg-slate-200", + *[html.span(s, class_=f"topic {s} bg-white") for s in repo["topics"]], + class_="topics", ), class_=( "repo-row " From 79059d8c68b0100e4852ff6df7922cccf71ea23b Mon Sep 17 00:00:00 2001 From: David Angarita Date: Wed, 14 May 2025 10:46:14 -0500 Subject: [PATCH 41/45] Remove class from repository --- src/github_helper/_adapters/to_html/repos.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/github_helper/_adapters/to_html/repos.py b/src/github_helper/_adapters/to_html/repos.py index e7d21d5..f1408e6 100644 --- a/src/github_helper/_adapters/to_html/repos.py +++ b/src/github_helper/_adapters/to_html/repos.py @@ -136,7 +136,6 @@ async def repos_template(repos_data): onclick="sortTableBy('sort-repo', '.repo')", title="Sort by asc", ), - class_="flex justify-evenly", ), colspan=4, ), From 59d12090d65ab69f0748beef027c149d541af57f Mon Sep 17 00:00:00 2001 From: David Angarita Date: Wed, 14 May 2025 11:28:06 -0500 Subject: [PATCH 42/45] Move table styles for common styles --- .../_adapters/to_html/_styles/common.css | 12 ++++++++++++ .../_adapters/to_html/_styles/rulesets.css | 15 --------------- src/github_helper/_adapters/to_html/repos.py | 5 ++++- src/github_helper/_adapters/to_html/rulesets.py | 3 +-- 4 files changed, 17 insertions(+), 18 deletions(-) delete mode 100644 src/github_helper/_adapters/to_html/_styles/rulesets.css diff --git a/src/github_helper/_adapters/to_html/_styles/common.css b/src/github_helper/_adapters/to_html/_styles/common.css index dd1eb2a..9716002 100644 --- a/src/github_helper/_adapters/to_html/_styles/common.css +++ b/src/github_helper/_adapters/to_html/_styles/common.css @@ -14,3 +14,15 @@ tr.even { tr.odd { background-color: #ffffff; } + +.table { + @apply table-auto mx-auto my-4 border border-gray-300 shadow-md rounded-md overflow-hidden text-sm; +} + +.table-header { + @apply text-center px-4 py-2 text-sm font-semibold text-gray-700 bg-gray-100 border-r; +} + +.table-col { + @apply px-4 py-2 align-top border-t border-gray-200 border-r; +} diff --git a/src/github_helper/_adapters/to_html/_styles/rulesets.css b/src/github_helper/_adapters/to_html/_styles/rulesets.css deleted file mode 100644 index ea2d5b4..0000000 --- a/src/github_helper/_adapters/to_html/_styles/rulesets.css +++ /dev/null @@ -1,15 +0,0 @@ -.table { - @apply table-auto w-11/12 mx-auto my-8 border border-gray-300 shadow-md rounded-md overflow-hidden text-sm; -} - -.table-header { - @apply text-center px-4 py-2 text-sm font-semibold text-gray-700 bg-gray-100 border-r; -} - -.table-col { - @apply px-4 py-2 align-top border-t border-gray-200 border-r; -} - -.table-col--code { - @apply bg-gray-50 text-gray-800 text-xs p-3 rounded-md overflow-x-auto font-mono border border-gray-300; -} diff --git a/src/github_helper/_adapters/to_html/repos.py b/src/github_helper/_adapters/to_html/repos.py index f1408e6..33aed90 100644 --- a/src/github_helper/_adapters/to_html/repos.py +++ b/src/github_helper/_adapters/to_html/repos.py @@ -119,7 +119,10 @@ def repo_rows(repos, context: Context) -> Component: # noqa: ARG001 async def repos_template(repos_data): _logger.debug("Building table.") styles = [ - html.style(await load_file(_STYLES_PATH / "common.css")), + html.style( + await load_file(_STYLES_PATH / "common.css"), + type="text/tailwindcss", + ), html.style(await load_file(_STYLES_PATH / "repos.css")), ] table = html.table( diff --git a/src/github_helper/_adapters/to_html/rulesets.py b/src/github_helper/_adapters/to_html/rulesets.py index 8ad3084..db878d2 100644 --- a/src/github_helper/_adapters/to_html/rulesets.py +++ b/src/github_helper/_adapters/to_html/rulesets.py @@ -62,9 +62,8 @@ def rulesets_rows(rulesets, context: Context) -> Component: # noqa: ARG001 async def rulesets_template(rulesets_data): _logger.debug("Building table.") styles = [ - html.style(await load_file(_STYLES_PATH / "common.css")), html.style( - await load_file(_STYLES_PATH / "rulesets.css"), + await load_file(_STYLES_PATH / "common.css"), type="text/tailwindcss", ), html.link( From b7f4566fb9bb4b3be25e66c5acc78063830e7a60 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Wed, 14 May 2025 11:29:04 -0500 Subject: [PATCH 43/45] Add table styles for repos --- src/github_helper/_adapters/to_html/repos.py | 29 ++++++++++++-------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/github_helper/_adapters/to_html/repos.py b/src/github_helper/_adapters/to_html/repos.py index 33aed90..9f9f21c 100644 --- a/src/github_helper/_adapters/to_html/repos.py +++ b/src/github_helper/_adapters/to_html/repos.py @@ -51,13 +51,19 @@ async def htmy(self, context: Context) -> Component: # noqa: ARG002 href=f"{github_com}/{repo['owner']}/{repo['name']}", target="_blank", ), - class_="repo", + class_="repo border-gray-200 border-r pr-2", + ), + html.td( + html.span(repo["version"]), + class_="text-center", + ), + html.td( + html.span("⑂" if repo["fork"] else ""), + class_="table-col", ), - html.td(html.span(repo["version"]), class_="text-center"), - html.td(html.span("⑂" if repo["fork"] else "")), html.td( html.span(repo["description"] or ""), - class_="description", + class_="description table-col", ), html.td( html.a( @@ -92,7 +98,7 @@ async def htmy(self, context: Context) -> Component: # noqa: ARG002 else repo["collaborators"] ) ], - class_="collaborators", + class_="collaborators table-col", ), html.td( html.a( @@ -101,7 +107,7 @@ async def htmy(self, context: Context) -> Component: # noqa: ARG002 target="_blank", ), *[html.span(s, class_=f"topic {s} bg-white") for s in repo["topics"]], - class_="topics", + class_="topics table-col", ), class_=( "repo-row " @@ -141,15 +147,16 @@ async def repos_template(repos_data): ), ), colspan=4, + class_="table-header", ), - html.th("Head Tag", colspan=2), - html.th("Description", colspan=1), - html.th("Collaborators", colspan=1), - html.th("Topics", colspan=1), + html.th("Head Tag", colspan=2, class_="table-header"), + html.th("Description", colspan=1, class_="table-header"), + html.th("Collaborators", colspan=1, class_="table-header"), + html.th("Topics", colspan=1, class_="table-header"), ), ), html.tbody(repo_rows(repos_data)), - class_="mx-auto", + class_="table", ) modal_iframe = _components.modal( "my-modal", From c5b632719b0367e6a29d22d02deb56e422882bd7 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Wed, 14 May 2025 11:29:30 -0500 Subject: [PATCH 44/45] Modify table styles for rulesets --- src/github_helper/_adapters/to_html/rulesets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/github_helper/_adapters/to_html/rulesets.py b/src/github_helper/_adapters/to_html/rulesets.py index db878d2..64c85d0 100644 --- a/src/github_helper/_adapters/to_html/rulesets.py +++ b/src/github_helper/_adapters/to_html/rulesets.py @@ -35,7 +35,7 @@ async def htmy(self, context: Context) -> Component: # noqa: ARG002 ruleset["diff"], pretty=True, ), - class_="language-json", + class_="language-json rounded-md", ), ) if isinstance(ruleset["diff"], dict) @@ -80,7 +80,7 @@ async def rulesets_template(rulesets_data): ), ), html.tbody(rulesets_rows(rulesets_data)), - class_="table", + class_="table table-auto w-11/12", ) scripts = [ html.script(src=f"{_HLJS_URL}/highlight.min.js"), From 4121f93e1001f2a3c0211660777df3127daad335 Mon Sep 17 00:00:00 2001 From: David Angarita Date: Wed, 14 May 2025 11:36:25 -0500 Subject: [PATCH 45/45] Change theader color and padding --- src/github_helper/_adapters/to_html/_styles/common.css | 4 ++-- src/github_helper/_adapters/to_html/rulesets.py | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/github_helper/_adapters/to_html/_styles/common.css b/src/github_helper/_adapters/to_html/_styles/common.css index 9716002..9eac68e 100644 --- a/src/github_helper/_adapters/to_html/_styles/common.css +++ b/src/github_helper/_adapters/to_html/_styles/common.css @@ -20,9 +20,9 @@ tr.odd { } .table-header { - @apply text-center px-4 py-2 text-sm font-semibold text-gray-700 bg-gray-100 border-r; + @apply text-center px-4 py-2 text-sm font-semibold text-gray-700 bg-gray-300 border-r; } .table-col { - @apply px-4 py-2 align-top border-t border-gray-200 border-r; + @apply px-2 py-2 align-top border-t border-gray-200 border-r; } diff --git a/src/github_helper/_adapters/to_html/rulesets.py b/src/github_helper/_adapters/to_html/rulesets.py index 64c85d0..3a0a232 100644 --- a/src/github_helper/_adapters/to_html/rulesets.py +++ b/src/github_helper/_adapters/to_html/rulesets.py @@ -31,10 +31,7 @@ async def htmy(self, context: Context) -> Component: # noqa: ARG002 html.td( html.pre( html.code( - to_json.format_json( - ruleset["diff"], - pretty=True, - ), + to_json.format_json(ruleset["diff"], pretty=True), class_="language-json rounded-md", ), )