Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@ Instructions: Add a subsection under `[Unreleased]` for additions, fixes, change

## [Unreleased]

### Added

- Formatted references are now a generated asset like any other, built when needed and regenerated when the bibliography, the citations, or the chosen style changes. `pretext generate references` is no longer a debugging-only step.
- A `citations` optional dependency (also part of `all`) installs `citeproc-py-styles`, which supplies the Citation Style Language (CSL) styles that journals use: `pip install pretext[citations]`.

### Changed

- Template publication files use the new `html/knowl/@exercise-reading` name (formerly `@exercise-readingquestion`), and their comments now list current defaults and values, including `read-aloud`, GeoGebra `play-button`, `slide-numbering`, and Beamer options.
- Naming a supported `journal` in the publication file now selects that journal's CSL style, so PreTeXt formats references and citations in every output format instead of BibTeX. These styles need `citeproc-py-styles` (see above); without it, references fall back to default formatting. Choose a different style with `common/citation-stylesheet-language/@style`.

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion pretext/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

VERSION = get_version("pretext", Path(__file__).parent.parent)

CORE_COMMIT = "cf64e2a6ed2d70d2c118306fbe8ab25e8522680f"
CORE_COMMIT = "ee0a7e135e3346f0b0c6192822d6942424cb4af0"


def activate() -> None:
Expand Down
10 changes: 10 additions & 0 deletions pretext/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"datafile",
"myopenmath",
"dynamic-subs",
"references",
"qrcode",
"gdscript",
],
Expand All @@ -45,6 +46,7 @@
"mermaid",
"myopenmath",
"dynamic-subs",
"references",
],
"latex": [
"webwork",
Expand All @@ -59,6 +61,7 @@
"mermaid",
"myopenmath",
"dynamic-subs",
"references",
],
"epub": [
"webwork",
Expand All @@ -73,6 +76,7 @@
"mermaid",
"myopenmath",
"dynamic-subs",
"references",
],
"epub_nozip": [
"webwork",
Expand All @@ -87,6 +91,7 @@
"mermaid",
"myopenmath",
"dynamic-subs",
"references",
],
"kindle": [
"webwork",
Expand All @@ -101,6 +106,7 @@
"mermaid",
"myopenmath",
"dynamic-subs",
"references",
],
"braille": [
"webwork",
Expand All @@ -114,9 +120,11 @@
"mermaid",
"myopenmath",
"dynamic-subs",
"references",
],
"revealjs": [
"webwork",
"references",
"latex-image",
"sageplot",
"asymptote",
Expand All @@ -139,6 +147,7 @@
"mermaid",
"myopenmath",
"dynamic-subs",
"references",
],
"webwork": [
"webwork",
Expand All @@ -157,6 +166,7 @@
"mermaid",
"myopenmath",
"dynamic-subs",
"references",
"gdscript",
],
}
Expand Down
133 changes: 103 additions & 30 deletions pretext/project/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,6 @@ def original_source_element(self) -> ET._Element:
source_doc = ET.parse(self.source_abspath())
for _ in range(25):
source_doc.xinclude()
print("Type of source_doc: ", type(source_doc))
return source_doc.getroot()

def source_element(self) -> ET._Element:
Expand Down Expand Up @@ -520,6 +519,66 @@ def source_element_with_ids(self) -> ET._Element:
def publication_abspath(self) -> Path:
return self._project.publication_abspath() / self.publication

def publication_csl_settings(self) -> t.Tuple[t.Optional[str], t.Optional[str]]:
"""
The journal name and the CSL style named in the publication file,
either of which may be absent. Between them they decide whether
references and citations are rendered by CSL at all, and which
style does it.

Read from the publication file directly: answering from there
saves a publisher variable report -- a full XSL pass over the
source -- on every build that could not possibly need one.
"""
try:
publication = ET.parse(self.publication_abspath())
except Exception:
# a malformed publication file is reported, loudly, elsewhere
return (None, None)

