feat: add gitlab ci, makefile, sphinx docs

This commit is contained in:
iriley 2024-04-29 14:57:36 +02:00
parent 2c6232a1bf
commit 3a5fc32ec8
215 changed files with 23686 additions and 52 deletions

View File

@ -1,30 +1,80 @@
include:
- project: "Gitlab/gitlab"
ref: 0.3.0
file: "/ci-templates/research/dvc-versioning-build-release.gitlab-ci.yml"
ref: main
file: "/ci-templates/research/versioning-build+azure_model-test-release.gitlab-ci.yml"
variables:
NEXUS_PROJECT_DIR: red
NEXUS_PROJECT_DIR: ff
IMAGENAME: "${CI_PROJECT_NAME}"
#################################
# temp. disable integration tests, b/c they don't cover the CV analysis case yet
trigger integration tests:
rules:
- when: never
release build:
stage: release
######
# DOCS
pages:
stage: unit-tests
before_script:
- !reference [.activate-venv, script]
script:
- sphinx-apidoc ./src -o ./docs/source/modules --no-toc --module-first --follow-links --separate --force
- sphinx-build -b html docs/source/ public/ -E -a
artifacts:
paths:
- public
# only:
# - main
########
# BUILDS
###### UPDATE/EDIT ######
# MAKE SURE TO SWITCH OUT ALL YOUR MODEL NAMES + VERSIONS
# name the job after the model it's using in the build, keep the prefix referencing `docker-build::`
docker-build::keyword-extraction-multi:
extends: .docker-build
variables:
MODEL_NAME: ${MODEL_MULTI_NAME}
MODEL_VERSION: ${MODEL_MULTI_VERSION}
docker-build::cv-analysis-service:
extends: .docker-build
needs:
- job: set custom version
artifacts: true
optional: true
- job: calculate patch version
artifacts: true
optional: true
- job: calculate minor version
artifacts: true
optional: true
- job: build docker nexus
artifacts: true
#################################
- !reference [.needs-versioning, needs]
- job: docker-build::cv-analysis-service
variables:
MODEL_NAME: ${MODEL_EN_NAME}
MODEL_VERSION: ${MODEL_EN_VERSION}
docker-build::keyword-extraction-de:
extends: .docker-build
needs:
- !reference [.needs-versioning, needs]
- job: docker-build::cv-analysis-service
variables:
MODEL_NAME: ${MODEL_DE_NAME}
MODEL_VERSION: ${MODEL_DE_VERSION}
###################
# INTEGRATION TESTS
trigger-integration-tests:
extends: .integration-tests
needs:
###### UPDATE/EDIT ######
# YOU NEED ONLY TO DEFINE ONE
# reason is that we want to have one built image to use with the integration tests
# this should be the same image you uploaded test data for
- job: docker-build::cv-analysis-service
artifacts: true
rules:
- when: never # temp. disable integration tests
#########
# RELEASE
release:
extends: .release
needs:
- !reference [.release, needs] # LEAVE THIS LINE AS IS
###### UPDATE/EDIT ######
# DEFINE ONE BUILD JOB THAT NEEDS TO EXIST BEFORE RELEASE
- job: docker-build::cv-analysis-service

30
.gitlab-ci.yml.bak Normal file
View File

@ -0,0 +1,30 @@
include:
- project: "Gitlab/gitlab"
ref: 0.3.0
file: "/ci-templates/research/dvc-versioning-build-release.gitlab-ci.yml"
variables:
NEXUS_PROJECT_DIR: red
IMAGENAME: "${CI_PROJECT_NAME}"
#################################
# temp. disable integration tests, b/c they don't cover the CV analysis case yet
trigger integration tests:
rules:
- when: never
release build:
stage: release
needs:
- job: set custom version
artifacts: true
optional: true
- job: calculate patch version
artifacts: true
optional: true
- job: calculate minor version
artifacts: true
optional: true
- job: build docker nexus
artifacts: true
#################################

View File

@ -10,6 +10,7 @@ repos:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
args: [--unsafe]
- id: check-toml
- id: detect-private-key
- id: check-added-large-files

91
Makefile Normal file
View File

@ -0,0 +1,91 @@
.PHONY: \
poetry in-project-venv dev-env use-env install install-dev tests \
update-version sync-version-with-git \
docker docker-build-run docker-build docker-run \
docker-rm docker-rm-container docker-rm-image \
pre-commit get-licenses prep-commit \
docs sphinx_html sphinx_apidoc
.DEFAULT_GOAL := run
export DOCKER=docker
export DOCKERFILE=Dockerfile
export IMAGE_NAME=keyword_extraction_service-image
export CONTAINER_NAME=keyword_extraction_service-container
export HOST_PORT=9999
export CONTAINER_PORT=9999
export PYTHON_VERSION=python3.10
# all commands should be executed in the root dir or the project,
# specific environments should be deactivated
poetry: in-project-venv use-env dev-env
in-project-venv:
poetry config virtualenvs.in-project true
use-env:
poetry env use ${PYTHON_VERSION}
dev-env:
poetry install --with dev && poetry update
install:
poetry add $(pkg)
install-dev:
poetry add --dev $(pkg)
requirements:
poetry export --without-hashes --output requirements.txt
update-version:
poetry version prerelease
sync-version-with-git:
git pull -p && poetry version $(git rev-list --tags --max-count=1 | git describe --tags --abbrev=0)
bom:
cyclonedx-py poetry -o bom.json
docker: docker-rm docker-build-run
docker-build-run: docker-build docker-run
docker-build:
$(DOCKER) build \
--no-cache --progress=plain \
-t $(IMAGE_NAME) -f $(DOCKERFILE) \
--build-arg USERNAME=${USERNAME} \
--build-arg TOKEN=${GITLAB_TOKEN} \
.
docker-run:
$(DOCKER) run -it --rm -p $(HOST_PORT):$(CONTAINER_PORT)/tcp --name $(CONTAINER_NAME) $(IMAGE_NAME)
docker-rm: docker-rm-container docker-rm-image
docker-rm-container:
-$(DOCKER) rm $(CONTAINER_NAME)
docker-rm-image:
-$(DOCKER) image rm $(IMAGE_NAME)
tests:
poetry run pytest ./tests
prep-commit:
docs get-license sync-version-with-git update-version pre-commit
pre-commit:
pre-commit run --all-files
get-licenses:
pip-licenses --format=json --order=license --with-urls > pkg-licenses.json
docs: sphinx_apidoc sphinx_html
sphinx_html:
poetry run sphinx-build -b html docs/source/ docs/build/html -E -a
sphinx_apidoc:
poetry run sphinx-apidoc ./src -o ./docs/source/modules --no-toc --module-first --follow-links --separate --force

4
docs/build/html/.buildinfo vendored Normal file
View File

@ -0,0 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 9fe322b0a30d48be95bbacb490584b25
tags: 645f666f9bcd5a90fca523b33c5a78b7

BIN
docs/build/html/.doctrees/README.doctree vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
docs/build/html/.doctrees/index.doctree vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

683
docs/build/html/README.html vendored Normal file
View File

