From a4f6500c2d4baa811ac4ae513f3e27a9300bf0e0 Mon Sep 17 00:00:00 2001 From: Xavier Raynaud Date: Thu, 25 Jan 2024 17:31:05 +0100 Subject: [PATCH] setup sphinx doc --- .github/workflows/doc.yml | 55 +++++ .github/workflows/update_ghpages.yml | 40 ---- sphinx/.gitignore | 1 + sphinx/Makefile | 20 ++ sphinx/README.org | 47 +++++ sphinx/about.rst | 34 +++ sphinx/conf.py | 301 +++++++++++++++++++++++++++ sphinx/contribute.rst | 53 +++++ sphinx/img/battinfologo.ico | Bin 0 -> 16853 bytes sphinx/img/battinfologo.jpg | Bin 0 -> 10467 bytes sphinx/index.rst | 32 +++ sphinx/requirements.txt | 30 +++ sphinx/ttl_to_rst.py | 168 +++++++++++++++ sphinx/util.el | 30 +++ 14 files changed, 771 insertions(+), 40 deletions(-) create mode 100644 .github/workflows/doc.yml delete mode 100644 .github/workflows/update_ghpages.yml create mode 100644 sphinx/.gitignore create mode 100644 sphinx/Makefile create mode 100644 sphinx/README.org create mode 100644 sphinx/about.rst create mode 100644 sphinx/conf.py create mode 100644 sphinx/contribute.rst create mode 100644 sphinx/img/battinfologo.ico create mode 100644 sphinx/img/battinfologo.jpg create mode 100644 sphinx/index.rst create mode 100644 sphinx/requirements.txt create mode 100644 sphinx/ttl_to_rst.py create mode 100644 sphinx/util.el diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml new file mode 100644 index 0000000..23c6f26 --- /dev/null +++ b/.github/workflows/doc.yml @@ -0,0 +1,55 @@ +name: "Sphinx: Render docs" + +on: + push: + branches: + - 'master' + +env: + GIT_USER_NAME: BattINFO Developers + GIT_USER_EMAIL: "BattINFO@big-map.org" + +jobs: + + updatepages: + + runs-on: ubuntu-latest + + permissions: + contents: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 #Uses python install action from here: https://github.com/marketplace?type=actions + with: + python-version: '3.10' # Replace with the desired Python version + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install Markdown==3.4.3 rdflib==6.3.2 # Add any other dependencies if needed + + - name: Render documentation from ttl + run: python sphinx/ttl_to_rst.py + + - name: Build HTML + uses: ammaraskar/sphinx-action@master + with: + docs-folder: "sphinx/" + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: documentationHTML + path: sphinx/_build/html/ + + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + if: github.ref == 'refs/heads/master' + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: sphinx/_build/html + diff --git a/.github/workflows/update_ghpages.yml b/.github/workflows/update_ghpages.yml deleted file mode 100644 index fe1e790..0000000 --- a/.github/workflows/update_ghpages.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Update GitHub Pages - -on: - push: - branches: - - master # Replace with the branch name you want to trigger the workflow on - -env: - GIT_USER_NAME: BattINFO Developers - GIT_USER_EMAIL: "BattINFO@big-map.org" - -jobs: - updatepages: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Set up Python - uses: actions/setup-python@v4 #Uses python install action from here: https://github.com/marketplace?type=actions - with: - python-version: '3.10' # Replace with the desired Python version - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install Markdown==3.4.3 rdflib==6.3.2 # Add any other dependencies if needed - - - name: Render documentation from md - run: python docs/scripts/md_to_html.py - - - name: Commit updated documentation - run: | - cd ${GITHUB_WORKSPACE} - git config --global user.email "${GIT_USER_EMAIL}" - git config --global user.name "${GIT_USER_NAME}" - git config pull.rebase false - git add . - git commit -m "Automatic update of github pages" - git push origin master diff --git a/sphinx/.gitignore b/sphinx/.gitignore new file mode 100644 index 0000000..9c5f578 --- /dev/null +++ b/sphinx/.gitignore @@ -0,0 +1 @@ +_build \ No newline at end of file diff --git a/sphinx/Makefile b/sphinx/Makefile new file mode 100644 index 0000000..5f473ab --- /dev/null +++ b/sphinx/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= -v +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/sphinx/README.org b/sphinx/README.org new file mode 100644 index 0000000..c06cd20 --- /dev/null +++ b/sphinx/README.org @@ -0,0 +1,47 @@ +#+TITLE: Documentation generation for BattInfo +* Requirements +** sphinx + installation instruction described [[https://www.sphinx-doc.org/en/master/usage/installation.html][here]] + #+begin_src + pip install sphinx + #+end_src +** install sphinx pydata theme + https://pydata-sphinx-theme.readthedocs.io/en/latest/index.html + #+begin_src + pip install pydata-sphinx-theme + #+end_src +** globabsubs extension + #+begin_src + pip install sphinxcontrib-globalsubs + #+end_src +** autosectionlabel + - part of default distribution + - We use ~autosectionlabel_prefix_document = True~ which means that the internal link must be prefixed by the file + name and a semi-column, see [[https://www.sphinx-doc.org/en/master/usage/extensions/autosectionlabel.html][here]]. + +* Workflow for editing + + In the ~Documentation~ directory, run from terminal + #+BEGIN_SRC sh + make html + #+END_SRC + + The command generates all the files for a static website and writes them in ~Documentation/_build/html~ + + All the files in this directory must then be copied to the ~BattInfo-doc~ [[https://github.com/BattMoTeam/BattMo-doc][repo]]. From there, make a *force* push. We do + not keep track of the history of the ouput files (html), but we of course keep track of the documentation source, directly in the + main repo ~BattInfo~. + + The deployment of the webpage can be followed from the [[https://github.com/BattMoTeam/BattMo-doc/actions][github pages section]] + + The result can be view at + + https://battmoteam.github.io/Battinfo-doc/ + +* Interesting Manuals +** sphinx manual + https://www.sphinx-doc.org/en/master/contents.html +** reStucturedText (rst) format + https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html +* Configuration file + - ~conf.py~ see https://www.sphinx-doc.org/en/master/usage/configuration.html diff --git a/sphinx/about.rst b/sphinx/about.rst new file mode 100644 index 0000000..e62d5d2 --- /dev/null +++ b/sphinx/about.rst @@ -0,0 +1,34 @@ +About Electrochemistry Domain Ontology +====================================== + +Contributors +------------ + +- Simon Clark, SINTEF, Norway +- Eibar Flores, SINTEF, Norway +- Francesca Lønstad Bleken, SINTEF, Norway +- Jesper Friis, SINTEF, Norway +- Casper Welzel Andersen, SINTEF, Norway +- Martin Uhrin, EPFL, Switzerland +- Simon Stier, Fraunhofer, Germany +- Marek Marcinek, Warsaw University of Technology, Poland +- Anna Szczesna, Warsaw University of Technology, Poland +- Miran Gaberscek, National Institute of Chemistry, Slovenia +- Deyana Stoytcheva, ICMAB, Spain +- Rosa Palacin, ICMAB, Spain +- Ingeborg-Helene Svenum, SINTEF, Norway +- Inga Gudem Ringdalen, SINTEF, Norway +- Emanuele Farhi, SOLEIL synchrotron, France + +Projects +-------- + +- `BIG-MAP `__; Grant Agreement No: 957189 + +License +------- + +The Battery Interface Domain Ontology is released under the `Creative +Commons Attribution 4.0 +International `__ +license (CC BY 4.0). diff --git a/sphinx/conf.py b/sphinx/conf.py new file mode 100644 index 0000000..709e862 --- /dev/null +++ b/sphinx/conf.py @@ -0,0 +1,301 @@ +# -*- coding: utf-8 -*- +# +# MATLAB Sphinx Documentation Test documentation build configuration file, created by +# sphinx-quickstart on Wed Jan 15 11:38:03 2014. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. +from __future__ import unicode_literals +import sys +import os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath(os.path.join('..'))) +matlab_src_dir = os.path.abspath('..') + +autoclass_content = 'both' +autodoc_member_order = 'bysource' + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = ['sphinxcontrib.globalsubs', + 'sphinx.ext.intersphinx', + 'sphinx.ext.autosectionlabel', + ] + +autosectionlabel_prefix_document = True + +global_substitutions = { +} + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'BattInfo' +copyright = '2021-2023' +author = 'Simon Clark' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +# version = '0.1' +# The full version, including alpha/beta/rc tags. +# release = '0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'pydata_sphinx_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. + +html_theme_options = { + # "show_nav_level": 4 +} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +html_logo = 'img/battinfologo.jpg' + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +html_favicon = 'img/battinfologo.ico' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] +html_css_files = ['css/custom.css'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +# htmlhelp_basename = 'MATLABSphinxDocumentationTestdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ +# ('index', 'MATLABSphinxDocumentationTest.tex', 'MATLAB Sphinx Documentation Test Documentation', +# 'Mark Mikofski', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +# man_pages = [ + # ('index', 'matlabsphinxdocumentationtest', 'MATLAB Sphinx Documentation Test Documentation', + # ['Mark Mikofski'], 1) +# ] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +# texinfo_documents = [ + # ('index', 'MATLABSphinxDocumentationTest', 'MATLAB Sphinx Documentation Test Documentation', + # 'Mark Mikofski', 'MATLABSphinxDocumentationTest', 'One line description of project.', + # 'Miscellaneous'), +# ] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False +# napoleon_google_docstring = True +add_module_names = False +# autodoc_preserve_defaults = True + +# from sphinxcontrib.mat_documenters import MatAttributeDocumenter, MatClassLevelDocumenter +# import sphinx + +# def _add_directive_header(self, sig): +# MatClassLevelDocumenter.add_directive_header(self, sig) +# if not self.options.annotation: +# if not self._datadescriptor: +# try: +# objrepr = sphinx.util.inspect.object_description(self.object.default) # display default +# except ValueError: +# pass +# else: +# self.add_line(' :annotation:', '') +# elif self.options.annotation is SUPPRESS: +# pass +# else: +# self.add_line(' :annotation: %s' % self.options.annotation, +# '') + +# MatAttributeDocumenter.add_directive_header = _add_directive_header + + diff --git a/sphinx/contribute.rst b/sphinx/contribute.rst new file mode 100644 index 0000000..bce0327 --- /dev/null +++ b/sphinx/contribute.rst @@ -0,0 +1,53 @@ +Contributing to the ontology +============================ + +There are two ways you can contribute to the ontology. + +Suggest minor changes on existing elements +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`Create a feature +request `__ in a +`Github +Issue `__ +to suggest edits to names, defintions, references on existing classes +and properties. + +Propose additions/deletion of elements +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + **NOTE:** We recommend contacting some of the + `BattINFO `__ contributors in + advance to discuss which additions and/or deletions you wish to make. + +We recommend using the `forking +workflow `__ +to contribute additions/deletions. Fork this repository, clone the fork +on your local PC, create your branch based on the existing ``dev`` +branch (e.g. ``dev_john_doe``) and work on the editions in you local +copy. + +You can edit ontologes in two main ways. One is programmatically, using +for instance `EMMOntoPy `__. The +second and more common is using the interface provided by the Protégé +software. In case of the latter, `install +Protégé `__ and use it to open the +ontology file you wish to edit. Before adding elements, ensure Prot´égé +is configured to create IRIs in the right format: + +- Open Protégé +- Go to File/Open and load the ontology file you wish to modify +- Go to File/Preferences and there go to the New Entities Tab +- Ensure you have configured the preferences as shown below: + + | |Protege config.| + | Here is the “Specified IRI” for you to copy: + ``https://emmo.info/battery#`` + +- Once you have made your changes, commit them to your fork and `create + a pull + request `__. +- We will merge the request after assessing it. + +.. |Protege config.| image:: doc/img/protege_config_contribute.png + diff --git a/sphinx/img/battinfologo.ico b/sphinx/img/battinfologo.ico new file mode 100644 index 0000000000000000000000000000000000000000..5c6375dcf234c1f7ce4e83035d048f4162d2905d GIT binary patch literal 16853 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�zYrM}S4+gMIcabe>uyS@keBA9{a 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 RV5G-uersS|AU*;y`9J3GwBY~% literal 0 HcmV?d00001 diff --git a/sphinx/index.rst b/sphinx/index.rst new file mode 100644 index 0000000..58b62de --- /dev/null +++ b/sphinx/index.rst @@ -0,0 +1,32 @@ +.. toctree:: + :includehidden: + :hidden: + + About + self + Contribute + + +======== +BattINFO +======== + +The **Batt**\ ery **IN**\ ter\ **F**\ ace **O**\ ntology is a digital resource to support interoperability of battery +data. + +BattINFO consists of a list of entities representing concepts used in batteries and electrochemistry. Each entity has a +unique identifier (IRI) and is annotated with additional information, such as its preferred name ("prefLabel"), +alternative names, definition, references, etc. As users link their research resources to BattINFO entities, they are +effectively describing their resource using a common vocabulary. Resources can be datasets, documents, persons, +organizations, equipment, samples... anything linked to the common vocabulary described in BattINFO, becomes part of an +ecosystem of Findable resources. + +Here you can find the entities described in BattINFO, classified in two domains. + +* `Battery Domain `__ +* `Electrochemistry Domain `__ + + + + + diff --git a/sphinx/requirements.txt b/sphinx/requirements.txt new file mode 100644 index 0000000..aacdcbe --- /dev/null +++ b/sphinx/requirements.txt @@ -0,0 +1,30 @@ +accessible-pygments==0.0.4 +alabaster==0.7.13 +Babel==2.14.0 +beautifulsoup4==4.12.2 +certifi==2023.11.17 +charset-normalizer==3.3.2 +docutils==0.19 +idna==3.6 +imagesize==1.4.1 +importlib-metadata==6.7.0 +Jinja2==3.1.3 +MarkupSafe==2.1.3 +packaging==23.2 +pydata-sphinx-theme==0.13.3 +Pygments==2.17.2 +pytz==2023.3.post1 +requests==2.31.0 +snowballstemmer==2.2.0 +soupsieve==2.4.1 +Sphinx==5.3.0 +sphinxcontrib-applehelp==1.0.2 +sphinxcontrib-devhelp==1.0.2 +sphinxcontrib-globalsubs==0.1.1 +sphinxcontrib-htmlhelp==2.0.0 +sphinxcontrib-jsmath==1.0.1 +sphinxcontrib-qthelp==1.0.3 +sphinxcontrib-serializinghtml==1.1.5 +typing_extensions==4.7.1 +urllib3==2.0.7 +zipp==3.15.0 diff --git a/sphinx/ttl_to_rst.py b/sphinx/ttl_to_rst.py new file mode 100644 index 0000000..f158f98 --- /dev/null +++ b/sphinx/ttl_to_rst.py @@ -0,0 +1,168 @@ +from rdflib import Graph + + + +########## LOAD TTL ################ + + +def load_ttl_from_url(url:str)->Graph: + g = Graph() + g.parse(url, format="turtle") + return g + + + + +########## QUERY TLL ################ + +def extract_terms_info_sparql(g: Graph)-> list: + + text_entities = [] + + # SPARQL QUERY # + PREFIXES = """ + PREFIX emmo: + PREFIX skos: + """ + + list_entity_types = ["IRI", "prefLabel", "Elucidation", "Alternative Label(s)", "IEC Reference", "IUPAC Reference", "Wikipedia Reference"] + + query = PREFIXES + """ + SELECT ?iri ?prefLabel ?elucidation (GROUP_CONCAT(?altLabel; SEPARATOR=", ") AS ?altLabels) ?iecref ?iupacref ?wikipediaref + WHERE { + ?iri skos:prefLabel ?prefLabel. + + OPTIONAL { ?iri emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 ?elucidation . } + OPTIONAL { ?iri skos:altLabel ?altLabel . } + OPTIONAL { ?iri emmo:EMMO_50c298c2_55a2_4068_b3ac_4e948c33181f ?iecref . } + OPTIONAL { ?iri emmo:EMMO_fe015383_afb3_44a6_ae86_043628697aa2 ?iupacref . } + OPTIONAL { ?iri emmo:EMMO_c84c6752_6d64_48cc_9500_e54a3c34898d ?wikipediaref . } + } + + GROUP BY ?iri ?prefLabel ?elucidation + + """ + + qres = g.query(query) + + for hit in qres: + hit_dict = {entity_type:str(entity) for entity_type, entity in zip(list_entity_types, hit)} + text_entities.append(hit_dict) + + text_entities.sort(key=lambda e: e["prefLabel"]) + + return text_entities + + + + + +########## RENDER HTML TOP ################ + +def render_rst_top() -> str: + + top_rst = """ +========== +References +========== + +""" + + return top_rst + + + + +########## RENDER ENTITIES ################ + +def entities_to_rst(entities: list[dict]) -> str: + + rst = "" + + for item in entities: + + iri_prefix, iri_suffix = item['IRI'].split("#") + + rst += ".. raw:: html\n\n" + rst += "
\n\n" + + rst += item['prefLabel'] + "\n" + for ind in range(len(item['prefLabel'])): + rst += "-" + rst += "\n\n" + + rst += "* " + item['IRI'] + "\n\n" + + rst += ".. raw:: html\n\n" + indent = " " + rst += indent + "\n" + for key, value in item.items(): + + if (key not in ['IRI', 'prefLabel']) & (value != "None") & (value != ""): + + rst += indent + "\n" + rst += indent + "\n" + if value.startswith("http"): + value = f"""{value}""" + value = value.encode('ascii', 'xmlcharrefreplace') + value = value.decode('utf-8') + value = value.replace('\n', '\n' + indent) + rst += indent + "\n" + rst += indent + "\n" + + rst += indent + "
" + key + "" + value + "
\n" + rst += "\n\n" + + return rst + + +########## RENDER RST BOTTOM ################ + + +def render_rst_bottom() -> str: + return """ + + """ + + +########### RUN THE RENDERING WORKFLOW ############## + + +def rendering_workflow(): + + # PAGES + ttl_modules = [ + {"section title": "Battery Concepts", + "path": "./battery.ttl"}, + {"section title": "Battery Quantities", + "path": "./batteryquantities.ttl"} + ] + + # GENERATE PAGES + rst_filename = "battery.rst" + + rst = render_rst_top() + + for module in ttl_modules: + + g = load_ttl_from_url(module["path"]) + + entities_list = extract_terms_info_sparql(g) + + page_title = module["section title"] + rst += page_title + "\n" + for ind in range(len(page_title)): + rst += "=" + rst += "\n\n" + rst += entities_to_rst(entities_list) + + rst += render_rst_bottom() + + with open("./sphinx/"+ rst_filename, "w+", encoding="utf-8") as f: + f.write(rst) + + + +if __name__ == "__main__": + + rendering_workflow() diff --git a/sphinx/util.el b/sphinx/util.el new file mode 100644 index 0000000..89c36c8 --- /dev/null +++ b/sphinx/util.el @@ -0,0 +1,30 @@ +;;; Utils for building BattMo doc + +;; Use user-login-name to get user name +;; (cond '(compare-strings user-login-name "xavier") + +(pcase (user-login-name) + ("xavier" (setq docdir "/home/xavier/Python/BattINFO/sphinx/") + ) + ) + +(defun battinfodoc-local-open () + "Open locally built documentation in browser" + (interactive) + (browse-url (concat docdir "_build/html/index.html")) + + ) + +(defun battinfodoc-build () + "Build BattMo documentation" + (interactive) + (let ((outputbuffer (get-buffer-create "*buildoutput*"))) + (pop-to-buffer outputbuffer) + (cd docdir) + (pyvenv-activate "~/Python/battinfo-doc-3.7-env/") + (erase-buffer) + (start-process "battinfo-build" outputbuffer "make" "html") + ) + ) + +