def first(xpath: str) -> t.Optional[str]:
values = publication.xpath(xpath)
assert isinstance(values, t.List)
value = str(values[0]).strip() if values else ""
return value or None

return (
first("/publication/common/journal/@name"),
first("/publication/common/citation-stylesheet-language/@style"),
)

def journal_csl_style(self, journal: str) -> t.Optional[str]:
"""
The CSL style a journal supplies, from core's list of supported
journals, or None for a journal without one (or an unknown one).
Journal codes are matched regardless of case, as core does.
"""
journals_xml = (
resources.resource_base_path() / "core" / "journals" / "journals.xml"
)
try:
journals = ET.parse(journals_xml)
except Exception as e:
log.debug(f"Unable to read the list of journals: {e}", exc_info=True)
return None
styles = journals.xpath(
"/ptx-journals/journal[code = $code]/citation-stylesheet-language/@style",
code=journal.lower(),
)
assert isinstance(styles, t.List)
return str(styles[0]) if styles else None

def publication_uses_csl(self) -> bool:
"""
Whether the publication file opts into CSL styles for references
and citations, by naming a style outright or a journal that
supplies one.
"""
journal, csl_style = self.publication_csl_settings()
if csl_style is not None:
return True
return journal is not None and self.journal_csl_style(journal) is not None