@ -0,0 +1,683 @@
<!DOCTYPE html>
<html lang="en" data-content_root="./" >
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Keyword-Service &#8212; CV Analysis Service 2.5.1 documentation</title>
<script data-cfasync="false">
document.documentElement.dataset.mode = localStorage.getItem("mode") || "";
document.documentElement.dataset.theme = localStorage.getItem("theme") || "light";
</script>
<!-- Loaded before other Sphinx assets -->
<link href="_static/styles/theme.css?digest=8d27b9dea8ad943066ae" rel="stylesheet" />
<link href="_static/styles/bootstrap.css?digest=8d27b9dea8ad943066ae" rel="stylesheet" />
<link href="_static/styles/pydata-sphinx-theme.css?digest=8d27b9dea8ad943066ae" rel="stylesheet" />
<link href="_static/vendor/fontawesome/6.5.1/css/all.min.css?digest=8d27b9dea8ad943066ae" rel="stylesheet" />
<link rel="preload" as="font" type="font/woff2" crossorigin href="_static/vendor/fontawesome/6.5.1/webfonts/fa-solid-900.woff2" />
<link rel="preload" as="font" type="font/woff2" crossorigin href="_static/vendor/fontawesome/6.5.1/webfonts/fa-brands-400.woff2" />
<link rel="preload" as="font" type="font/woff2" crossorigin href="_static/vendor/fontawesome/6.5.1/webfonts/fa-regular-400.woff2" />
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=a746c00c" />
<link rel="stylesheet" type="text/css" href="https://assets.readthedocs.org/static/css/badge_only.css" />
<!-- Pre-loaded scripts that we'll load fully later -->
<link rel="preload" as="script" href="_static/scripts/bootstrap.js?digest=8d27b9dea8ad943066ae" />
<link rel="preload" as="script" href="_static/scripts/pydata-sphinx-theme.js?digest=8d27b9dea8ad943066ae" />
<script src="_static/vendor/fontawesome/6.5.1/js/all.min.js?digest=8d27b9dea8ad943066ae"></script>
<script src="_static/documentation_options.js?v=983e91d6"></script>
<script src="_static/doctools.js?v=9a2dae69"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script>DOCUMENTATION_OPTIONS.pagename = 'README';</script>
<script async="async" src="https://assets.readthedocs.org/static/javascript/readthedocs-doc-embed.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="cv_analysis package" href="modules/cv_analysis.html" />
<link rel="prev" title="Welcome to Keyword Extraction Service documentation!" href="index.html" />
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="docsearch:language" content="en"/>
<!-- RTD Extra Head -->
<link rel="stylesheet" href="https://assets.readthedocs.org/static/css/readthedocs-doc-embed.css" type="text/css" />
<script type="application/json" id="READTHEDOCS_DATA">{"ad_free": "", "api_host": "", "builder": "sphinx", "canonical_url": "", "docroot": "", "features": {"docsearch_disabled": false}, "global_analytics_code": null, "language": "", "page": "README", "programming_language": "", "project": "", "source_suffix": ".md", "subprojects": {}, "theme": "", "user_analytics_code": null, "version": ""}</script>
<!--
Using this variable directly instead of using `JSON.parse` is deprecated.
The READTHEDOCS_DATA global variable will be removed in the future.
-->
<script type="text/javascript">
READTHEDOCS_DATA = JSON.parse(document.getElementById('READTHEDOCS_DATA').innerHTML);
</script>
<script type="text/javascript" src="https://assets.readthedocs.org/static/javascript/readthedocs-analytics.js" async="async"></script>
<!-- end RTD <extrahead> -->
</head>
<body data-bs-spy="scroll" data-bs-target=".bd-toc-nav" data-offset="180" data-bs-root-margin="0px 0px -60%" data-default-mode="">
<a id="pst-skip-link" class="skip-link" href="#main-content">Skip to main content</a>
<div id="pst-scroll-pixel-helper"></div>
<button type="button" class="btn rounded-pill" id="pst-back-to-top">
<i class="fa-solid fa-arrow-up"></i>
Back to top
</button>
<input type="checkbox"
class="sidebar-toggle"
name="__primary"
id="__primary"/>
<label class="overlay overlay-primary" for="__primary"></label>
<input type="checkbox"
class="sidebar-toggle"
name="__secondary"
id="__secondary"/>
<label class="overlay overlay-secondary" for="__secondary"></label>
<div class="search-button__wrapper">
<div class="search-button__overlay"></div>
<div class="search-button__search-container">
<form class="bd-search d-flex align-items-center"
action="search.html"
method="get">
<i class="fa-solid fa-magnifying-glass"></i>
<input type="search"
class="form-control"
name="q"
id="search-input"
placeholder="Search the docs ..."
aria-label="Search the docs ..."
autocomplete="off"
autocorrect="off"
autocapitalize="off"
spellcheck="false"/>
<span class="search-button__kbd-shortcut"><kbd class="kbd-shortcut__modifier">Ctrl</kbd>+<kbd>K</kbd></span>
</form></div>
</div>
<header class="bd-header navbar navbar-expand-lg bd-navbar">
<div class="bd-header__inner bd-page-width">
<label class="sidebar-toggle primary-toggle" for="__primary">
<span class="fa-solid fa-bars"></span>
</label>
<div class="col-lg-3 navbar-header-items__start">
<div class="navbar-item">
<a class="navbar-brand logo" href="index.html">
<img src="_static/logo.png" class="logo__image only-light" alt="CV Analysis Service 2.5.1 documentation - Home"/>
<script>document.write(`<img src="_static/logo.png" class="logo__image only-dark" alt="CV Analysis Service 2.5.1 documentation - Home"/>`);</script>
</a></div>
</div>
<div class="col-lg-9 navbar-header-items">
<div class="me-auto navbar-header-items__center">
<div class="navbar-item">
<nav class="navbar-nav">
<ul class="bd-navbar-elements navbar-nav">
<li class="nav-item current active">
<a class="nav-link nav-internal" href="#">
Keyword-Service
</a>
</li>
<li class="nav-item">
<a class="nav-link nav-internal" href="modules/cv_analysis.html">
cv_analysis package
</a>
</li>
<li class="nav-item">
<a class="nav-link nav-internal" href="modules/serve.html">
serve module
</a>
</li>
</ul>
</nav></div>
</div>
<div class="navbar-header-items__end">
<div class="navbar-item navbar-persistent--container">
<script>
document.write(`
<button class="btn navbar-btn search-button-field search-button__button" title="Search" aria-label="Search" data-bs-placement="bottom" data-bs-toggle="tooltip">
<i class="fa-solid fa-magnifying-glass"></i>
<span class="search-button__default-text">Search</span>
<span class="search-button__kbd-shortcut"><kbd class="kbd-shortcut__modifier">Ctrl</kbd>+<kbd class="kbd-shortcut__modifier">K</kbd></span>
</button>
`);
</script>
</div>
<div class="navbar-item">
<script>
document.write(`
<button class="btn btn-sm navbar-btn theme-switch-button" title="light/dark" aria-label="light/dark" data-bs-placement="bottom" data-bs-toggle="tooltip">
<span class="theme-switch nav-link" data-mode="light"><i class="fa-solid fa-sun fa-lg"></i></span>
<span class="theme-switch nav-link" data-mode="dark"><i class="fa-solid fa-moon fa-lg"></i></span>
<span class="theme-switch nav-link" data-mode="auto"><i class="fa-solid fa-circle-half-stroke fa-lg"></i></span>
</button>
`);
</script></div>
</div>
</div>
<div class="navbar-persistent--mobile">
<script>
document.write(`
<button class="btn navbar-btn search-button-field search-button__button" title="Search" aria-label="Search" data-bs-placement="bottom" data-bs-toggle="tooltip">
<i class="fa-solid fa-magnifying-glass"></i>
<span class="search-button__default-text">Search</span>
<span class="search-button__kbd-shortcut"><kbd class="kbd-shortcut__modifier">Ctrl</kbd>+<kbd class="kbd-shortcut__modifier">K</kbd></span>
</button>
`);
</script>
</div>
<label class="sidebar-toggle secondary-toggle" for="__secondary" tabindex="0">
<span class="fa-solid fa-outdent"></span>
</label>
</div>
</header>
<div class="bd-container">
<div class="bd-container__inner bd-page-width">
<div class="bd-sidebar-primary bd-sidebar">
<div class="sidebar-header-items sidebar-primary__section">
<div class="sidebar-header-items__center">
<div class="navbar-item">
<nav class="navbar-nav">
<ul class="bd-navbar-elements navbar-nav">
<li class="nav-item current active">
<a class="nav-link nav-internal" href="#">
Keyword-Service
</a>
</li>
<li class="nav-item">
<a class="nav-link nav-internal" href="modules/cv_analysis.html">
cv_analysis package
</a>
</li>
<li class="nav-item">
<a class="nav-link nav-internal" href="modules/serve.html">
serve module
</a>
</li>
</ul>
</nav></div>
</div>
<div class="sidebar-header-items__end">
<div class="navbar-item">
<script>
document.write(`
<button class="btn btn-sm navbar-btn theme-switch-button" title="light/dark" aria-label="light/dark" data-bs-placement="bottom" data-bs-toggle="tooltip">
<span class="theme-switch nav-link" data-mode="light"><i class="fa-solid fa-sun fa-lg"></i></span>
<span class="theme-switch nav-link" data-mode="dark"><i class="fa-solid fa-moon fa-lg"></i></span>
<span class="theme-switch nav-link" data-mode="auto"><i class="fa-solid fa-circle-half-stroke fa-lg"></i></span>
</button>
`);
</script></div>
</div>
</div>
<div class="sidebar-primary-items__start sidebar-primary__section">
<div class="sidebar-primary-item">
<nav class="bd-docs-nav bd-links"
aria-label="Section Navigation">
<p class="bd-links__title" role="heading" aria-level="1">Section Navigation</p>
<div class="bd-toc-item navbar-nav"></div>
</nav></div>
</div>
<div class="sidebar-primary-items__end sidebar-primary__section">
</div>
<div id="rtd-footer-container"></div>
</div>
<main id="main-content" class="bd-main">
<div class="bd-content">
<div class="bd-article-container">
<div class="bd-header-article">
<div class="header-article-items header-article__inner">
<div class="header-article-items__start">
<div class="header-article-item">
<nav aria-label="Breadcrumb">
<ul class="bd-breadcrumbs">
<li class="breadcrumb-item breadcrumb-home">
<a href="index.html" class="nav-link" aria-label="Home">
<i class="fa-solid fa-home"></i>
</a>
</li>
<li class="breadcrumb-item active" aria-current="page">Keyword-Service</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
<div id="searchbox"></div>
<article class="bd-article">
<section id="keyword-service">
<h1>Keyword-Service<a class="headerlink" href="#keyword-service" title="Link to this heading">#</a></h1>
<p>Service to get keywords of a paragraph or whole document.</p>
<!-- TOC --><ul class="simple">
<li><p><a class="reference external" href="#keyword-service">Keyword-Service</a></p>
<ul>
<li><p><a class="reference external" href="#api">API</a></p>
<ul>
<li><p><a class="reference external" href="#rest">REST</a></p></li>
<li><p><a class="reference external" href="#rabbitmq">RabbitMQ</a></p></li>
</ul>
</li>
<li><p><a class="reference external" href="#service-configuration">Service Configuration</a></p></li>
<li><p><a class="reference external" href="#language">Language</a></p></li>
<li><p><a class="reference external" href="#usage">Usage</a></p>
<ul>
<li><p><a class="reference external" href="#run-docker-commands">Run Docker Commands</a></p></li>
<li><p><a class="reference external" href="#run-locally">Run locally</a></p></li>
</ul>
</li>
</ul>
</li>
<li><p><a class="reference external" href="#upload-models-to-ml-flow">Upload models to ML Flow</a></p></li>
</ul>
<!-- TOC --><section id="api">
<h2>API<a class="headerlink" href="#api" title="Link to this heading">#</a></h2>
<section id="rest">
<h3>REST<a class="headerlink" href="#rest" title="Link to this heading">#</a></h3>
<p>The service provides endpoints to extract keywords from a text and to embed a text. For details, download
<a class="reference external" href="docs/openapi_redoc.html">OpenAPI schema</a> and view it in a browser.</p>
</section>
<section id="rabbitmq">
<h3>RabbitMQ<a class="headerlink" href="#rabbitmq" title="Link to this heading">#</a></h3>
<p>The service listens to a queue and processes the messages. This method is ment to be used for extracting keywords from
whole documents. All RabbitMQ parameters including the queue names are set in environment variables, refer to the
service respective HELM chart for more information.</p>
<p>The input message should be a JSON object with the following structure:</p>
<div class="highlight-json notranslate"><div class="highlight"><pre><span></span><span class="p">{</span>
<span class="w"> </span><span class="nt">&quot;targetFilePath&quot;</span><span class="p">:</span><span class="w"> </span><span class="err">s</span><span class="kc">tr</span><span class="err">i</span><span class="kc">n</span><span class="err">g</span><span class="p">,</span>
<span class="w"> </span><span class="nt">&quot;responseFilePath&quot;</span><span class="p">:</span><span class="w"> </span><span class="err">s</span><span class="kc">tr</span><span class="err">i</span><span class="kc">n</span><span class="err">g</span>
<span class="p">}</span>
</pre></div>
</div>
<p>The service downloads the file specified in <code class="docutils literal notranslate"><span class="pre">targetFilePath</span></code>. Supported data structures for the target file are:</p>
<ul class="simple">
<li><p>simplified text data (signifier key: <code class="docutils literal notranslate"><span class="pre">paragraphs</span></code>)</p></li>
<li><p>structure object data (signifier key: <code class="docutils literal notranslate"><span class="pre">structureObjects</span></code>)</p></li>
</ul>
<p>As a response, the service uploads a JSON-structured file (as defined in <code class="docutils literal notranslate"><span class="pre">responseFilePath</span></code>) with the result under the
<code class="docutils literal notranslate"><span class="pre">data</span></code> key. The structure of the response file is as follows:</p>
<div class="highlight-javascript notranslate"><div class="highlight"><pre><span></span><span class="p">{</span>
<span class="w"> </span><span class="s2">&quot;targetFilePath&quot;</span>
<span class="o">:</span>
<span class="w"> </span><span class="nx">string</span><span class="p">,</span>
<span class="w"> </span><span class="s2">&quot;responseFilePath&quot;</span>
<span class="o">:</span>
<span class="w"> </span><span class="nx">string</span><span class="p">,</span>
<span class="w"> </span><span class="c1">// and eventually further fields if present in the input message </span>
<span class="w"> </span><span class="s2">&quot;data&quot;</span>
<span class="o">:</span>
<span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="s2">&quot;keywords&quot;</span><span class="o">:</span><span class="w"> </span><span class="nb">Array</span><span class="p">[</span><span class="nx">string</span><span class="p">],</span>
<span class="w"> </span><span class="s2">&quot;paragraphId&quot;</span><span class="o">:</span><span class="w"> </span><span class="kr">int</span><span class="p">,</span>
<span class="w"> </span><span class="s2">&quot;embedding&quot;</span><span class="o">:</span><span class="w"> </span><span class="nb">Array</span><span class="p">[</span><span class="kr">float</span><span class="p">]</span><span class="w"> </span><span class="c1">// 384 dimensions</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">]</span>
<span class="p">}</span>
</pre></div>
</div>
<p><strong>Note</strong> that</p>
<ul class="simple">
<li><p>the <code class="docutils literal notranslate"><span class="pre">embedding</span></code> key is optional and can be omitted. The service will not calculate the embedding if the environment
variable <code class="docutils literal notranslate"><span class="pre">MODEL__COMPUTE_EMBEDDINGS</span></code> is set to <code class="docutils literal notranslate"><span class="pre">false</span></code>.</p></li>
<li><p>the service also computes the keywords for the whole document. In this case, the <code class="docutils literal notranslate"><span class="pre">paragraphId</span></code> is set to <code class="docutils literal notranslate"><span class="pre">-1</span></code>.</p></li>
</ul>
</section>
</section>
<section id="service-configuration">
<h2>Service Configuration<a class="headerlink" href="#service-configuration" title="Link to this heading">#</a></h2>
<p>The service is configured via environment variables. The following variables are available:</p>
<p>| Variable | Description | Default |
| —————————————— | ———————————————————————————– | ——- |
| LOGGING__LEVEL | Logging level | INFO |
| MODEL__MAX_KEYWORDS_PER_PARAGRAPH | Maximum number of keywords per paragraph | 5 |
| MODLE__MAX_KEYWORDS_PER_DOCUMENT | Maximum number of keywords per document, when set to 0, no keywords are extracted | 0 |
| MODEL__COMPUTE_EMBEDDINGS | Whether to compute keyword embeddings or not | true |
| MODEL__PREPROCESSING__MIN_PARAGRAPH_LENGTH | Minimum number of characters in a paragraph to be considered for keyword extraction | 1 |
| MODEL__POSTPROCESSING__FILTER_SUBWORDS | Whether to filter out subwords from the keywords or not | true |</p>
<p><strong>NOTE</strong> that these variables are subject to change. For the most recent configuration, refer to the service respective
HELM chart.</p>
</section>
<section id="language">
<h2>Language<a class="headerlink" href="#language" title="Link to this heading">#</a></h2>
<p>Currently, there is an english, a german and a multi-language model for keyword extraction. The models are uploaded to
mlflow and can
be set in the Dockerfile when building the container:</p>
<p>example for german model:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">ENV</span> <span class="n">AZURE_RESOURCE_GROUP</span><span class="o">=</span><span class="s2">&quot;mriedl&quot;</span>
<span class="n">ENV</span> <span class="n">AZURE_AML_WORKSPACE</span><span class="o">=</span><span class="s2">&quot;azureml-ws&quot;</span>
<span class="n">ENV</span> <span class="n">AZURE_AML_MODEL_NAME</span><span class="o">=</span><span class="s2">&quot;keyword-extraction-de&quot;</span>
<span class="n">ENV</span> <span class="n">AZURE_AML_MODEL_VERSION</span><span class="o">=</span><span class="s2">&quot;1&quot;</span>
</pre></div>
</div>
<p>and example for english model:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">ENV</span> <span class="n">AZURE_RESOURCE_GROUP</span><span class="o">=</span><span class="s2">&quot;mriedl&quot;</span>
<span class="n">ENV</span> <span class="n">AZURE_AML_WORKSPACE</span><span class="o">=</span><span class="s2">&quot;azureml-ws&quot;</span>
<span class="n">ENV</span> <span class="n">AZURE_AML_MODEL_NAME</span><span class="o">=</span><span class="s2">&quot;keyword-extraction-de&quot;</span>
<span class="n">ENV</span> <span class="n">AZURE_AML_MODEL_VERSION</span><span class="o">=</span><span class="s2">&quot;1&quot;</span>
</pre></div>
</div>
</section>
<section id="usage">
<h2>Usage<a class="headerlink" href="#usage" title="Link to this heading">#</a></h2>
<p><strong>Two Options:</strong></p>
<ol class="simple">
<li><p>REST: Send text per request to endpoint, endpoint returns keywords</p></li>
<li><p>Queue: Service gets text from queue, model calculates keywords, save keywords in queue</p></li>
</ol>
<p>To test the REST endpoint you have to set up an environment and do poetry install (
see https://gitlab.knecon.com/knecon/research/template-python-project for details for setting up poetry)
Then run</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">python</span> <span class="o">./</span><span class="n">src</span><span class="o">/</span><span class="n">serve</span><span class="o">.</span><span class="n">py</span>
</pre></div>
</div>
<p>You dont need to start a queue for that, just ignore the AMQP Error.
Port and host are set in settings.toml .
You can use the FastAPI under 127.0.0.1:8001/docs to send request to endpoint.</p>
<p>You can also test the service with docker:</p>
<section id="run-docker-commands">
<h3>Run Docker Commands<a class="headerlink" href="#run-docker-commands" title="Link to this heading">#</a></h3>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>docker<span class="w"> </span>build<span class="w"> </span>-t<span class="w"> </span><span class="si">${</span><span class="nv">IMAGE_NAME</span><span class="si">}</span><span class="w"> </span>-f<span class="w"> </span>Dockerfile<span class="w"> </span>--build-arg<span class="w"> </span><span class="nv">GITLAB_USER</span><span class="o">=</span><span class="si">${</span><span class="nv">GITLAB_USER</span><span class="si">}</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--build-arg<span class="w"> </span><span class="nv">GITLAB_ACCESS_TOKEN</span><span class="o">=</span><span class="si">${</span><span class="nv">GITLAB_ACCESS_TOKEN</span><span class="si">}</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--build-arg<span class="w"> </span><span class="nv">AZURE_TENANT_ID</span><span class="o">=</span><span class="si">${</span><span class="nv">AZURE_TENANT_ID</span><span class="si">}</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--build-arg<span class="w"> </span><span class="nv">AZURE_SUBSCRIPTION_ID</span><span class="o">=</span><span class="si">${</span><span class="nv">AZURE_SUBSCRIPTION_ID</span><span class="si">}</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--build-arg<span class="w"> </span><span class="nv">AZURE_CLIENT_ID</span><span class="o">=</span><span class="si">${</span><span class="nv">AZURE_CLIENT_ID</span><span class="si">}</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--build-arg<span class="w"> </span><span class="nv">AZURE_CLIENT_SECRET</span><span class="o">=</span><span class="si">${</span><span class="nv">AZURE_CLIENT_SECRET</span><span class="si">}</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--build-arg<span class="w"> </span><span class="nv">AZURE_AML_MODEL_VERSION</span><span class="o">=</span><span class="si">${</span><span class="nv">AZURE_AML_MODEL_VERSION</span><span class="si">}</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--build-arg<span class="w"> </span><span class="nv">AZURE_AML_MODEL_NAME</span><span class="o">=</span><span class="si">${</span><span class="nv">AZURE_AML_MODEL_NAME</span><span class="si">}</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--build-arg<span class="w"> </span><span class="nv">AZURE_RESOURCE_GROUP</span><span class="o">=</span><span class="si">${</span><span class="nv">AZURE_RESOURCE_GROUP</span><span class="si">}</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--build-arg<span class="w"> </span><span class="nv">AZURE_AML_WORKSPACE</span><span class="o">=</span><span class="si">${</span><span class="nv">AZURE_AML_WORKSPACE</span><span class="si">}</span>
</pre></div>
</div>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>docker<span class="w"> </span>run<span class="w"> </span>--net<span class="o">=</span>host<span class="w"> </span>-it<span class="w"> </span>--rm<span class="w"> </span>--name<span class="w"> </span><span class="si">${</span><span class="nv">CONTAINER_NAME</span><span class="si">}</span><span class="w"> </span><span class="si">${</span><span class="nv">IMAGE_NAME</span><span class="si">}</span>
</pre></div>
</div>
</section>
<section id="run-locally">
<h3>Run locally<a class="headerlink" href="#run-locally" title="Link to this heading">#</a></h3>
<p>First you need to download the model from mlflow. This can be done with the <em>“src/ml_flow/download_model.py”</em> script.
This scripts downloads a model and copies config and model data to the specific locations, such that the model can
be loaded.</p>
<p>For running/testing the keyword extraction locally you can use the <em>src/tests/test_process.py</em> script.</p>
<p>Model ist stored and loaded via DVC, you need the connection string under
https://portal.azure.com/#&#64;knecon.com/resource/subscriptions/4b9531fc-c5e4-4b11-8492-0cc173c1f97d/resourceGroups/taas-rg/providers/Microsoft.Storage/storageAccounts/taassaracer/keys</p>
</section>
</section>
</section>
<section id="upload-models-to-ml-flow">
<h1>Upload models to ML Flow<a class="headerlink" href="#upload-models-to-ml-flow" title="Link to this heading">#</a></h1>
<p>To upload the models to mlflow, you can use following script: src/mlflow/upload_model.py
For authentication following environment variables need to be set:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1">#AZURE_TENANT_ID=&quot;&quot;</span>
<span class="c1">#AZURE_SUBSCRIPTION_ID=&quot;&quot;</span>
<span class="c1">#AZURE_CLIENT_ID=&quot;&quot;</span>
<span class="c1">#AZURE_CLIENT_SECRET=&quot;&quot;</span>
</pre></div>
</div>
<p>Additional settings (resource group, experiment name, etc.) can be specified in the config (
<em>./src/mlflow/config/azure_config.toml</em>).
The <em>upload_model.py</em> has the following parameters:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">options</span><span class="p">:</span>
<span class="o">-</span><span class="n">h</span><span class="p">,</span> <span class="o">--</span><span class="n">help</span> <span class="n">show</span> <span class="n">this</span> <span class="n">help</span> <span class="n">message</span> <span class="ow">and</span> <span class="n">exit</span>
<span class="o">-</span><span class="n">a</span> <span class="n">AZURE_CONFIG</span><span class="p">,</span> <span class="o">--</span><span class="n">azure_config</span> <span class="n">AZURE_CONFIG</span>
<span class="n">Location</span> <span class="n">of</span> <span class="n">the</span> <span class="n">configuration</span> <span class="n">file</span> <span class="k">for</span> <span class="n">Azure</span> <span class="p">(</span><span class="n">default</span><span class="p">:</span> <span class="n">src</span><span class="o">/</span><span class="n">mlflow</span><span class="o">/</span><span class="n">config</span><span class="o">/</span><span class="n">azure_config</span><span class="o">.</span><span class="n">toml</span><span class="p">)</span>
<span class="o">-</span><span class="n">b</span> <span class="n">BASE_CONFIG</span><span class="p">,</span> <span class="o">--</span><span class="n">base_config</span> <span class="n">BASE_CONFIG</span>
<span class="n">Location</span> <span class="n">of</span> <span class="n">the</span> <span class="n">basic</span> <span class="n">training</span> <span class="n">configuration</span> <span class="p">(</span><span class="n">default</span><span class="p">:</span> <span class="n">src</span><span class="o">/</span><span class="n">mlflow</span><span class="o">/</span><span class="n">config</span><span class="o">/</span><span class="n">settings_de</span><span class="o">.</span><span class="n">toml</span><span class="p">)</span>
</pre></div>
</div>
<p>the base config contains all information for the models used. Examples for German and
English are placed in <em>/src/mlflow/config/</em></p>
<p>Note: Multi-language model tracking does not work for now. After the upload script reports an error, you have to
manually track the
model <a class="reference external" href="https://ml.azure.com/experiments?wsid=/subscriptions/4b9531fc-c5e4-4b11-8492-0cc173c1f97d/resourcegroups/fforesight-rg/providers/Microsoft.MachineLearningServices/workspaces/ff-aml-main&amp;tid=b44be368-e4f2-4ade-a089-cd2825458048">here</a>
where you can find the run. Adhere to the naming conventions for the model name and versions,
see <a class="reference external" href="https://ml.azure.com/model/list?wsid=/subscriptions/4b9531fc-c5e4-4b11-8492-0cc173c1f97d/resourcegroups/fforesight-rg/providers/Microsoft.MachineLearningServices/workspaces/ff-aml-main&amp;tid=b44be368-e4f2-4ade-a089-cd2825458048">here</a></p>
</section>
</article>
<footer class="prev-next-footer">
<div class="prev-next-area">
<a class="left-prev"
href="index.html"
title="previous page">
<i class="fa-solid fa-angle-left"></i>
<div class="prev-next-info">
<p class="prev-next-subtitle">previous</p>
<p class="prev-next-title">Welcome to Keyword Extraction Service documentation!</p>
</div>
</a>
<a class="right-next"
href="modules/cv_analysis.html"
title="next page">
<div class="prev-next-info">
<p class="prev-next-subtitle">next</p>
<p class="prev-next-title">cv_analysis package</p>
</div>
<i class="fa-solid fa-angle-right"></i>
</a>
</div>
</footer>
</div>
<div class="bd-sidebar-secondary bd-toc"><div class="sidebar-secondary-items sidebar-secondary__inner">
<div class="sidebar-secondary-item">
<div
id="pst-page-navigation-heading-2"
class="page-toc tocsection onthispage">
<i class="fa-solid fa-list"></i> On this page
</div>
<nav class="bd-toc-nav page-toc" aria-labelledby="pst-page-navigation-heading-2">
<ul class="visible nav section-nav flex-column">
<li class="toc-h1 nav-item toc-entry"><a class="reference internal nav-link" href="#">Keyword-Service</a><ul class="visible nav section-nav flex-column">
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#api">API</a><ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry"><a class="reference internal nav-link" href="#rest">REST</a></li>
<li class="toc-h3 nav-item toc-entry"><a class="reference internal nav-link" href="#rabbitmq">RabbitMQ</a></li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#service-configuration">Service Configuration</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#language">Language</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#usage">Usage</a><ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry"><a class="reference internal nav-link" href="#run-docker-commands">Run Docker Commands</a></li>
<li class="toc-h3 nav-item toc-entry"><a class="reference internal nav-link" href="#run-locally">Run locally</a></li>
</ul>
</li>
</ul>
</li>
<li class="toc-h1 nav-item toc-entry"><a class="reference internal nav-link" href="#upload-models-to-ml-flow">Upload models to ML Flow</a></li>
</ul>
</nav></div>
<div class="sidebar-secondary-item">
<div class="tocsection sourcelink">
<a href="_sources/README.md.txt">
<i class="fa-solid fa-file-lines"></i> Show Source
</a>
</div>
</div>
</div></div>
</div>
<footer class="bd-footer-content">
</footer>
</main>
</div>
</div>
<!-- Scripts loaded after <body> so the DOM is not blocked -->
<script src="_static/scripts/bootstrap.js?digest=8d27b9dea8ad943066ae"></script>
<script src="_static/scripts/pydata-sphinx-theme.js?digest=8d27b9dea8ad943066ae"></script>
<footer class="bd-footer">
<div class="bd-footer__inner bd-page-width">
<div class="footer-items__start">
<div class="footer-item">
<p class="copyright">
© Copyright All rights reserved.
<br/>
</p>
</div>
<div class="footer-item">
<p class="sphinx-version">
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 7.3.7.
<br/>
</p>
</div>
</div>
<div class="footer-items__end">
<div class="footer-item">
<p class="theme-version">
Built with the <a href="https://pydata-sphinx-theme.readthedocs.io/en/stable/index.html">PyData Sphinx Theme</a> 0.15.2.
</p></div>
</div>
</div>
</footer>
</body>
</html>

203
docs/build/html/_sources/README.md.txt vendored Normal file
View File

@ -0,0 +1,203 @@
# Keyword-Service
Service to get keywords of a paragraph or whole document.
<!-- TOC -->
- [Keyword-Service](#keyword-service)
- [API](#api)
- [REST](#rest)
- [RabbitMQ](#rabbitmq)
- [Service Configuration](#service-configuration)
- [Language](#language)
- [Usage](#usage)
- [Run Docker Commands](#run-docker-commands)
- [Run locally](#run-locally)
- [Upload models to ML Flow](#upload-models-to-ml-flow)
<!-- TOC -->
## API
### REST
The service provides endpoints to extract keywords from a text and to embed a text. For details, download
[OpenAPI schema](docs/openapi_redoc.html) and view it in a browser.
### RabbitMQ
The service listens to a queue and processes the messages. This method is ment to be used for extracting keywords from
whole documents. All RabbitMQ parameters including the queue names are set in environment variables, refer to the
service respective HELM chart for more information.
The input message should be a JSON object with the following structure:
```json
{
"targetFilePath": string,
"responseFilePath": string
}
```
The service downloads the file specified in `targetFilePath`. Supported data structures for the target file are:
- simplified text data (signifier key: `paragraphs`)
- structure object data (signifier key: `structureObjects`)
As a response, the service uploads a JSON-structured file (as defined in `responseFilePath`) with the result under the
`data` key. The structure of the response file is as follows:
```javascript
{
"targetFilePath"
:
string,
"responseFilePath"
:
string,
// and eventually further fields if present in the input message
"data"
:
[
{
"keywords": Array[string],
"paragraphId": int,
"embedding": Array[float] // 384 dimensions
}
]
}
```
**Note** that
- the `embedding` key is optional and can be omitted. The service will not calculate the embedding if the environment
variable `MODEL__COMPUTE_EMBEDDINGS` is set to `false`.
- the service also computes the keywords for the whole document. In this case, the `paragraphId` is set to `-1`.
## Service Configuration
The service is configured via environment variables. The following variables are available:
| Variable | Description | Default |
| ------------------------------------------ | ----------------------------------------------------------------------------------- | ------- |
| LOGGING__LEVEL | Logging level | INFO |
| MODEL__MAX_KEYWORDS_PER_PARAGRAPH | Maximum number of keywords per paragraph | 5 |
| MODLE__MAX_KEYWORDS_PER_DOCUMENT | Maximum number of keywords per document, when set to 0, no keywords are extracted | 0 |
| MODEL__COMPUTE_EMBEDDINGS | Whether to compute keyword embeddings or not | true |
| MODEL__PREPROCESSING__MIN_PARAGRAPH_LENGTH | Minimum number of characters in a paragraph to be considered for keyword extraction | 1 |
| MODEL__POSTPROCESSING__FILTER_SUBWORDS | Whether to filter out subwords from the keywords or not | true |
**NOTE** that these variables are subject to change. For the most recent configuration, refer to the service respective
HELM chart.
## Language
Currently, there is an english, a german and a multi-language model for keyword extraction. The models are uploaded to
mlflow and can
be set in the Dockerfile when building the container:
example for german model:
```
ENV AZURE_RESOURCE_GROUP="mriedl"
ENV AZURE_AML_WORKSPACE="azureml-ws"
ENV AZURE_AML_MODEL_NAME="keyword-extraction-de"
ENV AZURE_AML_MODEL_VERSION="1"
```
and example for english model:
```
ENV AZURE_RESOURCE_GROUP="mriedl"
ENV AZURE_AML_WORKSPACE="azureml-ws"
ENV AZURE_AML_MODEL_NAME="keyword-extraction-de"
ENV AZURE_AML_MODEL_VERSION="1"
```
## Usage
**Two Options:**
1. REST: Send text per request to endpoint, endpoint returns keywords
2. Queue: Service gets text from queue, model calculates keywords, save keywords in queue
To test the REST endpoint you have to set up an environment and do poetry install (
see https://gitlab.knecon.com/knecon/research/template-python-project for details for setting up poetry)
Then run
```
python ./src/serve.py
```
You don't need to start a queue for that, just ignore the AMQP Error.
Port and host are set in settings.toml .
You can use the FastAPI under 127.0.0.1:8001/docs to send request to endpoint.
You can also test the service with docker:
#### Run Docker Commands
```bash
docker build -t ${IMAGE_NAME} -f Dockerfile --build-arg GITLAB_USER=${GITLAB_USER} \
--build-arg GITLAB_ACCESS_TOKEN=${GITLAB_ACCESS_TOKEN} \
--build-arg AZURE_TENANT_ID=${AZURE_TENANT_ID} \
--build-arg AZURE_SUBSCRIPTION_ID=${AZURE_SUBSCRIPTION_ID} \
--build-arg AZURE_CLIENT_ID=${AZURE_CLIENT_ID} \
--build-arg AZURE_CLIENT_SECRET=${AZURE_CLIENT_SECRET} \
--build-arg AZURE_AML_MODEL_VERSION=${AZURE_AML_MODEL_VERSION} \
--build-arg AZURE_AML_MODEL_NAME=${AZURE_AML_MODEL_NAME} \
--build-arg AZURE_RESOURCE_GROUP=${AZURE_RESOURCE_GROUP} \
--build-arg AZURE_AML_WORKSPACE=${AZURE_AML_WORKSPACE}
```
```bash
docker run --net=host -it --rm --name ${CONTAINER_NAME} ${IMAGE_NAME}
```
#### Run locally
First you need to download the model from mlflow. This can be done with the *"src/ml_flow/download_model.py"* script.
This scripts downloads a model and copies config and model data to the specific locations, such that the model can
be loaded.
For running/testing the keyword extraction locally you can use the *src/tests/test_process.py* script.
Model ist stored and loaded via DVC, you need the connection string under
https://portal.azure.com/#@knecon.com/resource/subscriptions/4b9531fc-c5e4-4b11-8492-0cc173c1f97d/resourceGroups/taas-rg/providers/Microsoft.Storage/storageAccounts/taassaracer/keys
# Upload models to ML Flow
To upload the models to mlflow, you can use following script: src/mlflow/upload_model.py
For authentication following environment variables need to be set:
```
#AZURE_TENANT_ID=""
#AZURE_SUBSCRIPTION_ID=""
#AZURE_CLIENT_ID=""
#AZURE_CLIENT_SECRET=""
```
Additional settings (resource group, experiment name, etc.) can be specified in the config (
*./src/mlflow/config/azure_config.toml*).
The *upload_model.py* has the following parameters:
```
options:
-h, --help show this help message and exit
-a AZURE_CONFIG, --azure_config AZURE_CONFIG
Location of the configuration file for Azure (default: src/mlflow/config/azure_config.toml)
-b BASE_CONFIG, --base_config BASE_CONFIG
Location of the basic training configuration (default: src/mlflow/config/settings_de.toml)
```
the base config contains all information for the models used. Examples for German and
English are placed in */src/mlflow/config/*
Note: Multi-language model tracking does not work for now. After the upload script reports an error, you have to
manually track the
model [here](https://ml.azure.com/experiments?wsid=/subscriptions/4b9531fc-c5e4-4b11-8492-0cc173c1f97d/resourcegroups/fforesight-rg/providers/Microsoft.MachineLearningServices/workspaces/ff-aml-main&tid=b44be368-e4f2-4ade-a089-cd2825458048)
where you can find the run. Adhere to the naming conventions for the model name and versions,
see [here](https://ml.azure.com/model/list?wsid=/subscriptions/4b9531fc-c5e4-4b11-8492-0cc173c1f97d/resourcegroups/fforesight-rg/providers/Microsoft.MachineLearningServices/workspaces/ff-aml-main&tid=b44be368-e4f2-4ade-a089-cd2825458048)

37
docs/build/html/_sources/index.rst.txt vendored Normal file
View File

@ -0,0 +1,37 @@
.. Keyword Extraction Service documentation master file, created by
sphinx-quickstart on Mon Sep 12 12:04:24 2022.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
====================================================
Welcome to Keyword Extraction Service documentation!
====================================================
.. note::
If you'd like to change the looks of things 👉 https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html
Table of Contents
-----------------
.. toctree::
:maxdepth: 3
:caption: README
README.md
.. toctree::
:maxdepth: 3
:caption: Modules
modules/cv_analysis
modules/serve
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@ -0,0 +1,7 @@
cv\_analysis.config module
==========================
.. automodule:: cv_analysis.config
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
cv\_analysis.figure\_detection.figure\_detection module
=======================================================
.. automodule:: cv_analysis.figure_detection.figure_detection
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
cv\_analysis.figure\_detection.figures module
=============================================
.. automodule:: cv_analysis.figure_detection.figures
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,17 @@
cv\_analysis.figure\_detection package
======================================
.. automodule:: cv_analysis.figure_detection
:members:
:undoc-members:
:show-inheritance:
Submodules
----------
.. toctree::
:maxdepth: 4
cv_analysis.figure_detection.figure_detection
cv_analysis.figure_detection.figures
cv_analysis.figure_detection.text

View File

@ -0,0 +1,7 @@
cv\_analysis.figure\_detection.text module
==========================================
.. automodule:: cv_analysis.figure_detection.text
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
cv\_analysis.layout\_parsing module
===================================
.. automodule:: cv_analysis.layout_parsing
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
cv\_analysis.locations module
=============================
.. automodule:: cv_analysis.locations
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
cv\_analysis.redaction\_detection module
========================================
.. automodule:: cv_analysis.redaction_detection
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,30 @@
cv\_analysis package
====================
.. automodule:: cv_analysis
:members:
:undoc-members:
:show-inheritance:
Subpackages
-----------
.. toctree::
:maxdepth: 4
cv_analysis.figure_detection
cv_analysis.server
cv_analysis.utils
Submodules
----------
.. toctree::
:maxdepth: 4
cv_analysis.config
cv_analysis.layout_parsing
cv_analysis.locations
cv_analysis.redaction_detection
cv_analysis.table_inference
cv_analysis.table_parsing

View File

@ -0,0 +1,7 @@
cv\_analysis.server.pipeline module
===================================
.. automodule:: cv_analysis.server.pipeline
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,15 @@
cv\_analysis.server package
===========================
.. automodule:: cv_analysis.server
:members:
:undoc-members:
:show-inheritance:
Submodules
----------
.. toctree::
:maxdepth: 4
cv_analysis.server.pipeline

View File

@ -0,0 +1,7 @@
cv\_analysis.table\_inference module
====================================
.. automodule:: cv_analysis.table_inference
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
cv\_analysis.table\_parsing module
==================================
.. automodule:: cv_analysis.table_parsing
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
cv\_analysis.utils.annotate module
==================================
.. automodule:: cv_analysis.utils.annotate
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
cv\_analysis.utils.banner module
================================
.. automodule:: cv_analysis.utils.banner
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
cv\_analysis.utils.connect\_rects module
========================================
.. automodule:: cv_analysis.utils.connect_rects
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
cv\_analysis.utils.display module
=================================
.. automodule:: cv_analysis.utils.display
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
cv\_analysis.utils.draw module
==============================
.. automodule:: cv_analysis.utils.draw
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
cv\_analysis.utils.filters module
=================================
.. automodule:: cv_analysis.utils.filters
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
cv\_analysis.utils.image\_extraction module
===========================================
.. automodule:: cv_analysis.utils.image_extraction
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
cv\_analysis.utils.open\_pdf module
===================================
.. automodule:: cv_analysis.utils.open_pdf
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
cv\_analysis.utils.postprocessing module
========================================
.. automodule:: cv_analysis.utils.postprocessing
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
cv\_analysis.utils.preprocessing module
=======================================
.. automodule:: cv_analysis.utils.preprocessing
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,28 @@
cv\_analysis.utils package
==========================
.. automodule:: cv_analysis.utils
:members:
:undoc-members:
:show-inheritance:
Submodules
----------
.. toctree::
:maxdepth: 4
cv_analysis.utils.annotate
cv_analysis.utils.banner
cv_analysis.utils.connect_rects
cv_analysis.utils.display
cv_analysis.utils.draw
cv_analysis.utils.filters
cv_analysis.utils.image_extraction
cv_analysis.utils.open_pdf
cv_analysis.utils.postprocessing
cv_analysis.utils.preprocessing
cv_analysis.utils.structures
cv_analysis.utils.test_metrics
cv_analysis.utils.utils
cv_analysis.utils.visual_logging

View File

@ -0,0 +1,7 @@
cv\_analysis.utils.structures module
====================================
.. automodule:: cv_analysis.utils.structures
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
cv\_analysis.utils.test\_metrics module
=======================================
.. automodule:: cv_analysis.utils.test_metrics
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
cv\_analysis.utils.utils module
===============================
.. automodule:: cv_analysis.utils.utils
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
cv\_analysis.utils.visual\_logging module
=========================================
.. automodule:: cv_analysis.utils.visual_logging
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
serve module
============
.. automodule:: serve
:members:
:undoc-members:
:show-inheritance:

925
docs/build/html/_static/basic.css vendored Normal file
View File

@ -0,0 +1,925 @@
/*
* basic.css
* ~~~~~~~~~
*
* Sphinx stylesheet -- basic theme.
*
* :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
/* -- main layout ----------------------------------------------------------- */
div.clearer {
clear: both;
}
div.section::after {
display: block;
content: '';
clear: left;
}
/* -- relbar ---------------------------------------------------------------- */
div.related {
width: 100%;
font-size: 90%;
}
div.related h3 {
display: none;
}
div.related ul {
margin: 0;
padding: 0 0 0 10px;
list-style: none;
}
div.related li {
display: inline;
}
div.related li.right {
float: right;
margin-right: 5px;
}
/* -- sidebar --------------------------------------------------------------- */
div.sphinxsidebarwrapper {
padding: 10px 5px 0 10px;
}
div.sphinxsidebar {
float: left;
width: 270px;
margin-left: -100%;
font-size: 90%;
word-wrap: break-word;
overflow-wrap : break-word;
}
div.sphinxsidebar ul {
list-style: none;
}
div.sphinxsidebar ul ul,
div.sphinxsidebar ul.want-points {
margin-left: 20px;
list-style: square;
}
div.sphinxsidebar ul ul {
margin-top: 0;
margin-bottom: 0;
}
div.sphinxsidebar form {
margin-top: 10px;
}
div.sphinxsidebar input {
border: 1px solid #98dbcc;
font-family: sans-serif;
font-size: 1em;
}
div.sphinxsidebar #searchbox form.search {
overflow: hidden;
}
div.sphinxsidebar #searchbox input[type="text"] {
float: left;
width: 80%;
padding: 0.25em;
box-sizing: border-box;
}
div.sphinxsidebar #searchbox input[type="submit"] {
float: left;
width: 20%;
border-left: none;
padding: 0.25em;
box-sizing: border-box;
}
img {
border: 0;
max-width: 100%;
}
/* -- search page ----------------------------------------------------------- */
ul.search {
margin: 10px 0 0 20px;
padding: 0;
}
ul.search li {
padding: 5px 0 5px 20px;
background-image: url(file.png);
background-repeat: no-repeat;
background-position: 0 7px;
}
ul.search li a {
font-weight: bold;
}
ul.search li p.context {
color: #888;
margin: 2px 0 0 30px;
text-align: left;
}
ul.keywordmatches li.goodmatch a {
font-weight: bold;
}
/* -- index page ------------------------------------------------------------ */
table.contentstable {
width: 90%;
margin-left: auto;
margin-right: auto;
}
table.contentstable p.biglink {
line-height: 150%;
}
a.biglink {
font-size: 1.3em;
}
span.linkdescr {
font-style: italic;
padding-top: 5px;
font-size: 90%;
}
/* -- general index --------------------------------------------------------- */
table.indextable {
width: 100%;
}
table.indextable td {
text-align: left;
vertical-align: top;
}
table.indextable ul {
margin-top: 0;
margin-bottom: 0;
list-style-type: none;
}
table.indextable > tbody > tr > td > ul {
padding-left: 0em;
}
table.indextable tr.pcap {
height: 10px;
}
table.indextable tr.cap {
margin-top: 10px;
background-color: #f2f2f2;
}
img.toggler {
margin-right: 3px;
margin-top: 3px;
cursor: pointer;
}
div.modindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
div.genindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
/* -- domain module index --------------------------------------------------- */
table.modindextable td {
padding: 2px;
border-collapse: collapse;
}
/* -- general body styles --------------------------------------------------- */
div.body {
min-width: 360px;
max-width: 800px;
}
div.body p, div.body dd, div.body li, div.body blockquote {
-moz-hyphens: auto;
-ms-hyphens: auto;
-webkit-hyphens: auto;
hyphens: auto;
}
a.headerlink {
visibility: hidden;
}
a:visited {
color: #551A8B;
}
h1:hover > a.headerlink,
h2:hover > a.headerlink,
h3:hover > a.headerlink,
h4:hover > a.headerlink,
h5:hover > a.headerlink,
h6:hover > a.headerlink,
dt:hover > a.headerlink,
caption:hover > a.headerlink,
p.caption:hover > a.headerlink,
div.code-block-caption:hover > a.headerlink {
visibility: visible;
}
div.body p.caption {
text-align: inherit;
}
div.body td {
text-align: left;
}
.first {
margin-top: 0 !important;
}
p.rubric {
margin-top: 30px;
font-weight: bold;
}
img.align-left, figure.align-left, .figure.align-left, object.align-left {
clear: left;
float: left;
margin-right: 1em;
}
img.align-right, figure.align-right, .figure.align-right, object.align-right {
clear: right;
float: right;
margin-left: 1em;
}
img.align-center, figure.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
img.align-default, figure.align-default, .figure.align-default {
display: block;
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left;
}
.align-center {
text-align: center;
}
.align-default {
text-align: center;
}
.align-right {
text-align: right;
}
/* -- sidebars -------------------------------------------------------------- */
div.sidebar,
aside.sidebar {
margin: 0 0 0.5em 1em;
border: 1px solid #ddb;
padding: 7px;
background-color: #ffe;
width: 40%;
float: right;
clear: right;
overflow-x: auto;
}
p.sidebar-title {
font-weight: bold;
}
nav.contents,
aside.topic,
div.admonition, div.topic, blockquote {
clear: left;
}
/* -- topics ---------------------------------------------------------------- */
nav.contents,
aside.topic,
div.topic {
border: 1px solid #ccc;
padding: 7px;
margin: 10px 0 10px 0;
}
p.topic-title {
font-size: 1.1em;
font-weight: bold;
margin-top: 10px;
}
/* -- admonitions ----------------------------------------------------------- */
div.admonition {
margin-top: 10px;
margin-bottom: 10px;
padding: 7px;
}
div.admonition dt {
font-weight: bold;
}
p.admonition-title {
margin: 0px 10px 5px 0px;
font-weight: bold;
}
div.body p.centered {
text-align: center;
margin-top: 25px;
}
/* -- content of sidebars/topics/admonitions -------------------------------- */
div.sidebar > :last-child,
aside.sidebar > :last-child,
nav.contents > :last-child,
aside.topic > :last-child,
div.topic > :last-child,
div.admonition > :last-child {
margin-bottom: 0;
}
div.sidebar::after,
aside.sidebar::after,
nav.contents::after,
aside.topic::after,
div.topic::after,
div.admonition::after,
blockquote::after {
display: block;
content: '';
clear: both;
}
/* -- tables ---------------------------------------------------------------- */
table.docutils {
margin-top: 10px;
margin-bottom: 10px;
border: 0;
border-collapse: collapse;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
table.align-default {
margin-left: auto;
margin-right: auto;
}
table caption span.caption-number {
font-style: italic;
}
table caption span.caption-text {
}
table.docutils td, table.docutils th {
padding: 1px 8px 1px 5px;
border-top: 0;
border-left: 0;
border-right: 0;
border-bottom: 1px solid #aaa;
}
th {
text-align: left;
padding-right: 5px;
}
table.citation {
border-left: solid 1px gray;
margin-left: 1px;
}
table.citation td {
border-bottom: none;
}
th > :first-child,
td > :first-child {
margin-top: 0px;
}
th > :last-child,
td > :last-child {
margin-bottom: 0px;
}
/* -- figures --------------------------------------------------------------- */
div.figure, figure {
margin: 0.5em;
padding: 0.5em;
}
div.figure p.caption, figcaption {
padding: 0.3em;
}
div.figure p.caption span.caption-number,
figcaption span.caption-number {
font-style: italic;
}
div.figure p.caption span.caption-text,
figcaption span.caption-text {
}
/* -- field list styles ----------------------------------------------------- */
table.field-list td, table.field-list th {
border: 0 !important;
}
.field-list ul {
margin: 0;
padding-left: 1em;
}
.field-list p {
margin: 0;
}
.field-name {
-moz-hyphens: manual;
-ms-hyphens: manual;
-webkit-hyphens: manual;
hyphens: manual;
}
/* -- hlist styles ---------------------------------------------------------- */
table.hlist {
margin: 1em 0;
}
table.hlist td {
vertical-align: top;
}
/* -- object description styles --------------------------------------------- */
.sig {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
}
.sig-name, code.descname {
background-color: transparent;
font-weight: bold;
}
.sig-name {
font-size: 1.1em;
}
code.descname {
font-size: 1.2em;
}
.sig-prename, code.descclassname {
background-color: transparent;
}
.optional {
font-size: 1.3em;
}
.sig-paren {
font-size: larger;
}
.sig-param.n {
font-style: italic;
}
/* C++ specific styling */
.sig-inline.c-texpr,
.sig-inline.cpp-texpr {
font-family: unset;
}
.sig.c .k, .sig.c .kt,
.sig.cpp .k, .sig.cpp .kt {
color: #0033B3;
}
.sig.c .m,
.sig.cpp .m {
color: #1750EB;
}
.sig.c .s, .sig.c .sc,
.sig.cpp .s, .sig.cpp .sc {
color: #067D17;
}
/* -- other body styles ----------------------------------------------------- */
ol.arabic {
list-style: decimal;
}
ol.loweralpha {
list-style: lower-alpha;
}
ol.upperalpha {
list-style: upper-alpha;
}
ol.lowerroman {
list-style: lower-roman;
}
ol.upperroman {
list-style: upper-roman;
}
:not(li) > ol > li:first-child > :first-child,
:not(li) > ul > li:first-child > :first-child {
margin-top: 0px;
}
:not(li) > ol > li:last-child > :last-child,
:not(li) > ul > li:last-child > :last-child {
margin-bottom: 0px;
}
ol.simple ol p,
ol.simple ul p,
ul.simple ol p,
ul.simple ul p {
margin-top: 0;
}
ol.simple > li:not(:first-child) > p,
ul.simple > li:not(:first-child) > p {
margin-top: 0;
}
ol.simple p,
ul.simple p {
margin-bottom: 0;
}
aside.footnote > span,
div.citation > span {
float: left;
}
aside.footnote > span:last-of-type,
div.citation > span:last-of-type {
padding-right: 0.5em;
}
aside.footnote > p {
margin-left: 2em;
}
div.citation > p {
margin-left: 4em;
}
aside.footnote > p:last-of-type,
div.citation > p:last-of-type {
margin-bottom: 0em;
}
aside.footnote > p:last-of-type:after,
div.citation > p:last-of-type:after {
content: "";
clear: both;
}
dl.field-list {
display: grid;
grid-template-columns: fit-content(30%) auto;
}
dl.field-list > dt {
font-weight: bold;
word-break: break-word;
padding-left: 0.5em;
padding-right: 5px;
}
dl.field-list > dd {
padding-left: 0.5em;
margin-top: 0em;
margin-left: 0em;
margin-bottom: 0em;
}
dl {
margin-bottom: 15px;
}
dd > :first-child {
margin-top: 0px;
}
dd ul, dd table {
margin-bottom: 10px;
}
dd {
margin-top: 3px;
margin-bottom: 10px;
margin-left: 30px;
}
.sig dd {
margin-top: 0px;
margin-bottom: 0px;
}
.sig dl {
margin-top: 0px;
margin-bottom: 0px;
}
dl > dd:last-child,
dl > dd:last-child > :last-child {
margin-bottom: 0;
}
dt:target, span.highlighted {
background-color: #fbe54e;
}
rect.highlighted {
fill: #fbe54e;
}
dl.glossary dt {
font-weight: bold;
font-size: 1.1em;
}
.versionmodified {
font-style: italic;
}
.system-message {
background-color: #fda;
padding: 5px;
border: 3px solid red;
}
.footnote:target {
background-color: #ffa;
}
.line-block {
display: block;
margin-top: 1em;
margin-bottom: 1em;
}
.line-block .line-block {
margin-top: 0;
margin-bottom: 0;
margin-left: 1.5em;
}
.guilabel, .menuselection {
font-family: sans-serif;
}
.accelerator {
text-decoration: underline;
}
.classifier {
font-style: oblique;
}
.classifier:before {
font-style: normal;
margin: 0 0.5em;
content: ":";
display: inline-block;
}
abbr, acronym {
border-bottom: dotted 1px;
cursor: help;
}
.translated {
background-color: rgba(207, 255, 207, 0.2)
}
.untranslated {
background-color: rgba(255, 207, 207, 0.2)
}
/* -- code displays --------------------------------------------------------- */
pre {
overflow: auto;
overflow-y: hidden; /* fixes display issues on Chrome browsers */
}
pre, div[class*="highlight-"] {
clear: both;
}
span.pre {
-moz-hyphens: none;
-ms-hyphens: none;
-webkit-hyphens: none;
hyphens: none;
white-space: nowrap;
}
div[class*="highlight-"] {
margin: 1em 0;
}
td.linenos pre {
border: 0;
background-color: transparent;
color: #aaa;
}
table.highlighttable {
display: block;
}
table.highlighttable tbody {
display: block;
}
table.highlighttable tr {
display: flex;
}
table.highlighttable td {
margin: 0;
padding: 0;
}
table.highlighttable td.linenos {
padding-right: 0.5em;
}
table.highlighttable td.code {
flex: 1;
overflow: hidden;
}
.highlight .hll {
display: block;
}
div.highlight pre,
table.highlighttable pre {
margin: 0;
}
div.code-block-caption + div {
margin-top: 0;
}
div.code-block-caption {
margin-top: 1em;
padding: 2px 5px;
font-size: small;
}
div.code-block-caption code {
background-color: transparent;
}
table.highlighttable td.linenos,
span.linenos,
div.highlight span.gp { /* gp: Generic.Prompt */
user-select: none;
-webkit-user-select: text; /* Safari fallback only */
-webkit-user-select: none; /* Chrome/Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+ */
}
div.code-block-caption span.caption-number {
padding: 0.1em 0.3em;
font-style: italic;
}
div.code-block-caption span.caption-text {
}
div.literal-block-wrapper {
margin: 1em 0;
}
code.xref, a code {
background-color: transparent;
font-weight: bold;
}
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
background-color: transparent;
}
.viewcode-link {
float: right;
}
.viewcode-back {
float: right;
font-family: sans-serif;
}
div.viewcode-block:target {
margin: -1px -10px;
padding: 0 10px;
}
/* -- math display ---------------------------------------------------------- */
img.math {
vertical-align: middle;
}
div.body div.math p {
text-align: center;
}
span.eqno {
float: right;
}
span.eqno a.headerlink {
position: absolute;
z-index: 1;
}
div.math:hover a.headerlink {
visibility: visible;
}
/* -- printout stylesheet --------------------------------------------------- */
@media print {
div.document,
div.documentwrapper,
div.bodywrapper {
margin: 0 !important;
width: 100%;
}
div.sphinxsidebar,
div.related,
div.footer,
#top-link {
display: none;
}
}

156
docs/build/html/_static/doctools.js vendored Normal file
View File

@ -0,0 +1,156 @@
/*
* doctools.js
* ~~~~~~~~~~~
*
* Base JavaScript utilities for all Sphinx HTML documentation.
*
* :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
"use strict";
const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([
"TEXTAREA",
"INPUT",
"SELECT",
"BUTTON",
]);
const _ready = (callback) => {
if (document.readyState !== "loading") {
callback();
} else {
document.addEventListener("DOMContentLoaded", callback);
}
};
/**
* Small JavaScript module for the documentation.
*/
const Documentation = {
init: () => {
Documentation.initDomainIndexTable();
Documentation.initOnKeyListeners();
},
/**
* i18n support
*/
TRANSLATIONS: {},
PLURAL_EXPR: (n) => (n === 1 ? 0 : 1),
LOCALE: "unknown",
// gettext and ngettext don't access this so that the functions
// can safely bound to a different name (_ = Documentation.gettext)
gettext: (string) => {
const translated = Documentation.TRANSLATIONS[string];
switch (typeof translated) {
case "undefined":
return string; // no translation
case "string":
return translated; // translation exists
default:
return translated[0]; // (singular, plural) translation tuple exists
}
},
ngettext: (singular, plural, n) => {
const translated = Documentation.TRANSLATIONS[singular];
if (typeof translated !== "undefined")
return translated[Documentation.PLURAL_EXPR(n)];
return n === 1 ? singular : plural;
},
addTranslations: (catalog) => {
Object.assign(Documentation.TRANSLATIONS, catalog.messages);
Documentation.PLURAL_EXPR = new Function(
"n",
`return (${catalog.plural_expr})`
);
Documentation.LOCALE = catalog.locale;
},
/**
* helper function to focus on search bar
*/
focusSearchBar: () => {
document.querySelectorAll("input[name=q]")[0]?.focus();
},
/**
* Initialise the domain index toggle buttons
*/
initDomainIndexTable: () => {
const toggler = (el) => {
const idNumber = el.id.substr(7);
const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`);
if (el.src.substr(-9) === "minus.png") {
el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`;
toggledRows.forEach((el) => (el.style.display = "none"));
} else {
el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`;
toggledRows.forEach((el) => (el.style.display = ""));
}
};
const togglerElements = document.querySelectorAll("img.toggler");
togglerElements.forEach((el) =>
el.addEventListener("click", (event) => toggler(event.currentTarget))
);
togglerElements.forEach((el) => (el.style.display = ""));
if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler);
},
initOnKeyListeners: () => {
// only install a listener if it is really needed
if (
!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS &&
!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.altKey || event.ctrlKey || event.metaKey) return;
if (!event.shiftKey) {
switch (event.key) {
case "ArrowLeft":
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
const prevLink = document.querySelector('link[rel="prev"]');
if (prevLink && prevLink.href) {
window.location.href = prevLink.href;
event.preventDefault();
}
break;
case "ArrowRight":
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
const nextLink = document.querySelector('link[rel="next"]');
if (nextLink && nextLink.href) {
window.location.href = nextLink.href;
event.preventDefault();
}
break;
}
}
// some keyboard layouts may need Shift to get /
switch (event.key) {
case "/":
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
Documentation.focusSearchBar();
event.preventDefault();
}
});
},
};
// quick alias for translations
const _ = Documentation.gettext;
_ready(Documentation.init);

View File

@ -0,0 +1,13 @@
const DOCUMENTATION_OPTIONS = {
VERSION: '2.5.1',
LANGUAGE: 'en',
COLLAPSE_INDEX: false,
BUILDER: 'html',
FILE_SUFFIX: '.html',
LINK_SUFFIX: '.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: '.txt',
NAVIGATION_WITH_KEYS: false,
SHOW_SEARCH_SUMMARY: true,
ENABLE_SEARCH_SHORTCUTS: true,
};

BIN
docs/build/html/_static/file.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

199
docs/build/html/_static/language_data.js vendored Normal file
View File

@ -0,0 +1,199 @@
/*
* language_data.js
* ~~~~~~~~~~~~~~~~
*
* This script contains the language-specific data used by searchtools.js,
* namely the list of stopwords, stemmer, scorer and splitter.
*
* :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"];
/* Non-minified version is copied as a separate JS file, if available */
/**
* Porter Stemmer
*/
var Stemmer = function() {
var step2list = {
ational: 'ate',
tional: 'tion',
enci: 'ence',
anci: 'ance',
izer: 'ize',
bli: 'ble',
alli: 'al',
entli: 'ent',
eli: 'e',
ousli: 'ous',
ization: 'ize',
ation: 'ate',
ator: 'ate',
alism: 'al',
iveness: 'ive',
fulness: 'ful',
ousness: 'ous',
aliti: 'al',
iviti: 'ive',
biliti: 'ble',
logi: 'log'
};
var step3list = {
icate: 'ic',
ative: '',
alize: 'al',
iciti: 'ic',
ical: 'ic',
ful: '',
ness: ''
};
var c = "[^aeiou]"; // consonant
var v = "[aeiouy]"; // vowel
var C = c + "[^aeiouy]*"; // consonant sequence
var V = v + "[aeiou]*"; // vowel sequence
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
var s_v = "^(" + C + ")?" + v; // vowel in stem
this.stemWord = function (w) {
var stem;
var suffix;
var firstch;
var origword = w;
if (w.length < 3)
return w;
var re;
var re2;
var re3;
var re4;
firstch = w.substr(0,1);
if (firstch == "y")
w = firstch.toUpperCase() + w.substr(1);
// Step 1a
re = /^(.+?)(ss|i)es$/;
re2 = /^(.+?)([^s])s$/;
if (re.test(w))
w = w.replace(re,"$1$2");
else if (re2.test(w))
w = w.replace(re2,"$1$2");
// Step 1b
re = /^(.+?)eed$/;
re2 = /^(.+?)(ed|ing)$/;
if (re.test(w)) {
var fp = re.exec(w);
re = new RegExp(mgr0);
if (re.test(fp[1])) {
re = /.$/;
w = w.replace(re,"");
}
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1];
re2 = new RegExp(s_v);
if (re2.test(stem)) {
w = stem;
re2 = /(at|bl|iz)$/;
re3 = new RegExp("([^aeiouylsz])\\1$");
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re2.test(w))
w = w + "e";
else if (re3.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
else if (re4.test(w))
w = w + "e";
}
}
// Step 1c
re = /^(.+?)y$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(s_v);
if (re.test(stem))
w = stem + "i";
}
// Step 2
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step2list[suffix];
}
// Step 3
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step3list[suffix];
}
// Step 4
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
re2 = /^(.+?)(s|t)(ion)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
if (re.test(stem))
w = stem;
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1] + fp[2];
re2 = new RegExp(mgr1);
if (re2.test(stem))
w = stem;
}
// Step 5
re = /^(.+?)e$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
re2 = new RegExp(meq1);
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
w = stem;
}
re = /ll$/;
re2 = new RegExp(mgr1);
if (re.test(w) && re2.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
// and turn initial Y back to y
if (firstch == "y")
w = firstch.toLowerCase() + w.substr(1);
return w;
}
}

BIN
docs/build/html/_static/logo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
docs/build/html/_static/minus.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

BIN
docs/build/html/_static/plus.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

152
docs/build/html/_static/pygments.css vendored Normal file
View File

@ -0,0 +1,152 @@
html[data-theme="light"] .highlight pre { line-height: 125%; }
html[data-theme="light"] .highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
html[data-theme="light"] .highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
html[data-theme="light"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
html[data-theme="light"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
html[data-theme="light"] .highlight .hll { background-color: #7971292e }
html[data-theme="light"] .highlight { background: #fefefe; color: #545454 }
html[data-theme="light"] .highlight .c { color: #797129 } /* Comment */
html[data-theme="light"] .highlight .err { color: #d91e18 } /* Error */
html[data-theme="light"] .highlight .k { color: #7928a1 } /* Keyword */
html[data-theme="light"] .highlight .l { color: #797129 } /* Literal */
html[data-theme="light"] .highlight .n { color: #545454 } /* Name */
html[data-theme="light"] .highlight .o { color: #008000 } /* Operator */
html[data-theme="light"] .highlight .p { color: #545454 } /* Punctuation */
html[data-theme="light"] .highlight .ch { color: #797129 } /* Comment.Hashbang */
html[data-theme="light"] .highlight .cm { color: #797129 } /* Comment.Multiline */
html[data-theme="light"] .highlight .cp { color: #797129 } /* Comment.Preproc */
html[data-theme="light"] .highlight .cpf { color: #797129 } /* Comment.PreprocFile */
html[data-theme="light"] .highlight .c1 { color: #797129 } /* Comment.Single */
html[data-theme="light"] .highlight .cs { color: #797129 } /* Comment.Special */
html[data-theme="light"] .highlight .gd { color: #007faa } /* Generic.Deleted */
html[data-theme="light"] .highlight .ge { font-style: italic } /* Generic.Emph */
html[data-theme="light"] .highlight .gh { color: #007faa } /* Generic.Heading */
html[data-theme="light"] .highlight .gs { font-weight: bold } /* Generic.Strong */
html[data-theme="light"] .highlight .gu { color: #007faa } /* Generic.Subheading */
html[data-theme="light"] .highlight .kc { color: #7928a1 } /* Keyword.Constant */
html[data-theme="light"] .highlight .kd { color: #7928a1 } /* Keyword.Declaration */
html[data-theme="light"] .highlight .kn { color: #7928a1 } /* Keyword.Namespace */
html[data-theme="light"] .highlight .kp { color: #7928a1 } /* Keyword.Pseudo */
html[data-theme="light"] .highlight .kr { color: #7928a1 } /* Keyword.Reserved */
html[data-theme="light"] .highlight .kt { color: #797129 } /* Keyword.Type */
html[data-theme="light"] .highlight .ld { color: #797129 } /* Literal.Date */
html[data-theme="light"] .highlight .m { color: #797129 } /* Literal.Number */
html[data-theme="light"] .highlight .s { color: #008000 } /* Literal.String */
html[data-theme="light"] .highlight .na { color: #797129 } /* Name.Attribute */
html[data-theme="light"] .highlight .nb { color: #797129 } /* Name.Builtin */
html[data-theme="light"] .highlight .nc { color: #007faa } /* Name.Class */
html[data-theme="light"] .highlight .no { color: #007faa } /* Name.Constant */
html[data-theme="light"] .highlight .nd { color: #797129 } /* Name.Decorator */
html[data-theme="light"] .highlight .ni { color: #008000 } /* Name.Entity */
html[data-theme="light"] .highlight .ne { color: #7928a1 } /* Name.Exception */
html[data-theme="light"] .highlight .nf { color: #007faa } /* Name.Function */
html[data-theme="light"] .highlight .nl { color: #797129 } /* Name.Label */
html[data-theme="light"] .highlight .nn { color: #545454 } /* Name.Namespace */
html[data-theme="light"] .highlight .nx { color: #545454 } /* Name.Other */
html[data-theme="light"] .highlight .py { color: #007faa } /* Name.Property */
html[data-theme="light"] .highlight .nt { color: #007faa } /* Name.Tag */
html[data-theme="light"] .highlight .nv { color: #d91e18 } /* Name.Variable */
html[data-theme="light"] .highlight .ow { color: #7928a1 } /* Operator.Word */
html[data-theme="light"] .highlight .pm { color: #545454 } /* Punctuation.Marker */
html[data-theme="light"] .highlight .w { color: #545454 } /* Text.Whitespace */
html[data-theme="light"] .highlight .mb { color: #797129 } /* Literal.Number.Bin */
html[data-theme="light"] .highlight .mf { color: #797129 } /* Literal.Number.Float */
html[data-theme="light"] .highlight .mh { color: #797129 } /* Literal.Number.Hex */
html[data-theme="light"] .highlight .mi { color: #797129 } /* Literal.Number.Integer */
html[data-theme="light"] .highlight .mo { color: #797129 } /* Literal.Number.Oct */
html[data-theme="light"] .highlight .sa { color: #008000 } /* Literal.String.Affix */
html[data-theme="light"] .highlight .sb { color: #008000 } /* Literal.String.Backtick */
html[data-theme="light"] .highlight .sc { color: #008000 } /* Literal.String.Char */
html[data-theme="light"] .highlight .dl { color: #008000 } /* Literal.String.Delimiter */
html[data-theme="light"] .highlight .sd { color: #008000 } /* Literal.String.Doc */
html[data-theme="light"] .highlight .s2 { color: #008000 } /* Literal.String.Double */
html[data-theme="light"] .highlight .se { color: #008000 } /* Literal.String.Escape */
html[data-theme="light"] .highlight .sh { color: #008000 } /* Literal.String.Heredoc */
html[data-theme="light"] .highlight .si { color: #008000 } /* Literal.String.Interpol */
html[data-theme="light"] .highlight .sx { color: #008000 } /* Literal.String.Other */
html[data-theme="light"] .highlight .sr { color: #d91e18 } /* Literal.String.Regex */
html[data-theme="light"] .highlight .s1 { color: #008000 } /* Literal.String.Single */
html[data-theme="light"] .highlight .ss { color: #007faa } /* Literal.String.Symbol */
html[data-theme="light"] .highlight .bp { color: #797129 } /* Name.Builtin.Pseudo */
html[data-theme="light"] .highlight .fm { color: #007faa } /* Name.Function.Magic */
html[data-theme="light"] .highlight .vc { color: #d91e18 } /* Name.Variable.Class */
html[data-theme="light"] .highlight .vg { color: #d91e18 } /* Name.Variable.Global */
html[data-theme="light"] .highlight .vi { color: #d91e18 } /* Name.Variable.Instance */
html[data-theme="light"] .highlight .vm { color: #797129 } /* Name.Variable.Magic */
html[data-theme="light"] .highlight .il { color: #797129 } /* Literal.Number.Integer.Long */
html[data-theme="dark"] .highlight pre { line-height: 125%; }
html[data-theme="dark"] .highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
html[data-theme="dark"] .highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
html[data-theme="dark"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
html[data-theme="dark"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
html[data-theme="dark"] .highlight .hll { background-color: #ffd9002e }
html[data-theme="dark"] .highlight { background: #2b2b2b; color: #f8f8f2 }
html[data-theme="dark"] .highlight .c { color: #ffd900 } /* Comment */
html[data-theme="dark"] .highlight .err { color: #ffa07a } /* Error */
html[data-theme="dark"] .highlight .k { color: #dcc6e0 } /* Keyword */
html[data-theme="dark"] .highlight .l { color: #ffd900 } /* Literal */
html[data-theme="dark"] .highlight .n { color: #f8f8f2 } /* Name */
html[data-theme="dark"] .highlight .o { color: #abe338 } /* Operator */
html[data-theme="dark"] .highlight .p { color: #f8f8f2 } /* Punctuation */
html[data-theme="dark"] .highlight .ch { color: #ffd900 } /* Comment.Hashbang */
html[data-theme="dark"] .highlight .cm { color: #ffd900 } /* Comment.Multiline */
html[data-theme="dark"] .highlight .cp { color: #ffd900 } /* Comment.Preproc */
html[data-theme="dark"] .highlight .cpf { color: #ffd900 } /* Comment.PreprocFile */
html[data-theme="dark"] .highlight .c1 { color: #ffd900 } /* Comment.Single */
html[data-theme="dark"] .highlight .cs { color: #ffd900 } /* Comment.Special */
html[data-theme="dark"] .highlight .gd { color: #00e0e0 } /* Generic.Deleted */
html[data-theme="dark"] .highlight .ge { font-style: italic } /* Generic.Emph */
html[data-theme="dark"] .highlight .gh { color: #00e0e0 } /* Generic.Heading */
html[data-theme="dark"] .highlight .gs { font-weight: bold } /* Generic.Strong */
html[data-theme="dark"] .highlight .gu { color: #00e0e0 } /* Generic.Subheading */
html[data-theme="dark"] .highlight .kc { color: #dcc6e0 } /* Keyword.Constant */
html[data-theme="dark"] .highlight .kd { color: #dcc6e0 } /* Keyword.Declaration */
html[data-theme="dark"] .highlight .kn { color: #dcc6e0 } /* Keyword.Namespace */
html[data-theme="dark"] .highlight .kp { color: #dcc6e0 } /* Keyword.Pseudo */
html[data-theme="dark"] .highlight .kr { color: #dcc6e0 } /* Keyword.Reserved */
html[data-theme="dark"] .highlight .kt { color: #ffd900 } /* Keyword.Type */
html[data-theme="dark"] .highlight .ld { color: #ffd900 } /* Literal.Date */
html[data-theme="dark"] .highlight .m { color: #ffd900 } /* Literal.Number */
html[data-theme="dark"] .highlight .s { color: #abe338 } /* Literal.String */
html[data-theme="dark"] .highlight .na { color: #ffd900 } /* Name.Attribute */
html[data-theme="dark"] .highlight .nb { color: #ffd900 } /* Name.Builtin */
html[data-theme="dark"] .highlight .nc { color: #00e0e0 } /* Name.Class */
html[data-theme="dark"] .highlight .no { color: #00e0e0 } /* Name.Constant */
html[data-theme="dark"] .highlight .nd { color: #ffd900 } /* Name.Decorator */
html[data-theme="dark"] .highlight .ni { color: #abe338 } /* Name.Entity */
html[data-theme="dark"] .highlight .ne { color: #dcc6e0 } /* Name.Exception */
html[data-theme="dark"] .highlight .nf { color: #00e0e0 } /* Name.Function */
html[data-theme="dark"] .highlight .nl { color: #ffd900 } /* Name.Label */
html[data-theme="dark"] .highlight .nn { color: #f8f8f2 } /* Name.Namespace */
html[data-theme="dark"] .highlight .nx { color: #f8f8f2 } /* Name.Other */
html[data-theme="dark"] .highlight .py { color: #00e0e0 } /* Name.Property */
html[data-theme="dark"] .highlight .nt { color: #00e0e0 } /* Name.Tag */
html[data-theme="dark"] .highlight .nv { color: #ffa07a } /* Name.Variable */
html[data-theme="dark"] .highlight .ow { color: #dcc6e0 } /* Operator.Word */
html[data-theme="dark"] .highlight .pm { color: #f8f8f2 } /* Punctuation.Marker */
html[data-theme="dark"] .highlight .w { color: #f8f8f2 } /* Text.Whitespace */
html[data-theme="dark"] .highlight .mb { color: #ffd900 } /* Literal.Number.Bin */
html[data-theme="dark"] .highlight .mf { color: #ffd900 } /* Literal.Number.Float */
html[data-theme="dark"] .highlight .mh { color: #ffd900 } /* Literal.Number.Hex */
html[data-theme="dark"] .highlight .mi { color: #ffd900 } /* Literal.Number.Integer */
html[data-theme="dark"] .highlight .mo { color: #ffd900 } /* Literal.Number.Oct */
html[data-theme="dark"] .highlight .sa { color: #abe338 } /* Literal.String.Affix */
html[data-theme="dark"] .highlight .sb { color: #abe338 } /* Literal.String.Backtick */
html[data-theme="dark"] .highlight .sc { color: #abe338 } /* Literal.String.Char */
html[data-theme="dark"] .highlight .dl { color: #abe338 } /* Literal.String.Delimiter */
html[data-theme="dark"] .highlight .sd { color: #abe338 } /* Literal.String.Doc */
html[data-theme="dark"] .highlight .s2 { color: #abe338 } /* Literal.String.Double */
html[data-theme="dark"] .highlight .se { color: #abe338 } /* Literal.String.Escape */
html[data-theme="dark"] .highlight .sh { color: #abe338 } /* Literal.String.Heredoc */
html[data-theme="dark"] .highlight .si { color: #abe338 } /* Literal.String.Interpol */
html[data-theme="dark"] .highlight .sx { color: #abe338 } /* Literal.String.Other */
html[data-theme="dark"] .highlight .sr { color: #ffa07a } /* Literal.String.Regex */
html[data-theme="dark"] .highlight .s1 { color: #abe338 } /* Literal.String.Single */
html[data-theme="dark"] .highlight .ss { color: #00e0e0 } /* Literal.String.Symbol */
html[data-theme="dark"] .highlight .bp { color: #ffd900 } /* Name.Builtin.Pseudo */
html[data-theme="dark"] .highlight .fm { color: #00e0e0 } /* Name.Function.Magic */
html[data-theme="dark"] .highlight .vc { color: #ffa07a } /* Name.Variable.Class */
html[data-theme="dark"] .highlight .vg { color: #ffa07a } /* Name.Variable.Global */
html[data-theme="dark"] .highlight .vi { color: #ffa07a } /* Name.Variable.Instance */
html[data-theme="dark"] .highlight .vm { color: #ffd900 } /* Name.Variable.Magic */
html[data-theme="dark"] .highlight .il { color: #ffd900 } /* Literal.Number.Integer.Long */

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
/*!
* Bootstrap v5.3.2 (https://getbootstrap.com/)
* Copyright 2011-2023 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

619
docs/build/html/_static/searchtools.js vendored Normal file
View File

@ -0,0 +1,619 @@
/*
* searchtools.js
* ~~~~~~~~~~~~~~~~
*
* Sphinx JavaScript utilities for the full-text search.
*
* :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
"use strict";
/**
* Simple result scoring code.
*/
if (typeof Scorer === "undefined") {
var Scorer = {
// Implement the following function to further tweak the score for each result
// The function takes a result array [docname, title, anchor, descr, score, filename]
// and returns the new score.
/*
score: result => {
const [docname, title, anchor, descr, score, filename] = result
return score
},
*/
// query matches the full name of an object
objNameMatch: 11,
// or matches in the last dotted part of the object name
objPartialMatch: 6,
// Additive scores depending on the priority of the object
objPrio: {
0: 15, // used to be importantResults
1: 5, // used to be objectResults
2: -5, // used to be unimportantResults
},
// Used when the priority is not in the mapping.
objPrioDefault: 0,
// query found in title
title: 15,
partialTitle: 7,
// query found in terms
term: 5,
partialTerm: 2,
};
}
const _removeChildren = (element) => {
while (element && element.lastChild) element.removeChild(element.lastChild);
};
/**
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
*/
const _escapeRegExp = (string) =>
string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
const _displayItem = (item, searchTerms, highlightTerms) => {
const docBuilder = DOCUMENTATION_OPTIONS.BUILDER;
const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX;
const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX;
const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY;
const contentRoot = document.documentElement.dataset.content_root;
const [docName, title, anchor, descr, score, _filename] = item;
let listItem = document.createElement("li");
let requestUrl;
let linkUrl;
if (docBuilder === "dirhtml") {
// dirhtml builder
let dirname = docName + "/";
if (dirname.match(/\/index\/$/))
dirname = dirname.substring(0, dirname.length - 6);
else if (dirname === "index/") dirname = "";
requestUrl = contentRoot + dirname;
linkUrl = requestUrl;
} else {
// normal html builders
requestUrl = contentRoot + docName + docFileSuffix;
linkUrl = docName + docLinkSuffix;
}
let linkEl = listItem.appendChild(document.createElement("a"));
linkEl.href = linkUrl + anchor;
linkEl.dataset.score = score;
linkEl.innerHTML = title;
if (descr) {
listItem.appendChild(document.createElement("span")).innerHTML =
" (" + descr + ")";
// highlight search terms in the description
if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js
highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted"));
}
else if (showSearchSummary)
fetch(requestUrl)
.then((responseData) => responseData.text())
.then((data) => {
if (data)
listItem.appendChild(
Search.makeSearchSummary(data, searchTerms, anchor)
);
// highlight search terms in the summary
if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js
highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted"));
});
Search.output.appendChild(listItem);
};
const _finishSearch = (resultCount) => {
Search.stopPulse();
Search.title.innerText = _("Search Results");
if (!resultCount)
Search.status.innerText = Documentation.gettext(
"Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories."
);
else
Search.status.innerText = _(
"Search finished, found ${resultCount} page(s) matching the search query."
).replace('${resultCount}', resultCount);
};
const _displayNextItem = (
results,
resultCount,
searchTerms,
highlightTerms,
) => {
// results left, load the summary and display it
// this is intended to be dynamic (don't sub resultsCount)
if (results.length) {
_displayItem(results.pop(), searchTerms, highlightTerms);
setTimeout(
() => _displayNextItem(results, resultCount, searchTerms, highlightTerms),
5
);
}
// search finished, update title and status message
else _finishSearch(resultCount);
};
// Helper function used by query() to order search results.
// Each input is an array of [docname, title, anchor, descr, score, filename].
// Order the results by score (in opposite order of appearance, since the
// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically.
const _orderResultsByScoreThenName = (a, b) => {
const leftScore = a[4];
const rightScore = b[4];
if (leftScore === rightScore) {
// same score: sort alphabetically
const leftTitle = a[1].toLowerCase();
const rightTitle = b[1].toLowerCase();
if (leftTitle === rightTitle) return 0;
return leftTitle > rightTitle ? -1 : 1; // inverted is intentional
}
return leftScore > rightScore ? 1 : -1;
};
/**
* Default splitQuery function. Can be overridden in ``sphinx.search`` with a
* custom function per language.
*
* The regular expression works by splitting the string on consecutive characters
* that are not Unicode letters, numbers, underscores, or emoji characters.
* This is the same as ``\W+`` in Python, preserving the surrogate pair area.
*/
if (typeof splitQuery === "undefined") {
var splitQuery = (query) => query
.split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu)
.filter(term => term) // remove remaining empty strings
}
/**
* Search Module
*/
const Search = {
_index: null,
_queued_query: null,
_pulse_status: -1,
htmlToText: (htmlString, anchor) => {
const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html');
for (const removalQuery of [".headerlinks", "script", "style"]) {
htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() });
}
if (anchor) {
const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`);
if (anchorContent) return anchorContent.textContent;
console.warn(
`Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.`
);
}
// if anchor not specified or not found, fall back to main content
const docContent = htmlElement.querySelector('[role="main"]');
if (docContent) return docContent.textContent;
console.warn(
"Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template."
);
return "";
},
init: () => {
const query = new URLSearchParams(window.location.search).get("q");
document
.querySelectorAll('input[name="q"]')
.forEach((el) => (el.value = query));
if (query) Search.performSearch(query);
},
loadIndex: (url) =>
(document.body.appendChild(document.createElement("script")).src = url),
setIndex: (index) => {
Search._index = index;
if (Search._queued_query !== null) {
const query = Search._queued_query;
Search._queued_query = null;
Search.query(query);
}
},
hasIndex: () => Search._index !== null,
deferQuery: (query) => (Search._queued_query = query),
stopPulse: () => (Search._pulse_status = -1),
startPulse: () => {
if (Search._pulse_status >= 0) return;
const pulse = () => {
Search._pulse_status = (Search._pulse_status + 1) % 4;
Search.dots.innerText = ".".repeat(Search._pulse_status);
if (Search._pulse_status >= 0) window.setTimeout(pulse, 500);
};
pulse();
},
/**
* perform a search for something (or wait until index is loaded)
*/
performSearch: (query) => {
// create the required interface elements
const searchText = document.createElement("h2");
searchText.textContent = _("Searching");
const searchSummary = document.createElement("p");
searchSummary.classList.add("search-summary");
searchSummary.innerText = "";
const searchList = document.createElement("ul");
searchList.classList.add("search");
const out = document.getElementById("search-results");
Search.title = out.appendChild(searchText);
Search.dots = Search.title.appendChild(document.createElement("span"));
Search.status = out.appendChild(searchSummary);
Search.output = out.appendChild(searchList);
const searchProgress = document.getElementById("search-progress");
// Some themes don't use the search progress node
if (searchProgress) {
searchProgress.innerText = _("Preparing search...");
}
Search.startPulse();
// index already loaded, the browser was quick!
if (Search.hasIndex()) Search.query(query);
else Search.deferQuery(query);
},
_parseQuery: (query) => {
// stem the search terms and add them to the correct list
const stemmer = new Stemmer();
const searchTerms = new Set();
const excludedTerms = new Set();
const highlightTerms = new Set();
const objectTerms = new Set(splitQuery(query.toLowerCase().trim()));
splitQuery(query.trim()).forEach((queryTerm) => {
const queryTermLower = queryTerm.toLowerCase();
// maybe skip this "word"
// stopwords array is from language_data.js
if (
stopwords.indexOf(queryTermLower) !== -1 ||
queryTerm.match(/^\d+$/)
)
return;
// stem the word
let word = stemmer.stemWord(queryTermLower);
// select the correct list
if (word[0] === "-") excludedTerms.add(word.substr(1));
else {
searchTerms.add(word);
highlightTerms.add(queryTermLower);
}
});
if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js
localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" "))
}
// console.debug("SEARCH: searching for:");
// console.info("required: ", [...searchTerms]);
// console.info("excluded: ", [...excludedTerms]);
return [query, searchTerms, excludedTerms, highlightTerms, objectTerms];
},
/**
* execute search (requires search index to be loaded)
*/
_performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => {
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const titles = Search._index.titles;
const allTitles = Search._index.alltitles;
const indexEntries = Search._index.indexentries;
// Collect multiple result groups to be sorted separately and then ordered.
// Each is an array of [docname, title, anchor, descr, score, filename].
const normalResults = [];
const nonMainIndexResults = [];
_removeChildren(document.getElementById("search-progress"));
const queryLower = query.toLowerCase().trim();
for (const [title, foundTitles] of Object.entries(allTitles)) {
if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) {
for (const [file, id] of foundTitles) {
let score = Math.round(100 * queryLower.length / title.length)
normalResults.push([
docNames[file],
titles[file] !== title ? `${titles[file]} > ${title}` : title,
id !== null ? "#" + id : "",
null,
score,
filenames[file],
]);
}
}
}
// search for explicit entries in index directives
for (const [entry, foundEntries] of Object.entries(indexEntries)) {
if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) {
for (const [file, id, isMain] of foundEntries) {
const score = Math.round(100 * queryLower.length / entry.length);
const result = [
docNames[file],
titles[file],
id ? "#" + id : "",
null,
score,
filenames[file],
];
if (isMain) {
normalResults.push(result);
} else {
nonMainIndexResults.push(result);
}
}
}
}
// lookup as object
objectTerms.forEach((term) =>
normalResults.push(...Search.performObjectSearch(term, objectTerms))
);
// lookup as search terms in fulltext
normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms));
// let the scorer override scores with a custom scoring function
if (Scorer.score) {
normalResults.forEach((item) => (item[4] = Scorer.score(item)));
nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item)));
}
// Sort each group of results by score and then alphabetically by name.
normalResults.sort(_orderResultsByScoreThenName);
nonMainIndexResults.sort(_orderResultsByScoreThenName);
// Combine the result groups in (reverse) order.
// Non-main index entries are typically arbitrary cross-references,
// so display them after other results.
let results = [...nonMainIndexResults, ...normalResults];
// remove duplicate search results
// note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept
let seen = new Set();
results = results.reverse().reduce((acc, result) => {
let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(',');
if (!seen.has(resultStr)) {
acc.push(result);
seen.add(resultStr);
}
return acc;
}, []);
return results.reverse();
},
query: (query) => {
const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query);
const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms);
// for debugging
//Search.lastresults = results.slice(); // a copy
// console.info("search results:", Search.lastresults);
// print the results
_displayNextItem(results, results.length, searchTerms, highlightTerms);
},
/**
* search for object names
*/
performObjectSearch: (object, objectTerms) => {
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const objects = Search._index.objects;
const objNames = Search._index.objnames;
const titles = Search._index.titles;
const results = [];
const objectSearchCallback = (prefix, match) => {
const name = match[4]
const fullname = (prefix ? prefix + "." : "") + name;
const fullnameLower = fullname.toLowerCase();
if (fullnameLower.indexOf(object) < 0) return;
let score = 0;
const parts = fullnameLower.split(".");
// check for different match types: exact matches of full name or
// "last name" (i.e. last dotted part)
if (fullnameLower === object || parts.slice(-1)[0] === object)
score += Scorer.objNameMatch;
else if (parts.slice(-1)[0].indexOf(object) > -1)
score += Scorer.objPartialMatch; // matches in last name
const objName = objNames[match[1]][2];
const title = titles[match[0]];
// If more than one term searched for, we require other words to be
// found in the name/title/description
const otherTerms = new Set(objectTerms);
otherTerms.delete(object);
if (otherTerms.size > 0) {
const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase();
if (
[...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0)
)
return;
}
let anchor = match[3];
if (anchor === "") anchor = fullname;
else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname;
const descr = objName + _(", in ") + title;
// add custom score for some objects according to scorer
if (Scorer.objPrio.hasOwnProperty(match[2]))
score += Scorer.objPrio[match[2]];
else score += Scorer.objPrioDefault;
results.push([
docNames[match[0]],
fullname,
"#" + anchor,
descr,
score,
filenames[match[0]],
]);
};
Object.keys(objects).forEach((prefix) =>
objects[prefix].forEach((array) =>
objectSearchCallback(prefix, array)
)
);
return results;
},
/**
* search for full-text terms in the index
*/
performTermsSearch: (searchTerms, excludedTerms) => {
// prepare search
const terms = Search._index.terms;
const titleTerms = Search._index.titleterms;
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const titles = Search._index.titles;
const scoreMap = new Map();
const fileMap = new Map();
// perform the search on the required terms
searchTerms.forEach((word) => {
const files = [];
const arr = [
{ files: terms[word], score: Scorer.term },
{ files: titleTerms[word], score: Scorer.title },
];
// add support for partial matches
if (word.length > 2) {
const escapedWord = _escapeRegExp(word);
if (!terms.hasOwnProperty(word)) {
Object.keys(terms).forEach((term) => {
if (term.match(escapedWord))
arr.push({ files: terms[term], score: Scorer.partialTerm });
});
}
if (!titleTerms.hasOwnProperty(word)) {
Object.keys(titleTerms).forEach((term) => {
if (term.match(escapedWord))
arr.push({ files: titleTerms[term], score: Scorer.partialTitle });
});
}
}
// no match but word was a required one
if (arr.every((record) => record.files === undefined)) return;
// found search word in contents
arr.forEach((record) => {
if (record.files === undefined) return;
let recordFiles = record.files;
if (recordFiles.length === undefined) recordFiles = [recordFiles];
files.push(...recordFiles);
// set score for the word in each file
recordFiles.forEach((file) => {
if (!scoreMap.has(file)) scoreMap.set(file, {});
scoreMap.get(file)[word] = record.score;
});
});
// create the mapping
files.forEach((file) => {
if (!fileMap.has(file)) fileMap.set(file, [word]);
else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(word);
});
});
// now check if the files don't contain excluded terms
const results = [];
for (const [file, wordList] of fileMap) {
// check if all requirements are matched
// as search terms with length < 3 are discarded
const filteredTermCount = [...searchTerms].filter(
(term) => term.length > 2
).length;
if (
wordList.length !== searchTerms.size &&
wordList.length !== filteredTermCount
)
continue;
// ensure that none of the excluded terms is in the search result
if (
[...excludedTerms].some(
(term) =>
terms[term] === file ||
titleTerms[term] === file ||
(terms[term] || []).includes(file) ||
(titleTerms[term] || []).includes(file)
)
)
break;
// select one (max) score for the file.
const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w]));
// add result to the result list
results.push([
docNames[file],
titles[file],
"",
null,
score,
filenames[file],
]);
}
return results;
},
/**
* helper function to return a node containing the
* search summary for a given text. keywords is a list
* of stemmed words.
*/
makeSearchSummary: (htmlText, keywords, anchor) => {
const text = Search.htmlToText(htmlText, anchor);
if (text === "") return null;
const textLower = text.toLowerCase();
const actualStartPosition = [...keywords]
.map((k) => textLower.indexOf(k.toLowerCase()))
.filter((i) => i > -1)
.slice(-1)[0];
const startWithContext = Math.max(actualStartPosition - 120, 0);
const top = startWithContext === 0 ? "" : "...";
const tail = startWithContext + 240 < text.length ? "..." : "";
let summary = document.createElement("p");
summary.classList.add("context");
summary.textContent = top + text.substr(startWithContext, 240).trim() + tail;
return summary;
},
};
/* Search initialization removed for Read the Docs */

