From 441c4d00f90b7b8fcb06e83c8c98c384115c8ddc Mon Sep 17 00:00:00 2001 From: Shyue Ping Ong Date: Sat, 22 Oct 2022 06:42:01 -0700 Subject: [PATCH] Update changelog. --- .pre-commit-config.yaml | 2 +- CHANGES.rst | 13 +++ docs/_static/sphinx_highlight.js | 144 +++++++++++++++++++++++++++++++ tasks.py | 56 ++++++------ 4 files changed, 190 insertions(+), 25 deletions(-) create mode 100644 docs/_static/sphinx_highlight.js diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 467d9494e19..d4c4e804cf6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,4 @@ -exclude: ^(docs|.*test_files|cmd_line) +exclude: ^(docs|.*test_files|cmd_line|tasks.py) ci: autoupdate_schedule: monthly diff --git a/CHANGES.rst b/CHANGES.rst index d8c0b1a6d18..e3bb9850350 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,19 @@ Change log ========== +v2022.10.22 +----------- +* Allow env settings to override .pmgrc.yaml (@janosh) +* Add EntryLike type (@janosh) +* Update spglib to 2.0+. +* @cnncnnzh Method to plot the atom-resolved phonon band structures. +* @jmmshn More Flexible reproduction of VASP's optical code +* @Ameyanagi Fix the sorting of the FEFF IO module to create ATOMS input. +* @JaGeo Extend the ThermalDisplacementMatrices class to read cif files in P1 format. +* @rkingsbury Changes to FEFF I/O to support the use of non-periodic input structures. +* @jmmshn Merge Waverder and Wavederf +* @jmmshn Set the structure_charge while parsing Potcar + v2022.9.21 ---------- * @chunweizhu fix the bugs when running `TEMCalculator` diff --git a/docs/_static/sphinx_highlight.js b/docs/_static/sphinx_highlight.js new file mode 100644 index 00000000000..aae669d7ea6 --- /dev/null +++ b/docs/_static/sphinx_highlight.js @@ -0,0 +1,144 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + parent.insertBefore( + span, + parent.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(SphinxHighlight.highlightSearchWords); +_ready(SphinxHighlight.initEscapeListener); diff --git a/tasks.py b/tasks.py index 173dc1df852..36668614705 100644 --- a/tasks.py +++ b/tasks.py @@ -1,4 +1,5 @@ -"""Pyinvoke tasks.py file for automating releases and admin stuff. +""" +Pyinvoke tasks.py file for automating releases and admin stuff. Author: Shyue Ping Ong """ @@ -11,15 +12,16 @@ import webbrowser import requests -from invoke import Context, task +from invoke import task from monty.os import cd from pymatgen.core import __version__ as CURRENT_VER @task -def make_doc(ctx: Context) -> None: - """Generate API documentation + run Sphinx. +def make_doc(ctx): + """ + Generate API documentation + run Sphinx. :param ctx: """ @@ -81,8 +83,9 @@ def make_doc(ctx: Context) -> None: @task -def make_dash(ctx: Context) -> None: - """Make customized doc version for Dash. +def make_dash(ctx): + """ + Make customized doc version for Dash :param ctx: """ @@ -146,8 +149,9 @@ def submit_dash_pr(ctx, version): @task -def update_doc(ctx: Context) -> None: - """Update the web documentation. +def update_doc(ctx): + """ + Update the web documentation. :param ctx: """ @@ -159,8 +163,9 @@ def update_doc(ctx: Context) -> None: @task -def publish(ctx: Context) -> None: - """Upload release to Pypi using twine. +def publish(ctx): + """ + Upload release to Pypi using twine. :param ctx: """ @@ -188,7 +193,8 @@ def set_ver(ctx, version): @task def release_github(ctx, version): - """Release to Github using Github API. + """ + Release to Github using Github API. :param ctx: """ @@ -216,7 +222,8 @@ def release_github(ctx, version): @task def post_discourse(ctx, version): - """Post release announcement to http://discuss.matsci.org/c/pymatgen. + """ + Post release announcement to http://discuss.matsci.org/c/pymatgen. :param ctx: """ @@ -240,12 +247,12 @@ def post_discourse(ctx, version): @task -def update_changelog(ctx, version=None, sim=False): - """Create a preliminary change log using the git logs. +def update_changelog(ctx, version=datetime.datetime.now().strftime("%Y.%-m.%-d"), sim=False): + """ + Create a preliminary change log using the git logs. :param ctx: """ - version = version or datetime.datetime.now().strftime("%Y.%-m.%-d") output = subprocess.check_output(["git", "log", "--pretty=format:%s", f"v{CURRENT_VER}..HEAD"]) lines = [] misc = [] @@ -256,8 +263,9 @@ def update_changelog(ctx, version=None, sim=False): contrib, pr_name = m.group(2).split("/", 1) response = requests.get(f"https://api.github.com/repos/materialsproject/pymatgen/pulls/{pr_number}") lines.append(f"* PR #{pr_number} from @{contrib} {pr_name}") - if "body" in response.json(): - for ll in response.json()["body"].split("\n"): + json_resp = response.json() + if "body" in json_resp and json_resp["body"]: + for ll in json_resp["body"].split("\n"): ll = ll.strip() if ll in ["", "## Summary"]: continue @@ -282,13 +290,13 @@ def update_changelog(ctx, version=None, sim=False): @task -def release(ctx, version=None, nodoc=False): - """Run full sequence for releasing pymatgen. +def release(ctx, version=datetime.datetime.now().strftime("%Y.%-m.%-d"), nodoc=False): + """ + Run full sequence for releasing pymatgen. :param ctx: :param nodoc: Whether to skip doc generation. """ - version = version or datetime.datetime.now().strftime("%Y.%-m.%-d") ctx.run("rm -r dist build pymatgen.egg-info", warn=True) set_ver(ctx, version) if not nodoc: @@ -305,8 +313,9 @@ def release(ctx, version=None, nodoc=False): @task -def open_doc(ctx: Context) -> None: - """Open local documentation in web browser. +def open_doc(ctx): + """ + Open local documentation in web browser. :param ctx: """ @@ -315,7 +324,6 @@ def open_doc(ctx: Context) -> None: @task -def lint(ctx: Context) -> None: - """Run linters.""" +def lint(ctx): for cmd in ["pycodestyle", "mypy", "flake8", "pydocstyle"]: ctx.run(f"{cmd} pymatgen")