Skip to content

Commit 5a24972

Browse files
committed
Fix #238: Add user portal schema doc
* Describe the portal schema from the perspective of a user trying to add or change the config. * Amend the glossary with more terms or redefine some terms * Config changes: * Use css/custom.css instead of css/style.css * Use SUSE font * Apply styling for glossary * Add glossary processing to conf.py to make letters appear in the body and on the right side toc.
1 parent 5e15069 commit 5a24972

16 files changed

Lines changed: 1075 additions & 16 deletions
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,11 @@ html {
55
--pst-font-family-base: SUSE, var(--pst-font-family-base-system);
66
--pst-font-family-heading: SUSE, sans-serif, var(--pst-font-family-base-system);
77
--pst-font-family-monospace: Courier, var(--pst-font-family-monospace-system);
8-
}
8+
}
9+
10+
/* Add a horizontal line below the glossary letter titles */
11+
.glossary-letter-title {
12+
border-bottom: 1px solid var(--pst-color-border); /* Use theme's border color */
13+
padding-bottom: 0.5em; /* Space between title text and the line */
14+
margin-bottom: 1em; /* Space between the line and the content below */
15+
}

docs/source/conf.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99
from datetime import datetime
1010
from pathlib import Path
1111
import sys
12+
from typing import Self
1213

14+
from docutils import nodes
1315
from sphinx.application import Sphinx
1416
from sphinx.errors import ExtensionError
17+
from sphinx.transforms import SphinxTransform
1518
from sphinx.util import logging
1619

1720
from docbuild.__about__ import __version__
@@ -287,7 +290,143 @@ def run_toml_generator(app: Sphinx) -> None:
287290
generate_toml_reference(toml_input, rst_output, **config_data)
288291

289292

293+
class GlossaryRearranger(SphinxTransform):
294+
"""Group glossary terms by first letter into section nodes.
295+
296+
The glossary directive emits a flat definition list. This transform rewrites
297+
it into per-letter sections so each letter gets its own heading and anchor.
298+
"""
299+
300+
default_priority = 400
301+
302+
@staticmethod
303+
def _is_glossary_definition_list(node: nodes.definition_list) -> bool:
304+
"""Return whether a definition list belongs to a glossary block."""
305+
parent = node.parent
306+
if parent is None:
307+
return False
308+
309+
return "glossary" in parent.get("classes", []) or "glossary" in node.get(
310+
"classes", []
311+
)
312+
313+
@staticmethod
314+
def _group_terms_by_letter(
315+
node: nodes.definition_list,
316+
) -> dict[str, list[nodes.definition_list_item]]:
317+
"""Group glossary items by the first letter of each term."""
318+
groups: dict[str, list[nodes.definition_list_item]] = {}
319+
for item in list(node.children):
320+
if not isinstance(item, nodes.definition_list_item):
321+
continue
322+
323+
term_node = item.next_node(nodes.term)
324+
if not term_node:
325+
continue
326+
327+
term_text = term_node.astext().strip()
328+
if not term_text:
329+
continue
330+
331+
letter = term_text[0].upper()
332+
groups.setdefault(letter, []).append(item)
333+
334+
return groups
335+
336+
@staticmethod
337+
def _build_letter_sections(
338+
groups: dict[str, list[nodes.definition_list_item]],
339+
) -> list[nodes.section]:
340+
"""Build one section per glossary letter group."""
341+
new_sections: list[nodes.section] = []
342+
for letter in sorted(groups.keys()):
343+
sec = nodes.section(
344+
ids=[letter],
345+
classes=["glossary-section", f"glossary-letter-{letter.lower()}"],
346+
)
347+
sec += nodes.title(letter, letter, classes=["glossary-letter-title"])
348+
349+
letter_list = nodes.definition_list()
350+
letter_list.extend(groups[letter])
351+
sec += letter_list
352+
new_sections.append(sec)
353+
354+
return new_sections
355+
356+
@staticmethod
357+
def _replace_with_sections(
358+
parent: nodes.Element,
359+
node: nodes.definition_list,
360+
new_sections: list[nodes.section],
361+
) -> None:
362+
"""Replace glossary list with generated sections in the proper parent."""
363+
if getattr(parent, "tagname", "") == "glossary" and parent.parent is not None:
364+
# Sphinx's ToC collector ignores <section> nodes nested inside a
365+
# <glossary> node. Hoist sections one level up so they are indexed.
366+
grandparent = parent.parent
367+
idx = grandparent.index(parent)
368+
for sec in reversed(new_sections):
369+
grandparent.insert(idx, sec)
370+
parent.remove(node)
371+
if not parent.children:
372+
grandparent.remove(parent)
373+
return
374+
375+
node.replace_self(new_sections)
376+
377+
def apply(self: Self) -> None:
378+
"""Rewrite glossary definition lists into grouped letter sections."""
379+
for node in list(self.document.findall(nodes.definition_list)):
380+
if node.get("_glossary_rearranged", False):
381+
continue
382+
383+
if not self._is_glossary_definition_list(node):
384+
continue
385+
386+
groups = self._group_terms_by_letter(node)
387+
if not groups:
388+
continue
389+
390+
new_sections = self._build_letter_sections(groups)
391+
parent = node.parent
392+
if not isinstance(parent, nodes.Element):
393+
continue
394+
395+
self._replace_with_sections(parent, node, new_sections)
396+
397+
node["_glossary_rearranged"] = True
398+
logger.info("GlossaryRearranger: Grouped %d letters.", len(groups))
399+
400+
401+
class GlossaryToCBuilder(SphinxTransform):
402+
"""Add glossary term permalinks used by the rendered HTML output.
403+
404+
This transform keeps generated glossary terms linkable via header links.
405+
"""
406+
407+
default_priority = 950
408+
409+
def apply(self) -> None:
410+
"""Attach permalink references to glossary term nodes."""
411+
# Inject Term Permalinks (existing logic)
412+
for term_node in self.document.findall(nodes.term):
413+
t_ids = term_node.get("ids", [])
414+
if t_ids and not any(
415+
isinstance(c, nodes.reference) for c in term_node.children
416+
):
417+
t_ref = nodes.reference(
418+
"",
419+
"#",
420+
refuri=f"#{t_ids[0]}",
421+
classes=["headerlink"],
422+
reftitle="Permalink",
423+
)
424+
term_node += t_ref
425+
426+
290427
def setup(app: Sphinx) -> None:
291428
"""Sphinx setup function to connect the TOML generator to the build process."""
292429
# This hook ensures the file exists before Sphinx tries to 'include' it
293430
app.connect("builder-inited", run_toml_generator)
431+
app.add_transform(GlossaryRearranger)
432+
app.add_transform(GlossaryToCBuilder)