View File

@ -0,0 +1,154 @@
/* 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)));
const rest = document.createTextNode(val.substr(pos + text.length));
parent.insertBefore(
span,
parent.insertBefore(
rest,
node.nextSibling
)
);
node.nodeValue = val.substr(0, pos);
/* There may be more occurrences of search term in this node. So call this
* function recursively on the remaining fragment.
*/
_highlight(rest, addItems, text, className);
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(
'<p class="highlight-link">' +
'<a href="javascript:SphinxHighlight.hideSearchWords()">' +
_("Hide Search Matches") +
"</a></p>"
)
);
},
/**
* 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(() => {
/* Do not call highlightSearchWords() when we are on the search page.
* It will highlight words from the *previous* search query.
*/
if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords();
SphinxHighlight.initEscapeListener();
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
/* Provided by Sphinx's 'basic' theme, and included in the final set of assets */
@import "../basic.css";

View File

@ -0,0 +1,165 @@
Fonticons, Inc. (https://fontawesome.com)
--------------------------------------------------------------------------------
Font Awesome Free License
Font Awesome Free is free, open source, and GPL friendly. You can use it for
commercial projects, open source projects, or really almost whatever you want.
Full Font Awesome Free license: https://fontawesome.com/license/free.
--------------------------------------------------------------------------------
# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/)
The Font Awesome Free download is licensed under a Creative Commons
Attribution 4.0 International License and applies to all icons packaged
as SVG and JS file types.
--------------------------------------------------------------------------------
# Fonts: SIL OFL 1.1 License
In the Font Awesome Free download, the SIL OFL license applies to all icons
packaged as web and desktop font files.
Copyright (c) 2023 Fonticons, Inc. (https://fontawesome.com)
with Reserved Font Name: "Font Awesome".
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
SIL OPEN FONT LICENSE
Version 1.1 - 26 February 2007
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting — in part or in whole — any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
--------------------------------------------------------------------------------
# Code: MIT License (https://opensource.org/licenses/MIT)
In the Font Awesome Free download, the MIT license applies to all non-font and
non-icon files.
Copyright 2023 Fonticons, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in the
Software without restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
# Attribution
Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font
Awesome Free files already contain embedded comments with sufficient
attribution, so you shouldn't need to do anything additional when using these
files normally.
We've kept attribution comments terse, so we ask that you do not actively work
to remove them from files, especially code. They're a great way for folks to
learn about Font Awesome.
--------------------------------------------------------------------------------
# Brand Icons
All brand icons are trademarks of their respective owners. The use of these
trademarks does not indicate endorsement of the trademark holder by Font
Awesome, nor vice versa. **Please do not use brand logos for any purpose except
to represent the company, product, or service to which they refer.**

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
/*!
* Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2023 Fonticons, Inc.
*/

Some files were not shown because too many files have changed in this diff Show More