From bc2943bb1cdb50ecbf80df4da7dd0de2820e8c2c Mon Sep 17 00:00:00 2001 From: ttrenty <154608953+ttrenty@users.noreply.github.com> Date: Thu, 15 Oct 2026 10:12:32 -0400 Subject: [PATCH] feat: update profiles, fix CI, improve editors --- README.md | 33 +++++-- assets/js/site.js | 42 ++++----- content/people/jana-vatter.md | 5 +- content/people/michael-dangana.md | 8 +- content/people/shiquan-zhang.md | 7 +- content/people/yunhao-mao.md | 6 +- data/content_schema.json | 36 ++++++-- hugo.yaml | 13 --- layouts/partials/people/now.html | 2 + layouts/partials/people/roster.html | 2 +- layouts/publications/archive.html | 14 +-- scripts/browser_check.cjs | 31 ++++++- scripts/edit_profiles.py | 121 +++++++++++++++++++++++--- scripts/editor_check.cjs | 102 +++++++++++++++++++++- scripts/profile_editor/editor.css | 8 ++ scripts/profile_editor/editor.js | 109 ++++++++++++++++++++--- scripts/profile_editor/index.html | 13 ++- tests/test_publication_identifiers.py | 114 ++++++++++++++++++++++++ tests/test_roster_transitions.py | 32 +++++-- tests/test_website_editor.py | 73 +++++++++++++++- 20 files changed, 666 insertions(+), 105 deletions(-) create mode 100644 layouts/partials/people/now.html create mode 100644 tests/test_publication_identifiers.py diff --git a/README.md b/README.md index 56f145c..ddc3f47 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,8 @@ pixi run editor ``` Open **http://127.0.0.1:1314** and choose what to edit using the selector at the -top. The editor starts the local website preview automatically; **Ctrl+C** stops both. +top. The editor starts the local website preview automatically and prints its link +in the terminal (usually **http://127.0.0.1:1313/**); **Ctrl+C** stops both. Website editor showing the content selector and editing form @@ -52,16 +53,21 @@ top. The editor starts the local website preview automatically; **Ctrl+C** stops | Personal Pages | Build a page from a template or preview your custom website. | | Datasets and downloads | Add descriptions, related papers, download links, and files. | -Use **Save changes**, then **Open preview in new tab** to see saved edits. +Use **Create** for a new entry or **Save changes** for an existing entry, then +**Open preview in new tab** to see saved edits. Saving stays local; [submit a pull request](#check-and-submit-your-pull-request) to publish. Uploads support up to **20 MB per file** and preserve existing files. ### Edit your member profile -Choose **Member profiles**, select your name or **Create an entry**, and fill in +Choose **Member profiles**, select your name or **New entry**, and fill in the form. Use **Upload a portrait** for a JPEG, PNG, or WebP photo, and adjust the crop alignment if needed. +For specialities and name variants, type an item and press **Enter** or choose +**Add**. Remove an item with its **×** button. Under **Your name as it appears on +publications**, add versions of your own name exactly as credited on your papers. + Avoid giving away your personal email address, as bots regularly scrape the web for email addresses. Prefer using your university or work email address. @@ -78,10 +84,22 @@ to share that. ### Add or edit publications -Choose **Publications**, select a paper or **Create an entry**, and fill in -the title, year, authors (one per line), venue, paper URL, and publication type. +Choose **Publications**, select a paper or **New entry**, and fill in +the title, year, authors, venue, paper URL, and publication type. +Use **Year** for the paper's publication year. +Choose **Publication type** from the dropdown. If the correct type is missing, +contact the site owner to have it added. +For authors, search website members and their published name variants, or type +another author's name. Press **Enter** or choose **Add** for each author, in the +order shown on the paper. Tags and other item lists work the same way; you can +also paste a list with line breaks. Add research areas and related datasets where relevant. +The page identifier is assigned automatically from the original title and cannot +be edited. If it is already in use, a number is added (for example, `paper-2`). +Changing the title later keeps the original URL and adds a redirect from a URL +based on the new title, also numbered if needed. Existing links remain valid. + Use the published paper URL when available. Include full papers, not abstract-only conference, poster, or demo entries. @@ -96,7 +114,8 @@ Use `*text*` for italics, not `\textit{text}` outside an equation. Full LaTeX documents and custom packages are not supported. Put extra details in **Additional notes**. -The archive shows 100 papers per page; search covers the entire archive. +The archive shows 100 papers per page. Use the research area buttons to narrow +the results, or combine them with text search; both cover the entire archive. ### Create or edit a personal page @@ -136,7 +155,7 @@ template folder or `static/~jane-doe/` custom site; having both fails the build. ### Add or edit datasets and downloads -Choose **Datasets and downloads**, select an entry or **Create an entry**, +Choose **Datasets and downloads**, select an entry or **New entry**, and add a title, summary, source / creator, and related publication if available. Under **Downloads**, add a label and link, or use **Upload a file** beside the link field. Save, preview the page, and check each download link. diff --git a/assets/js/site.js b/assets/js/site.js index 2b18fd4..e547dbd 100644 --- a/assets/js/site.js +++ b/assets/js/site.js @@ -72,15 +72,14 @@ document.addEventListener("DOMContentLoaded", () => { const errorState = document.querySelector("[data-publication-error]"); const emptyState = document.querySelector("[data-publication-empty]"); const controls = document.querySelector("[data-publication-controls]"); - const researchSelect = document.querySelector("[data-publication-research]"); + const researchButtons = [...document.querySelectorAll("[data-publication-research]")]; const resultCount = document.querySelector("[data-publication-count]"); const clearButton = document.querySelector("[data-publication-clear]"); - const filterButtons = [...document.querySelectorAll("[data-publication-tag]")]; const normalize = (text) => text.normalize("NFD").replace(/\p{M}/gu, "").replace(/[‘’]/g, "'").toLowerCase(); const pageSize = 100; let records; let indexRequest; - let activeTag = "all"; + let activeResearch = "all"; let currentPage = Number(archive.dataset.publicationPage); let revision = 0; let searchTimer; @@ -126,7 +125,7 @@ document.addEventListener("DOMContentLoaded", () => { }; const filterURL = (page = currentPage) => { const url = new URL(archive.dataset.publicationBase, location.href); - for (const [key, value] of [["q", searchInput.value.trim()], ["tag", activeTag], ["research", researchSelect?.value]]) { + for (const [key, value] of [["q", searchInput.value.trim()], ["research", activeResearch]]) { if (value && value !== "all") url.searchParams.set(key, value); } if (page > 1) url.searchParams.set("page", page); @@ -157,8 +156,17 @@ document.addEventListener("DOMContentLoaded", () => { if (currentPage < pageCount) addLink("Next", currentPage + 1, "next"); }); }; + const updateControls = () => { + researchButtons.forEach((button) => { + const selected = button.dataset.publicationResearch === activeResearch; + button.classList.toggle("is-active", selected); + button.setAttribute("aria-pressed", String(selected)); + }); + clearButton.disabled = !searchInput.value.trim() && activeResearch === "all"; + }; const applyFilter = async (updateURL = true) => { const requestRevision = ++revision; + updateControls(); if (!records) resultCount.textContent = "Loading publication search…"; try { await loadIndex(); } catch { if (requestRevision === revision) { @@ -170,25 +178,17 @@ document.addEventListener("DOMContentLoaded", () => { if (requestRevision !== revision) return; errorState.hidden = true; const terms = normalize(searchInput.value.trim()).split(/\s+/).filter(Boolean); - const research = researchSelect?.value || "all"; const matches = records.filter((record) => terms.every((term) => record.search.includes(term)) && - (activeTag === "all" || record.tags.includes(activeTag)) && - (research === "all" || record.research.includes(research))); + (activeResearch === "all" || record.research.includes(activeResearch))); const pageCount = Math.max(1, Math.ceil(matches.length / pageSize)); currentPage = Math.min(Math.max(1, currentPage), pageCount); const start = (currentPage - 1) * pageSize; results.replaceChildren(...matches.slice(start, start + pageSize).map(cardFor)); - filterButtons.forEach((button) => { - const selected = button.dataset.publicationTag === activeTag; - button.classList.toggle("is-active", selected); - button.setAttribute("aria-pressed", String(selected)); - }); emptyState.hidden = matches.length !== 0; resultCount.textContent = matches.length ? `Showing ${start + 1}–${Math.min(start + pageSize, matches.length)} of ${matches.length} publications` : "0 publications"; renderPagination(pageCount); - clearButton.disabled = !searchInput.value && activeTag === "all" && research === "all"; if (updateURL) history.replaceState(null, "", filterURL()); }; const restoreFilter = () => { @@ -197,15 +197,13 @@ document.addEventListener("DOMContentLoaded", () => { const url = new URL(location.href); const params = url.searchParams; searchInput.value = params.get("q") || params.get("author") || ""; - const tag = params.get("tag"); - activeTag = filterButtons.some((button) => button.dataset.publicationTag === tag) ? tag : "all"; const area = params.get("research"); - researchSelect.value = [...researchSelect.options].some((option) => option.value === area) ? area : "all"; + activeResearch = researchButtons.some((button) => button.dataset.publicationResearch === area) ? area : "all"; const page = params.get("page") || url.pathname.match(/\/page\/(\d+)\//)?.[1] || "1"; currentPage = /^[1-9]\d*$/.test(page) && Number.isSafeInteger(Number(page)) ? Number(page) : 1; - const filtered = searchInput.value || activeTag !== "all" || researchSelect.value !== "all"; + const filtered = searchInput.value || activeResearch !== "all"; + updateControls(); if (records || filtered || params.has("page")) applyFilter(false); - clearButton.disabled = !filtered; }; const changeFilter = () => { clearTimeout(searchTimer); @@ -218,15 +216,13 @@ document.addEventListener("DOMContentLoaded", () => { currentPage = 1; searchTimer = setTimeout(() => applyFilter(), 150); }); - researchSelect.addEventListener("change", changeFilter); - filterButtons.forEach((button) => button.addEventListener("click", () => { - activeTag = button.dataset.publicationTag || "all"; + researchButtons.forEach((button) => button.addEventListener("click", () => { + activeResearch = button.dataset.publicationResearch; changeFilter(); })); clearButton.addEventListener("click", () => { searchInput.value = ""; - activeTag = "all"; - researchSelect.value = "all"; + activeResearch = "all"; changeFilter(); searchInput.focus(); }); diff --git a/content/people/jana-vatter.md b/content/people/jana-vatter.md index 92e5ad0..3d26ffe 100644 --- a/content/people/jana-vatter.md +++ b/content/people/jana-vatter.md @@ -2,7 +2,7 @@ name = "Jana Vatter" slug = "jana-vatter" role = "PhD Student" -status = "current" +status = "alumni" start_date = "2022" location = "Munich, Germany" research = ["distributed-machine-learning", "data-management"] @@ -10,4 +10,7 @@ interests = ["Graph Neural Networks", "Distributed Deep Learning", "Graph Proces portrait = "/images/people/jana-vatter.jpg" linkedin = "https://www.linkedin.com/in/jana-vatter/" # Ongoing PhD membership and 2022 start confirmed by the maintainer. +end_date = "2026-10" +scholar = "https://scholar.google.com/citations?user=rHSR_fwAAAAJ&hl=de" +researchgate = "https://www.researchgate.net/scientific-contributions/Jana-Vatter-2251660107" +++ diff --git a/content/people/michael-dangana.md b/content/people/michael-dangana.md index 663acf1..fbf6958 100644 --- a/content/people/michael-dangana.md +++ b/content/people/michael-dangana.md @@ -2,14 +2,18 @@ name = "Michael Dang'ana" slug = "michael-dangana" role = "PhD Student" -status = "current" +status = "alumni" start_date = "2020" # Member correction: PhD since 2020, expected graduation in 2026 (not a confirmed end date). location = "Toronto, Canada" research = ["data-management"] email = "michael.dangana@mail.utoronto.ca" -linkedin = "https://www.linkedin.com/in/michaeldangana" +linkedin = "https://www.linkedin.com/in/michaelkdangana" author_names = ["Michael Dang'ana", "Michael Dangana", "Michael Dang’ana"] interests = ["High-Performance Computing", "Data Processing", "Cloud Resource Management"] portrait = "/images/people/michael-dangana.jpg" +end_date = "2026-08" +scholar = "https://scholar.google.com/citations?user=cOe8jYoAAAAJ&hl=en" +dblp = "https://dblp.org/pid/254/3540.html" +researchgate = "https://www.researchgate.net/profile/Michael-Dangana" +++ diff --git a/content/people/shiquan-zhang.md b/content/people/shiquan-zhang.md index b786dc8..ec697ae 100644 --- a/content/people/shiquan-zhang.md +++ b/content/people/shiquan-zhang.md @@ -2,7 +2,7 @@ name = "Shiquan Zhang" slug = "shiquan-zhang" role = "PhD Student" -status = "current" +status = "alumni" start_date = "2021-09" location = "Toronto, Canada" research = ["data-management"] @@ -10,4 +10,9 @@ email = "shiquan.zhang@mail.utoronto.ca" author_names = ["Shiquan Zhang"] interests = ["Blockchain", "Consensus Protocols", "Zero-Knowledge Proofs", "Distributed Systems"] portrait = "/images/people/shiquan-zhang.jpg" +end_date = "2026-10" +scholar = "https://scholar.google.com/citations?user=Bg-PYGgAAAAJ&hl=en" +linkedin = "https://www.linkedin.com/in/shiquan-zhang-0535752bb/?isSelfProfile=false" +dblp = "https://dblp.org/pid/151/6081.html" +researchgate = "https://www.researchgate.net/scientific-contributions/Shiquan-Zhang-2209030147" +++ diff --git a/content/people/yunhao-mao.md b/content/people/yunhao-mao.md index d7f6027..debdbac 100644 --- a/content/people/yunhao-mao.md +++ b/content/people/yunhao-mao.md @@ -2,7 +2,7 @@ name = "Yunhao Mao" slug = "yunhao-mao" role = "PhD Student" -status = "current" +status = "alumni" start_date = "2020-09" location = "Toronto, Canada" research = ["data-management"] @@ -12,4 +12,8 @@ interests = ["Distributed Systems", "CRDTs", "Blockchain"] portrait = "/images/people/yunhao-mao.png" email = "yunhao.mao@mail.utoronto.ca" # Contact address supplied in the maintainer's group-member list, 2026-09-09. +end_date = "2026-10" +scholar = "https://scholar.google.com/citations?user=sWvruhgAAAAJ&hl=en" +github = "https://github.com/yunhaom94" +researchgate = "https://www.researchgate.net/scientific-contributions/Yunhao-Mao-2236336402" +++ diff --git a/data/content_schema.json b/data/content_schema.json index ff9bc01..eaf7039 100644 --- a/data/content_schema.json +++ b/data/content_schema.json @@ -164,7 +164,8 @@ "label": "Specialities", "type": "strings", "default": [], - "help": "One speciality per line, for example Distributed Systems or Quantum Computing." + "suggestions_source": "saved_values", + "help": "Add your specialities, for example Distributed Systems or Quantum Computing." }, { "key": "portrait", @@ -257,11 +258,11 @@ }, { "key": "author_names", - "label": "Publication name variants", + "label": "Your name as it appears on publications", "type": "strings", "default": [], "section": "Publications and awards", - "help": "One name per line, exactly as it appears in publication author lists." + "help": "Add each version of your own name used in publication author lists, for example Jane Doe or J. Doe. Use the exact spelling from the paper so publications can be linked to your profile." }, { "key": "awards", @@ -312,10 +313,12 @@ }, { "key": "date", - "label": "Created", + "label": "Entry creation date", "type": "string", "default": "", - "seed": "date" + "editor": false, + "seed": "date", + "help": "Optional date this website entry was created, used for page metadata and feeds. Leave blank if unknown. Use Year below for the paper's publication year." }, { "key": "year", @@ -330,6 +333,8 @@ "label": "Authors", "type": "strings", "default": [], + "suggestions_source": "member_names", + "help": "Search website members or type any author's name. Add authors in the order shown on the paper, using their published names.", "required": true }, { @@ -352,7 +357,23 @@ "label": "Publication type", "type": "string", "default": "Conference Paper", - "required": true + "required": true, + "options": [ + "Conference Paper", + "Journal Article", + "Workshop Paper", + "Book Chapter", + "Book", + "Edited Book", + "ArXiv Preprint", + "Preprint", + "Technical Report", + "Doctoral Thesis", + "Master's Thesis", + "Dataset", + "Software" + ], + "help": "Choose the publication's format. If the correct option is not available, contact the site owner to have it added." }, { "key": "research", @@ -382,6 +403,7 @@ "key": "tags", "label": "Tags", "type": "strings", + "suggestions_source": "saved_values", "default": [] }, { @@ -690,7 +712,7 @@ "label": "Previous website paths", "type": "strings", "default": [], - "help": "Optional redirects, one /path/ per line." + "help": "Add previous website paths such as /old-page/ to redirect them to this page." } ] } diff --git a/hugo.yaml b/hugo.yaml index 0927a2d..f91bcd7 100644 --- a/hugo.yaml +++ b/hugo.yaml @@ -52,19 +52,6 @@ params: success: "#003300" news: elfsight_app_id: "fcaa2115-34c7-4dbb-b62f-daf5be6f9039" - publication_filters: - - label: "Quantum Systems" - slug: "quantum-systems" - - label: "Federated Learning" - slug: "federated-learning" - - label: "Graph Systems" - slug: "graph-systems" - - label: "Blockchain" - slug: "blockchain" - - label: "Cloud Systems" - slug: "cloud-systems" - - label: "Benchmarks" - slug: "benchmarking" social: - name: LinkedIn url: https://www.linkedin.com/company/middleware-systems-research-group/ diff --git a/layouts/partials/people/now.html b/layouts/partials/people/now.html new file mode 100644 index 0000000..9c71d71 --- /dev/null +++ b/layouts/partials/people/now.html @@ -0,0 +1,2 @@ +{{/* Separate the clock so boundary tests can supply an exact, non-ticking instant. */}} +{{ return now }} diff --git a/layouts/partials/people/roster.html b/layouts/partials/people/roster.html index e05fbab..12c5839 100644 --- a/layouts/partials/people/roster.html +++ b/layouts/partials/people/roster.html @@ -1,7 +1,7 @@ {{/* One build-time classification shared by the people and research pages. */}} {{ $current := slice }} {{ $alumni := slice }} -{{ $now := now }} +{{ $now := partial "people/now.html" . }} {{ range (site.GetPage "/people").RegularPages }} {{ partial "content/validate.html" (dict "page" . "schema" hugo.Data.content_schema.types.people) }} {{ $status := .Params.status }} diff --git a/layouts/publications/archive.html b/layouts/publications/archive.html index 760714e..1a71193 100644 --- a/layouts/publications/archive.html +++ b/layouts/publications/archive.html @@ -12,17 +12,11 @@

{{ .Title }}