docs/source/glossary.rst

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
Glossary
22
========
33

4-
:term:`A <ADoc>` | :term:`C <Changelog>` | :term:`D <DAPS>` | :term:`G <GIL>` | :term:`I <IPython>` | :term:`L <Lifecycle>` | :term:`M <Module>` | :term:`O <Option>` | :term:`P <Package>` | :term:`R <RNC>` | :term:`S <SemVer>` | :term:`U <UV>` | :term:`V <VENV>` | :term:`X <XML>`
5-
64
For Python specific terms, look into:
75

86
* https://docs.python.org/3/glossary.html
@@ -52,16 +50,24 @@ For Python specific terms, look into:
5250
See also :term:`Asynchronous Programming`, :term:`asyncio`, :term:`Parallelism`
5351

5452
DAPS
55-
The *Documentation and Publishing System* (DAPS) is a tool to build documentation from DocBook or ADoc files.
53+
The *DocBook and Publishing System* (DAPS) is a tool to build documentation from DocBook or ADoc files.
5654
It is used to generate various output formats such as HTML, PDF, and EPUB.
5755

58-
See https://github.com/openSUSE/daps
56+
See :term:`DocBook`, :term:`ADoc`, :term:`DC File`
57+
58+
See also https://github.com/openSUSE/daps
5959

6060
DC File
61-
The *DAPS Configuration File* (DC file) is a configuration file used by DAPS to define parameters for building documentation. For example, it contains information about the entry file, what stylesheets to use, and other build options.
61+
The *DAPS Configuration* File ("DC file") is a configuration file used by DAPS to define parameters for building documentation. For example, it contains information about the entry file, what stylesheets to use, and other build options.
6262

6363
Deliverable
64-
The smallest unit of documentation that can be built. It's mapped to a DC File. A deliverable is usually being built in different formats.
64+
The smallest publishable unit of documentation. It is either dynamically
65+
generated by DAPS from source into various output formats or supplied
66+
as a pre-built deliverable.
67+
68+
See also :term:`DC File` and :term:`Pre-built Deliverable`
69+
70+
See class :class:`docbuild.models.deliverable.Deliverable`.
6571

6672
DocBook
6773
A semantic markup language based on :term:`XML` used for writing
@@ -70,7 +76,7 @@ For Python specific terms, look into:
7076
See also :term:`ADoc`, https://www.docbook.org
7177

7278
Docset
73-
Usually a release or version of a project. For example, ``15-SP6``.
79+
See :term:`Release`
7480

7581
Doctype
7682
A formal syntax to identify one or many set of documents.
@@ -79,6 +85,11 @@ For Python specific terms, look into:
7985

8086
See section :ref:`doctype-syntax`.
8187

88+
Docserv Config
89+
A deprecated XML configuration that defines products, releases, deliverables, and translations.
90+
91+
See :term:`Portal Config`.
92+
8293
GIL
8394
Global Interpreter Lock
8495
A mutex (a lock) in the standard CPython interpreter that ensures only one thread can execute Python bytecode at any given time within a single process. This lock effectively prevents multi-threaded, CPU-bound Python programs from achieving true parallelism on multi-core processors, as only one thread can run on one core at a time.
@@ -96,10 +107,16 @@ For Python specific terms, look into:
96107

97108
See section :ref:`use-ipython`.
98109

