From 3fe2522539a961f7ee227b3a686660733c7e26d7 Mon Sep 17 00:00:00 2001 From: "callumnmw@gmail.com" Date: Wed, 17 Dec 2025 07:51:58 +1300 Subject: [PATCH 01/12] Remove footer css (not used) --- docs/assets/stylesheets/footer.css | 31 ------------------------------ mkdocs.yml | 1 - 2 files changed, 32 deletions(-) delete mode 100644 docs/assets/stylesheets/footer.css diff --git a/docs/assets/stylesheets/footer.css b/docs/assets/stylesheets/footer.css deleted file mode 100644 index 14dc199dc..000000000 --- a/docs/assets/stylesheets/footer.css +++ /dev/null @@ -1,31 +0,0 @@ -#new-footer { - font-family: Lato; - font-size: 12px; - font-weight: 400; - background-color: #101010; -} - -#partners { - height: auto; - background-color: #101010; -} - -#partners #logos img { - height: 40px; - margin: 10px; -} - -#partners #logos { - padding: 10px; - text-align: center; -} - -#partners #logos .nesi-footer-logo img { - margin-right: 100px; - height: 60px; -} - -#partners #logos img { - height: 40px; - margin: 10px; -} \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 3c67e4d31..6db7938c0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -98,7 +98,6 @@ plugins: module_name: macro_hooks include_dir: overrides extra_css: - - assets/stylesheets/footer.css - assets/stylesheets/custom_admonations.css - assets/stylesheets/theme.css extra_javascript: From e2158ff8dc03fd3acb3208c04c30b50702f80ff1 Mon Sep 17 00:00:00 2001 From: "callumnmw@gmail.com" Date: Wed, 17 Dec 2025 09:30:17 +1300 Subject: [PATCH 02/12] will comment on weird tags --- checks/fail_checks.md | 5 +++- checks/run_meta_check.py | 53 ++++++++++++++++++++++++++++++++++------ 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/checks/fail_checks.md b/checks/fail_checks.md index 334cc7eaf..d1a3f45da 100644 --- a/checks/fail_checks.md +++ b/checks/fail_checks.md @@ -3,7 +3,10 @@ created_at: 2025-01-28 template: not_a_template not_a_parameter: This isn't valid tags: - - Not enough + - chemistryies + - move data + - gibberjabber + - mostly nonsense --- diff --git a/checks/run_meta_check.py b/checks/run_meta_check.py index 07ddb49ca..8671cfe76 100755 --- a/checks/run_meta_check.py +++ b/checks/run_meta_check.py @@ -13,6 +13,7 @@ import time from titlecase import titlecase from pathlib import Path +from difflib import SequenceMatcher # Ignore files if they match this regex EXCLUDED_FROM_CHECKS = [ @@ -28,10 +29,10 @@ MAX_TITLE_LENGTH = 28 # As font isn't monospace, this is only approx MAX_HEADER_LENGTH = 32 # minus 2 per extra header level -MIN_TAGS = 1 +RANGE_TAGS = [1, 5] RANGE_SIBLING = [4, 8] DOC_ROOT = "docs" - +APPROVED_TAGS = "checks/.approved_tags.yml" # Warning level for missing parameters. EXPECTED_PARAMETERS = { "title": "", @@ -43,7 +44,7 @@ "postreq": "", "suggested": "", # Add info here when implimented. "created_at": "", - "tags": "", # Add info here when implimented. + "tags": yaml.safe_load(open(APPROVED_TAGS, 'r')), "search": "", "hide": ["toc", "nav", "tags"], } @@ -263,6 +264,25 @@ def _nav_check(): ) +def _jaccard_index(s1, s2): + set1 = set(s1.lower()) # Split into words + set2 = set(s2.lower()) + intersection = len(set1.intersection(set2)) + union = len(set1.union(set2)) + return intersection / union + +def _sim_index(str1, str2): + return SequenceMatcher(None, str1, str2).ratio() + +def _most_similar(s1, op): + bestval = 0 + bestword = "" + for o in op: + ji = _jaccard_index(s1, o) + if ji > bestval: + bestval = ji + bestword = o + return bestword, bestval def title_redundant(): lineno = _get_lineno(r"^title:.*$") if "title" in meta.keys() and title_from_filename == meta["title"]: @@ -300,9 +320,10 @@ def _test(v): for key, value in meta.items(): if key not in EXPECTED_PARAMETERS.keys(): + similar, ji = _most_similar(key, EXPECTED_PARAMETERS.keys()) yield { "line": _get_lineno(r"^" + key + r":.*$"), - "message": f"Unexpected parameter in front-matter '{key}'", + "message": f"Unexpected parameter in front-matter '{key}'{', did you mean \'' + similar + "\'?" if ji > 0.4 else ""} .", } elif EXPECTED_PARAMETERS[key]: if isinstance(value, list): @@ -334,16 +355,31 @@ def title_capitalisation(): '{correct_title}' is preferred", } -def minimum_tags(): +def number_tags(): if "tags" not in meta or not isinstance(meta["tags"], list): yield {"message": "'tags' property in meta is missing or malformed."} - elif len(meta["tags"]) < MIN_TAGS: + elif len(meta["tags"]) < RANGE_TAGS[0]: yield { "line": _get_lineno(r"^tags:.*$"), - "message": "Try to include at least 2 'tags'\ + "message": f"Try to include at least {RANGE_TAGS[0]} 'tags'\ (helps with search optimisation).", } + elif len(meta["tags"]) > RANGE_TAGS[1]: + yield { + "line": _get_lineno(r"^tags:.*$"), + "message": f"{RANGE_TAGS[1]} is a lot of 'tags', are you sure they are all useful?", + } +def approved_tags(): + if "tags" not in meta or not isinstance(meta["tags"], list): + return + for tag in meta["tags"]: + if tag not in EXPECTED_PARAMETERS["tags"]: + similar, ji = _most_similar(tag, EXPECTED_PARAMETERS["tags"]) + yield { + "line": _get_lineno(rf".*{tag}.*"), + "message": f"Tag '{tag}' is not an approved tag, {'did you mean \'' + similar + "\'?" if ji > 0.4 else 'See \'' + APPROVED_TAGS + '\'.'}", + } def click_here(): """ @@ -419,7 +455,8 @@ def dynamic_slurm_link(): title_capitalisation, meta_missing_description, meta_unexpected_key, - minimum_tags, + number_tags, + approved_tags, walk_toc, ] From 9172e108070b71f815a145f8a5a4069fbe05ca55 Mon Sep 17 00:00:00 2001 From: "callumnmw@gmail.com" Date: Wed, 17 Dec 2025 10:59:52 +1300 Subject: [PATCH 03/12] tidy up tags --- checks/.approved_tags.yml | 64 ++++++++------------------------------- 1 file changed, 12 insertions(+), 52 deletions(-) diff --git a/checks/.approved_tags.yml b/checks/.approved_tags.yml index 2bf08d249..943911f11 100644 --- a/checks/.approved_tags.yml +++ b/checks/.approved_tags.yml @@ -1,84 +1,44 @@ --- [ - ".core", - "2fa", - "Computational Chemistry", - "Density Functional Theory", - "Molecular Dynamics", + "Chemistry", + "DensityFunctionalTheory", + "MolecularDynamics", "Slurm", - "XC50", "access", - "account", - "allocation", - "announcement", - "assessment", - "authentication", - "bash", + "account", # Any page relating to user account + "allocation", "biology", "cae", "cfd", "chemistry", "compiler", - "compression", "containers", - "corefile", - "CS400", - "CS500", - "data compression", - "disk quota", + "diskQuota", "earthquake", "engineering", "fea", "geo", - "git", - "github", - "globus", "gpu", - "home", "hydrodynamics", "ide", - "introduction", - "jupyter lab", + "jupyter", "login", - "mahuika", - "maui", - "mfa", - "milan", - "machine learning", + "machineLearning", "morphodynamics", "mpi", "multiphysics", "mynesi", - "nearline", "nobackup", - "omp", - "onboarding", + "multiproccessing", "ondemand", - "particle modelling", + "particleModelling", "profiling", "project", - "quotas", - "refresh", "releasenote", - "Project Membership", - "scaling", - "scp", - "software", "ssh", "storage", - "tape", - "terminal", - "toolchain", - "Data Transfer", - "tuakiri", - "upload", - "version control", + "DataTransfer", "visualisation", - "water quality testing", - "wave modelling", - "webinar", + "waveModelling", "windows", - "workshop", - "wsl", - "x11", ] From d730c51f71a6226c1dbf1edf4b31c3a8357f57c9 Mon Sep 17 00:00:00 2001 From: "callumnmw@gmail.com" Date: Wed, 17 Dec 2025 11:00:03 +1300 Subject: [PATCH 04/12] add some tag info --- checks/README.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/checks/README.md b/checks/README.md index 96cfb8901..58f59dc8b 100644 --- a/checks/README.md +++ b/checks/README.md @@ -2,6 +2,8 @@ This directory contains QA tests for the documentation. +All these checks are intended to run on the markdown files, not rendered files. + Tests should be made as Python scripts to allow flexibility of use. Currently these checks are run two ways: - [GitHub Actions](https://docs.github.com/en/actions) as defined in [workflows](../.github/workflows/), @@ -44,16 +46,8 @@ Individual rules can be disabled/enabled in [.markdownlint.json](../.markdownlin *This linter is defined in [run_meta_check.py](run_meta_check.py) script.* Catch-all for custom checks. -Currently defined checks are: -- title_redundant -- title_length -- meta_missing_description -- meta_unexpected_key -- minimum_tags -- walk_toc -- click_here -- dynamic_slurm_link +See script for details. ### Test Build @@ -66,3 +60,12 @@ Each type of test has a debug job in VSCode. Most will run on the [fail_checks](fail_checks.md) page ![alt text](../docs/assets/images/debug_menu.png) + +## Tags + +Tags are used to help search indexing, but can also be used to search by topic. + +We don't want a large number of similar/duplicate tag topics, as this is visually messy and reduces the utility of being able to sort by one. +There is a list of 'approved' tags [.approved_tags.yml](./.approved_tags.yml), feel free to add to it. + +Tags are checked in [run_meta_check.py](run_meta_check.py). From 53599eba44d5951bfc2f866011c2bcaf71846e78 Mon Sep 17 00:00:00 2001 From: "callumnmw@gmail.com" Date: Wed, 17 Dec 2025 11:00:14 +1300 Subject: [PATCH 05/12] tidy up unused css --- .../stylesheets/supportedApplications.css | 59 ++++++++++--------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/docs/assets/stylesheets/supportedApplications.css b/docs/assets/stylesheets/supportedApplications.css index d0da8b9e5..45f4a6ea6 100644 --- a/docs/assets/stylesheets/supportedApplications.css +++ b/docs/assets/stylesheets/supportedApplications.css @@ -33,34 +33,6 @@ font-family: "Material Icons"; margin-right: 0.2em; } -.btn-cluster { - padding: 0.4em 0.4em 0.4em 0.4em; - margin: 0 0.2em 0 0.2em; - border-radius: .25rem; - border: 1px var(--md-default-fg-color--light) solid; -} -.btn-cluster:before { - content: " \f233 "; - font-family: "FontAwesome"; - margin-right: 0.2em; -} -.btn-cluster-maui { - color: #3c816e; - border-color: #3c816e; -} -.btn-cluster-mahuika { - color: #972220; - border-color: #972220; -} -.active.btn-cluster-maui { - color: var(--md-primary-bg-color); - background-color: #3c816e; -} -.active.btn-cluster-mahuika { - color: var(--md-primary-bg-color); - background-color: #972220; -} - .grid.flex-card-parent { display: flex; flex-direction: row; @@ -136,6 +108,7 @@ div#tier2 { li { list-style: none; } + /*badges*/ .badge{ margin: 0 0.5em 0 0.5em; @@ -146,6 +119,7 @@ li { padding: 0.2em; border-radius: 1pt; } + .badge-closeable > button { background-color: transparent; border: 0; @@ -154,91 +128,115 @@ li { padding: 0.4em 0.3em 0.3em 0.6em; margin: -0.3em; } + .badge-closeable > button::after { content: " \f057"; font-family: "FontAwesome"; } + .badge-closeable { cursor: default; } + .badge-cluster { float: right; border-style: double; } + .badge-cluster-maui { background-color: #3c816e; } + .badge-cluster-maui_ancil { background-color: #3600b3; } + .badge-cluster-mahuika { background-color: #972220; } + .badge-domain-biology{ background-color: #28a745; } + .badge-domain-chemistry { background-color: #ff07d8; } + .badge-domain-earth_science { background-color: #794e00; } + .badge-domain-engineering{ background-color: #ff7737; } + .badge-domain-mathematics { background-color: #9935dc; } + .badge-domain-medical_science { background-color: #d10000; } + .badge-domain-physics { background-color: #6495ed; } + .badge-domain-social_science { background-color: #a11d3e; } + .badge-domain-machine_learning { background-color: #ffdd00; color: dimgrey; } + .badge-domain-data_analytics { background-color: #feff00; color: dimgrey; } + .badge-domain-social_science { background-color: #1797ad; } + .badge-domain-visualisation { display:none; box-shadow: 0px 0px 0px 1px #495057 inset; background-color: #fea; } + .badge-domain-language { box-shadow: 0px 0px 0px 1px #495057 inset; background-color: #eceff1; display:none; } + .badge-licence{ padding: 0; color: red; cursor: default; } + .badge-largeinator{ padding: 0.4em; margin: -0.4em; cursor: pointer; } + @media (max-width: 560px){ .badge-domain{ display:none; } } + @media (max-width: 800px){ .cardbody-licence{ display:none; } } + div#updated { display: flex; font-style: italic; @@ -259,12 +257,15 @@ div#updated { width: 100%; padding: 0 0 1rem 0; } + .md-search-aux__inner { width: 100% } + .md-search__icon.md-search-aux__icon{ color: var(--md-default-fg-color--light); } + .md-search-aux__input{ box-shadow: 0 0 0.6rem #0000; height: 1.8rem; @@ -272,12 +273,14 @@ div#updated { transition: color .25s,background-color .25s; z-index: 2; } + .md-search__icon[for=__search-aux] { position: absolute; top: .3rem; left: 0.5rem; z-index: 2 } + .md-search-aux__form{ background-color: var(--md-default-bg-color); border-radius: 0.1rem; From 663633a4bf78c9314d343793c85d440a0f371a98 Mon Sep 17 00:00:00 2001 From: "callumnmw@gmail.com" Date: Wed, 17 Dec 2025 11:01:39 +1300 Subject: [PATCH 06/12] re-write to not use jq. Also use element data rather than reading css tags. --- .../javascripts/supportedApplications.js | 214 ++++++++++-------- docs/assets/stylesheets/theme.css | 6 - overrides/partials/app/app_card.html | 4 +- overrides/supported_apps.html | 1 - 4 files changed, 127 insertions(+), 98 deletions(-) diff --git a/docs/assets/javascripts/supportedApplications.js b/docs/assets/javascripts/supportedApplications.js index 59056baa5..a5d8ebed1 100644 --- a/docs/assets/javascripts/supportedApplications.js +++ b/docs/assets/javascripts/supportedApplications.js @@ -1,114 +1,148 @@ -// Copied from module list repo. -CLUSTER_WHITELIST=["mahuika", "maui", "maui_ancil"] -DOMAIN_WHITELIST=["astronomy","biology","chemistry", "data_analytics", "earth_science", "engineering", "language", "machine_learning", -"mathematics","medical_science","physics","social_science","visualisation","climate_science","workflow_management"] +const DOMAIN_WHITELIST = [ + "astronomy", "biology", "chemistry", "data_analytics", + "earth_science", "engineering", "language", "machine_learning", + "mathematics", "medical_science", "physics", "social_science", + "visualisation", "climate_science", "workflow_management" +]; -$(document).ready(function() { - params = new URL(document.location).searchParams; - search_string = params.get("search"); - cluster_tags = (params.get("cluster") ?? "").split(",").filter(Boolean); - domain_tags = (params.get("domain") ?? "").split(",").filter(Boolean); +const state = { + search: "", + domain: null // string | null +}; - cluster_tags.forEach((tag)=>addBadge(tag, "cluster")); - domain_tags.forEach((tag)=>addBadge(tag, "domain")); +function syncURL() { + const params = new URLSearchParams(); - if (search_string){ - $('#__search-aux')[0].value = search_string; - } - filterSearch(); -}) + if (state.search) { + params.set("search", state.search); + } -function addBadge(tag, filter_type){ - $(`#srchbar-badge-party-${filter_type}s`).append(() => { - return `${tag.charAt(0).toUpperCase() + tag.replace('_', ' ').slice(1)}`; -}) -} + if (state.domain) { + params.set("domain", state.domain); + } -function removeBadge(tag, filter_type){ - $(`#srchbar-badge-party-${filter_type}s > .badge-${filter_type}-${tag}`).remove(); // Remove tag class from DOM + history.pushState(null, "", `?${params.toString()}`); } -function addTag(tag, filter_type){ - addBadge(tag, filter_type); - params.set(filter_type, (params.get(filter_type) ?? "").split(",").filter(Boolean).concat(tag).join()); - history.pushState(null, '', window.location.pathname + '?' + params.toString()); +function renderDomainBadge() { + const container = document.getElementById("srchbar-badge-party-domains"); + if (!container) return; + + container.innerHTML = ""; + + if (!state.domain) return; + + const badge = document.createElement("span"); + + badge.className = `badge badge-closeable badge-domain badge-domain-${state.domain}`; + badge.dataset.domain = state.domain; + + badge.textContent = + state.domain.charAt(0).toUpperCase() + + state.domain.replace("_", " ").slice(1); + + const button = document.createElement("button"); + button.type = "button"; + button.setAttribute("aria-label", "Close"); + + badge.appendChild(button); + container.appendChild(badge); } -function removeTag(tag, filter_type){ - removeBadge(tag, filter_type); - params.set(filter_type, (params.get(filter_type) ?? "").split(",").filter(Boolean).filter(e => e !== tag).join()); // Remove tag class from search string - history.pushState(null, '', window.location.pathname + '?' + params.toString()); // push to search history for live update. +function render() { + renderDomainBadge(); + filterSearch(); } -function domainToggleFilter(domain) { - if (DOMAIN_WHITELIST.includes(domain)){ - if ($(`#srchbar-badge-party-domains > .badge-domain-${domain}`).length < 1) { - addTag(domain, "domain"); - } else { - removeTag(domain, "domain"); - } - filterSearch(); +/* ============================================================================ + * Filtering + * ========================================================================== */ + +function filterSearch() { + const items = document.querySelectorAll(".list-group-item-application"); + + items.forEach(item => { + let visible = true; + + const text = item.textContent.toLowerCase(); + + const itemDomains = + item.dataset.domains?.split(",") ?? []; + + if (state.domain) { + visible &&= itemDomains.includes(state.domain); } -} -function clusterToggleFilter(cluster) { - if (CLUSTER_WHITELIST.includes(cluster)){ - if ($(`#srchbar-badge-party-clusters > .badge-cluster-${cluster}`).length < 1) { - addTag(cluster, "cluster"); - } else { - removeTag(cluster, "cluster"); - } - filterSearch(); + if (state.search) { + visible &&= text.includes(state.search.toLowerCase()); } + + item.classList.toggle("hide_search", !visible); + }); } +function toggleDomain(domain) { + if (!DOMAIN_WHITELIST.includes(domain)) return; + + // Clicking the active domain clears it + // Clicking a different domain replaces it + state.domain = state.domain === domain ? null : domain; -function srchFunc(event) { - // Function called whenever search field edited. - // Consider replacing with Fuse, if fuzzy or faster search needed. - // Check if search string matches canon domain. - search_string = $('#__search-aux')[0].value; - params.set("search", search_string); - // Rather that add to url, edit history. - history.pushState(null, '', window.location.pathname + '?' + params.toString()); - filterSearch() + syncURL(); + render(); } -//Goes through each app and shows/hides accordingly. -function filterSearch() { - function matchClasses(element, inarray) { - // Only doing this as extreme DRY - if (inarray.length < 1) { - return true - } - for (i = 0; i < inarray.length; i++) { - if (element.hasClass(`list-group-item-application-${inarray[i]}`)) { - return true - } - } - return false - } - function matchSearch(comptxt){ - if (search_string){ - return (comptxt.indexOf(search_string) > -1) - } - return true - } +// Debounce input +let searchDebounceTimer = null; + +function onSearchInput(event) { + clearTimeout(searchDebounceTimer); - $('.list-group-item-application').each(function() { // Get list members. - element = $(this) - comptxt = (element.text() ?? "").toLowerCase(); // Flatten content - $(element).removeClass('hide_search'); //Show all element - // If element matches all contitions, leave visible and skip to next element - if (matchClasses(element, domain_tags) && matchClasses(element, cluster_tags) && matchSearch(comptxt)) { - return true - } - element.addClass('hide_search'); //Hides element - }); + searchDebounceTimer = setTimeout(() => { + state.search = event.target.value ?? ""; + syncURL(); + filterSearch(); + }, 150); } -//Stop propigation of clicks to their parent elements. -$(".badge-largeinator").click(function(event) { + +document.addEventListener("DOMContentLoaded", () => { + const params = new URLSearchParams(window.location.search); + + state.search = params.get("search") ?? ""; + state.domain = params.get("domain"); + + // Validate domain from URL + if (!DOMAIN_WHITELIST.includes(state.domain)) { + state.domain = null; + } + + const searchInput = document.getElementById("__search-aux"); + if (searchInput) { + searchInput.value = state.search; + searchInput.addEventListener("input", onSearchInput); + } + + // Badge close handling + document.addEventListener("click", event => { + const badge = event.target.closest(".badge-domain"); + if (!badge) return; + + toggleDomain(badge.dataset.domain); event.stopPropagation(); -}) + }); + + // Prevent bubbling from badge container + document.querySelectorAll(".badge-largeinator").forEach(el => { + el.addEventListener("click", e => e.stopPropagation()); + }); + + render(); +}); + +/* ============================================================================ + * Public API (for existing onclick hooks) + * ========================================================================== */ + +window.domainToggleFilter = toggleDomain; diff --git a/docs/assets/stylesheets/theme.css b/docs/assets/stylesheets/theme.css index 0cd427aeb..d86450e73 100644 --- a/docs/assets/stylesheets/theme.css +++ b/docs/assets/stylesheets/theme.css @@ -74,8 +74,6 @@ .md-nav--primary > .md-nav__title { display: none; } -/* fix neotiri card colors */ - /* Make button more buttony */ .md-button--primary { box-shadow: grey 2px 2px 2px; @@ -133,7 +131,6 @@ span.md-status.md-status--deprecated { cursor: default; } - /* Make cards look right */ .md-typeset .grid.cards { display: grid; @@ -179,7 +176,6 @@ span.md-status.md-status--deprecated { color: rgba(0, 0, 0, 0.87); } - #calendar-banner { background: #fffae6; border-bottom: 1px solid #ccc; @@ -188,8 +184,6 @@ span.md-status.md-status--deprecated { z-index: 9999; } - - #calendar-banner button { position: absolute; top: 4px; diff --git a/overrides/partials/app/app_card.html b/overrides/partials/app/app_card.html index f57ed9153..93a803124 100644 --- a/overrides/partials/app/app_card.html +++ b/overrides/partials/app/app_card.html @@ -1,6 +1,8 @@
+ " + data-domains="{% for domain in app.domains -%}{{ domain }},{% endfor -%}" + >