def output_dir_abspath(self) -> Path:
if self.is_standalone() and self.output_dir is None:
if self.format == Format.PDF or self.compression == Compression.SCORM:
Expand Down Expand Up @@ -708,6 +767,16 @@ def generate_asset_table(self) -> pt.AssetTable:
for node in setups:
assert isinstance(node, ET._Element)
hash.update(ET.tostring(node))
# For references, the style is as much an input as the
# bibliography itself: changing journals must regenerate. Both
# publication file entries are hashed, since either can pick the
# style. (A style that changes inside core, for a journal whose
# name stays put, is caught instead by the assembly-time check
# against the style stamped on the generated file.)
if asset == "references":
for setting in self.publication_csl_settings():
if setting is not None:
hash.update(setting.encode("utf-8"))
# Finally, we store the hash as a string in the dictionary.
asset_hash_dict[asset] = hash.hexdigest()
return asset_hash_dict
Expand Down Expand Up @@ -1239,21 +1308,14 @@ def generate_assets(
# without a build. A no-op when a build already ran the check.
self.check_schema()

# To help with debugging, we are temporarily adding a reference generation step here. The only way this will be called is if `pretext generate references` is called explicitly.
if requested_asset_types == ("references",):
try:
core.references(
xml_source=self.source_abspath(),
pub_file=self.publication_abspath().as_posix(),
stringparams=self.stringparams.copy(),
xmlid_root=xmlid,
dest_dir=self.generated_dir_abspath() / "references",
)
except Exception as e:
log.error(f"Unable to generate some references:\n {e}")
log.debug(e, exc_info=True)
finally:
return
# Whether references were asked for by name, which changes how a
# project that has not opted into CSL styles is answered below.
references_requested = (
requested_asset_types is not None
and "references" in requested_asset_types
and "ALL" not in requested_asset_types
)

# To help with debugging, we are temporarily adding a stack generation step here. The only way this will be called is if `pretext generate stack` is called explicitly.
if requested_asset_types == ("stack",):
try:
Expand Down Expand Up @@ -1303,6 +1365,18 @@ def generate_assets(
log.debug(
f"Based on format {self.format}, assets to be generated are: {requested_asset_types}."
)
# Rendering references and citations with CSL is opt-in: without a
# journal or a style named in the publication file there is nothing
# to render them with, and core would rightly object.
if "references" in requested_asset_types and not self.publication_uses_csl():
if references_requested:
log.warning(
"References are rendered with a Citation Style Language (CSL) style, "
"which this project has not asked for. Name a style as "
"`citation-stylesheet-language/@style`, or a journal that has one as `journal/@name`, "
"in the publication file's `common` element. No references will be generated."
)
requested_asset_types.remove("references")
# We always build the asset hash table, even if only_changed=True: this tells us which assets need to be built, and how to update the saved asset hash table at the end of the method.
# utils.clean_asset_table purges any asset types from the loaded table that are no longer in the target.
source_asset_table = self.generate_asset_table()
Expand Down Expand Up @@ -1593,20 +1667,19 @@ def generate_assets(
except Exception as e:
log.error(f"Unable to generate some datafiles:\n {e}")
log.debug(e, exc_info=True)
# The following code will eventually be needed, but for now, we leave as a placeholder.
# if "references" in assets_to_generate and debug_references:
# try:
# core.references(
# xml_source=self.source_abspath(),
# pub_file=self.publication_abspath().as_posix(),
# stringparams=stringparams_copy,
# xmlid_root=xmlid,
# dest_dir=self.generated_dir_abspath() / "references",
# )
# successful_assets.append("references")
# except Exception as e:
# log.error(f"Unable to generate some references:\n {e}")
# log.debug(e, exc_info=True)
if "references" in assets_to_generate:
try:
core.references(
xml_source=self.source_abspath(),
pub_file=self.publication_abspath().as_posix(),
stringparams=stringparams_copy,
xmlid_root=xmlid,
dest_dir=self.generated_dir_abspath() / "references",
)
successful_assets.append("references")
except Exception as e:
log.error(f"Unable to generate some references:\n {e}")
log.debug(e, exc_info=True)
# Delete temporary directories left behind by core:
try:
core.release_temporary_directories(any_log_level=clean_tmp_dirs)
Expand Down
7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,12 @@ pretext = "pretext.cli:main"
[project.optional-dependencies]
homepage = ["pelican[markdown]>=4.10,<5"]
prefigure = ["prefig[text]>=0.7.4,<0.8"]
all = ["pelican[markdown]>=4.10,<5", "prefig[text]>=0.7.4,<0.8"]
citations = ["citeproc-py-styles>=0.1,<0.2"]
all = [
"pelican[markdown]>=4.10,<5",
"prefig[text]>=0.7.4,<0.8",
"citeproc-py-styles>=0.1,<0.2",
]

# Development dependencies
# ------------------------
Expand Down
22 changes: 22 additions & 0 deletions tests/examples/projects/journal-bibliography/project.ptx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<project>
<targets>
<target name="web">
<format>html</format>
<source>source/main.ptx</source>
<publication>publication.xml</publication>
<output-dir>output/web</output-dir>
</target>
</targets>
<executables>
<latex>latex</latex>
<pdflatex>pdflatex</pdflatex>
<xelatex>xelatex</xelatex>
<asy>asy</asy>
<sage>sage</sage>
<pdfeps>pdftops</pdfeps>
<pdfcrop>pdf-crop-margins</pdfcrop>
<pageres>pageres</pageres>
<node>node</node>
</executables>
</project>
5 changes: 5 additions & 0 deletions tests/examples/projects/journal-bibliography/publication.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<publication>
<common>
<journal name="bull-amer-math-soc"/>
</common>
</publication>
23 changes: 23 additions & 0 deletions tests/examples/projects/journal-bibliography/source/main.ptx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<pretext>
<article xml:id="article-journal-bibliography">
<title>An Article For A Journal</title>
<section xml:id="sec-one">
<title>One</title>
<p>A result appears in <xref ref="biblio-lay"/> and also in <xref ref="biblio-judson"/>.</p>
</section>
<backmatter>
<references xml:id="references-backmatter">
<title>References</title>
<biblio xml:id="biblio-judson" type="book">
<author><name><given>Thomas</given><family>Judson</family></name></author>
<title>Abstract Algebra: Theory and Applications</title>
</biblio>
<biblio xml:id="biblio-lay" type="article">
<author><name><given>David C.</given><family>Lay</family></name></author>
<title>Subspaces and Echelon Forms</title>
</biblio>
</references>
</backmatter>
</article>
</pretext>
Loading
Loading