110+
Language
111+
The linguistic and regional setting for a deliverable. In this context, a language is defined by a combination of a language code and a country code. For example, ``en-us`` for English (United States).
112+
113+
See class :class:`docbuild.models.language.LanguageCode`.
114+
115+
99116
Lifecycle
100117
Describes the distinct stages a product goes through, from its initial introduction to the market until its eventual decline and retirement.
101118

102-
See class :class:`~docbuild.models.lifecycle.LifecycleFlag`.
119+
See class :class:`docbuild.models.lifecycle.LifecycleFlag`.
103120

104121
Module
105122
In Python context, a single Python file containing code.
@@ -136,10 +153,30 @@ For Python specific terms, look into:
136153

137154
See https://peps.python.org/
138155

156+
Portal Config
157+
XML configuration that describes all products, their releases,
158+
deliverables and translations if available.
159+
160+
See also :term:`XML`
161+
162+
Pre-built Deliverable
163+
A documentation artifact provided in its final form, generated by
164+
external toolchains rather than by DAPS. These deliverables are
165+
integrated as static files and do not require further processing or
166+
transformation.
167+
168+
See also :term:`DC File`
169+
139170
Product
140-
A abbreviated name for a SUSE product. For example, ``sles``.
171+
A distinct software offering or suite of components.
172+
It represents the top-level entity in the documentation hierarchy,
173+
under which specific releases and deliverables are organized.
174+
Every product is identified by a full formal name and a unique
175+
abbreviation used for internal references and build configurations.
176+
177+
See also :term:`Doctype`, :term:`Release`, :term:`Deliverable`
141178

142-
See class :class:`~docbuild.models.product.Product`.
179+
See class :class:`docbuild.models.product.Product`.
143180

144181
Pydantic
145182
A Python library for data validation and settings management using Python type annotations. It provides a way to define data models with strict type checking and validation rules, making it easier to ensure that the data your application works with is correct and consistent.
@@ -162,13 +199,27 @@ For Python specific terms, look into:
162199
RELAX NG
163200
RNC
164201
RNG
165-
A schema language for XML used to define and validate the structure
166-
and content of XML documents.
202+
*REgular LAnguage for XML Next Generation* is a schema language for XML
203+
used to define and validate the structure and content of XML documents.
167204
RNC is the compact syntax of RELAX NG, while RNG is the XML syntax.
168205
Both are equivalent in terms of expressiveness.
169206

170207
See https://relaxng.org/
171208

209+
Release
210+
A collection of deliverables associated with a specific version of a
211+
product.
212+
A release represents a published milestone in the product lifecycle,
213+
encompassing both beta and final (GA) states.
214+
215+
See also :term:`Docset`, :term:`Product`
216+
217+
Repository
218+
A centralized digital storage space for the documentation source code.
219+
Typically hosted on platforms such as GitHub or GitLab.
220+
221+
See class :class:`docbuild.models.repo.Repo`
222+
172223
Ruff
173224
A fast extensible linter and code formatter to improve code qualitiy
174225
and enforce style guidelines.
@@ -221,12 +272,30 @@ For Python specific terms, look into:
221272

222273
See section :ref:`prepare-devel-env`.
223274

275+
XInclude
276+
A standard XML mechanism used to assemble complex structures from
277+
smaller, independent fragments. In this project, it is used to
278+
modularize both documentation source files and the Portal Config.
279+
280+
See also :term:`DocBook`, :term:`Portal Config`, :term:`XML`
281+
282+
See https://www.w3.org/TR/xinclude-11/
283+
224284
XML
225285
The *eXtensible Markup Language* is a text-based markup
226286
language used to structure, store, and transport data in
227287
a format that is both human- and machine-readable.
228288

289+
XPath
290+
A query language used to navigate and address specific parts of an
291+
XML document. It enables tools and build scripts to precisely locate,
292+
retrieve, or manipulate data.
293+
294+
See https://www.w3.org/TR/xpath-10/
295+
229296
XSLT
230297
The *eXtensible Stylesheet Language for Transformations*
231298
is a language that transforms XML documents into other
232299
formats like HTML, plain text, or new XML structures.
300+
301+
See https://www.w3.org/TR/xslt-10/

docs/source/user/config/index.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
Viewing and Validating Configuration
2-
------------------------------------
2+
====================================
33

44
You can Use the :command:`config` subcommand to list or validate your current settings.
55

66
Listing Configuration
7-
~~~~~~~~~~~~~~~~~~~~~
7+
---------------------
88

99
To see the current merged configuration:
1010

@@ -15,7 +15,7 @@ To see the current merged configuration:
1515
Use the ``--flat`` flag to see the dotted-path format, or filter by ``--app`` or ``--env``.
1616

1717
Validating Configuration
18-
~~~~~~~~~~~~~~~~~~~~~~~~
18+
------------------------
1919

2020
To ensure your TOML files match the required schema:
2121

docs/source/user/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ User Guide
77

88
install
99
config
10+
portal-config/index
1011
run-docbuild
1112
config/index
1213
build

0 commit comments

Comments
 (0)