Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ NEW: Add NeedsLookUpTableBuilder #961

Open
wants to merge 53 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
bdc4d77
add builder needs look up for permalink
nhatnamnguyengtvthcm Jul 18, 2023
37deab7
keep variable for need_file
nhatnamnguyengtvthcm Jul 18, 2023
4095a0d
keep variable for need_file
nhatnamnguyengtvthcm Jul 18, 2023
b144bc3
change some env variable
nhatnamnguyengtvthcm Jul 20, 2023
0af5175
add config mode permalink
nhatnamnguyengtvthcm Jul 21, 2023
c9a10fd
add config mode permalink
nhatnamnguyengtvthcm Jul 21, 2023
db592c0
add config mode permalink
nhatnamnguyengtvthcm Jul 21, 2023
99dc2dd
add config mode permalink
nhatnamnguyengtvthcm Jul 21, 2023
9452708
fix ci test
nhatnamnguyengtvthcm Aug 10, 2023
9d6badf
update confiration for needs_lut
nhatnamnguyengtvthcm Aug 11, 2023
adfc885
update confiration for needs_lut
nhatnamnguyengtvthcm Aug 11, 2023
02e65be
add test-case and fix some setting for need per id builder
nhatnamnguyengtvthcm Aug 15, 2023
5199edc
update version
nhatnamnguyengtvthcm Aug 21, 2023
d4e4f04
Removed esbonio for IDE support
haiyangToAI Feb 17, 2023
390dd36
enable docker arm builds
cpolzer May 31, 2023
7177c03
🔧 Add .nox to gitignore
chrisjsewell Aug 2, 2023
61dcbef
Update pyproject.toml
chrisjsewell Aug 3, 2023
98030a8
Update pyproject.toml
chrisjsewell Aug 3, 2023
25d42f0
Update requirements.txt
chrisjsewell Aug 3, 2023
994b6b1
Update api.rst
chrisjsewell Aug 3, 2023
4f5d185
Added config allow unsafe filter for filter_func (#949)
haiyangToAI Aug 3, 2023
78aedd7
🔧 Update pre-commit hooks (#956)
chrisjsewell Aug 4, 2023
e86bb39
Bump actions/checkout from 3.3.0 to 3.5.3
dependabot[bot] Jun 12, 2023
3a0f270
needs: Remove some of the extra IDs in the output
tim-nordell-nimbelink Nov 21, 2022
c6f0c2d
Added config option needs_report_dead_links (#937)
haiyangToAI Aug 4, 2023
4babb2d
Raises version to 1.3.0
danwos Aug 16, 2023
4cec93b
👌 Performance: Memoize Inline Parser
chrisjsewell Aug 11, 2023
09b14ca
fix linting
chrisjsewell Aug 16, 2023
0230f3d
Add to changelog
chrisjsewell Aug 16, 2023
271f6a6
Easier Sphinx-Needs docs builds
danwos Aug 18, 2023
036c723
Docker fix
danwos Aug 18, 2023
d3e0f90
Removing docker context: ci
danwos Aug 18, 2023
e144909
♻️ Change `NeedsBuilder` format to `needs`
chrisjsewell Aug 21, 2023
d229036
Update changelog.rst
chrisjsewell Aug 21, 2023
4ead31a
add builder needs look up for permalink
nhatnamnguyengtvthcm Jul 18, 2023
c6723ea
fix ci test
nhatnamnguyengtvthcm Aug 10, 2023
d0e7849
update confiration for needs_lut
nhatnamnguyengtvthcm Aug 11, 2023
2faeec3
add test-case and fix some setting for need per id builder
nhatnamnguyengtvthcm Aug 15, 2023
3d32679
fix merge version 1.3.0
nhatnamnguyengtvthcm Aug 22, 2023
de90ff2
fix merge version 1.3.0
nhatnamnguyengtvthcm Aug 22, 2023
569d5a6
fix merge version 1.3.0
nhatnamnguyengtvthcm Aug 22, 2023
830c12a
update format builder
nhatnamnguyengtvthcm Aug 24, 2023
a93f75f
merge needsidbuilder
nhatnamnguyengtvthcm Sep 11, 2023
ff74834
turn off needs_lut_build_json
nhatnamnguyengtvthcm Sep 11, 2023
b69da59
change test case name
nhatnamnguyengtvthcm Sep 11, 2023
27bd083
Merge remote-tracking branch 'origin/master' into Namnn/needs-lookup
nhatnamnguyengtvthcm Sep 14, 2023
c77162c
refix needs look up builder, test_case
nhatnamnguyengtvthcm Sep 18, 2023
5fdcd3c
👌 Optimize needextend filter_needs usage (#1030)
danwos Sep 20, 2023
fd1f612
change logic write_lut_json and test case
nhatnamnguyengtvthcm Sep 25, 2023
6649155
git commit -m"fix conflict changelog.rst"
nhatnamnguyengtvthcm Sep 25, 2023
c3bb82c
Merge branch 'master' into Namnn/needs-lookup
nhatnamnguyengtvthcm Sep 25, 2023
0a20d81
fix-confict change log for needs_lut_builder
nhatnamnguyengtvthcm Oct 9, 2023
4472aed
refactor filter_needs in need_lut_builder
nhatnamnguyengtvthcm Oct 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions docs/builders.rst
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,53 @@ or
.. hint::

As an alternative, you can set the config option :ref:`needs_build_needumls` to export the needumls files during each build.

.. _needs_lut_builder:

needs_lut
---------
.. versionadded:: 1.4.0

The **needs_lut** builder exports all found needs to a single json file, which only include list of key ``id`` and value of ``docname`` or ``external_url``.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should mention what lut stands for. Maybe:

**needs_lut** (Needs Lookup Table)


The build creates file called **needs_lut.json** inside the given build-folder.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.. a file called

Usage
+++++

.. code-block:: bash

sphinx-build -b needs_lut source_dir build_dir

Format
++++++

.. code-block:: python

{
"extend_test_001": "directives/needextend",
"extend_test_002": "directives/needextend",

"req_arch_001": "directives/needarch",
"req_arch_004": "directives/needarch",

"spec_arch_001": "directives/needarch",
"test_arch_001": "directives/needarch",
"COMP_T_001": "directives/needarch",
"COMP_T_002": "directives/needarch",

"EX_REQ_1": "examples/index",
"EX_REQ_2": "examples/index",

"R_F4722": "examples/index",
"OWN_ID_123": "examples/index",
"IMPL_01": "examples/index",
"T_C3893": "examples/index",
"R_2A9D0": "filter",
"R_22EB2": "filter",
"S_D70B0": "filter",
"S_01A67": "filter",
"T_5CCAA": "filter",
"R_17EB4": "filter",
"req_flow_001": "directives/needflow",
"spec_flow_001": "directives/needflow",
}
1 change: 1 addition & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Released: under development
1.4.0
-----
Released: under development
* Improvement: Added Builder :ref:`needs_lut_builder` and config option :ref:`needs_lut_build_json` in `conf.py`.

* Improvement: Reduce document build time, by memoizing the inline parse in ``build_need`` (`#968 <https://github.com/useblocks/sphinx-needs/pull/968>`_)

Expand Down
3 changes: 3 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,9 @@ def custom_defined_func():
# build needs.json to make permalinks work
needs_build_json = True

# build needs_lut.json to make permalinks work
needs_lut_build_json = False

# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]

Expand Down
25 changes: 24 additions & 1 deletion docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1821,7 +1821,7 @@ needs_permalink_file
The option specifies the name of the permalink html file,
which will be copied to the html build directory during build.

The permalink web site will load a ``needs.json`` file as specified
The permalink web site will load a ``needs.json`` or ``needs_lut.json`` file as specified
by :ref:`needs_permalink_data` and re-direct the web browser to the html document
of the need, which is specified by appending the need ID as a query
parameter, e.g., ``http://localhost:8000/permalink.html?id=REQ_4711``.
Expand Down Expand Up @@ -1853,6 +1853,7 @@ an absolute path (on the web server) or an URL.

Default value: ``needs.json``

You can choose needs_lut_build_json ``needs_lut.json`` after setting :ref:`needs_lut_build_json`

.. _needs_constraints:

Expand Down Expand Up @@ -2289,3 +2290,25 @@ If true, need options like status, tags or links are collapsed and shown only af
Default value: True

Can be overwritten for each single need by setting :ref:`need_collapse`.

.. __needs_lut_build_json:

needs_lut_build_json
~~~~~~~~~~~~~~~~~~~~

.. versionadded:: 1.4.0

Builds a ``needs_lut.json`` file during other builds (like ``html``), which is different from ``needs.json``, only include list of key ``id`` and value of ``docname`` or ``external_url``.
This is helpful for loading data fastly when you need improve performance.
Default: False

Example:

.. code-block:: python

needs_lut_build_json = False

.. hint::

The created ``needs_lut.json`` file gets stored in the ``outdir`` of the current builder.
So if ``html`` is used as builder, the final location is e.g. ``_build/html/needs_lut.json``.
64 changes: 64 additions & 0 deletions sphinx_needs/builder.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
import os
from typing import Iterable, Optional, Set

Expand Down Expand Up @@ -157,3 +158,66 @@ def build_needumls_pumls(app: Sphinx, _exception: Exception) -> None:
needs_builder.set_environment(env)

needs_builder.finish()


class NeedsLookUpTableBuilder(Builder):
danwos marked this conversation as resolved.
Show resolved Hide resolved
name = "needs_lut"
format = "needs"
file_suffix = ".txt"
links_suffix = None

def write_doc(self, docname: str, doctree: nodes.document) -> None:
pass

def finish(self) -> None:
env = unwrap(self.env)
needs = env.needs_all_needs.values()
needs_dict = {}
for need in needs:
if need["is_external"]:
needs_dict[need["id"]] = need["external_url"]
else:
needs_dict[need["id"]] = need["docname"]

try:
fname = os.path.join(self.outdir, "needs_lut.json")
with open(fname, "w", encoding="utf-8") as f:
json.dump(needs_dict, f, indent=4)
except Exception as e:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add this part (dict creation + json.dump) also to NeedsList as write_json_lut?
It's easier to maintain if all storage functions are defined in one place.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should stay with add_need (instead of add_lut_need) and transform the needed data in a special writer function write_json_lut.

log.error(f"Error during writing json file: {e}")
else:
log.info("Needs lookup table json successfully created")

def get_outdated_docs(self) -> Iterable[str]:
return []

def prepare_writing(self, _docnames: Set[str]) -> None:
pass

def write_doc_serialized(self, _docname: str, _doctree: nodes.document) -> None:
pass

def cleanup(self) -> None:
pass

def get_target_uri(self, _docname: str, _typ: Optional[str] = None) -> str:
return ""


def build_needs_look_up_json(app: Sphinx, _exception: Exception) -> None:
env = unwrap(app.env)

if not env.config.needs_lut_build_json:
return

# Do not create an additional look up table json, if builder is already in use.
if isinstance(app.builder, NeedsLookUpTableBuilder):
return

try:
needs_lut_builder = NeedsLookUpTableBuilder(app, env)
except TypeError:
needs_lut_builder = NeedsLookUpTableBuilder(app)
needs_lut_builder.set_environment(env)

needs_lut_builder.finish()
12 changes: 11 additions & 1 deletion sphinx_needs/needs.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
from sphinx_needs.api.configuration import add_extra_option
from sphinx_needs.builder import (
NeedsBuilder,
NeedsLookUpTableBuilder,
NeedumlsBuilder,
build_needs_json,
build_needs_look_up_json,
build_needumls_pumls,
)
from sphinx_needs.config import NEEDS_CONFIG
Expand Down Expand Up @@ -141,6 +143,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:

app.add_builder(NeedsBuilder)
app.add_builder(NeedumlsBuilder)
app.add_builder(NeedsLookUpTableBuilder)
app.add_config_value(
"needs_types",
[
Expand Down Expand Up @@ -258,11 +261,15 @@ def setup(app: Sphinx) -> Dict[str, Any]:

app.add_config_value("needs_build_needumls", "", "html", types=[str])

app.add_config_value("needs_lut_build_json", False, "html", types=[bool])

# Permalink related config values.
# path to permalink.html; absolute path from web-root
app.add_config_value("needs_permalink_file", "permalink.html", "html")
# path to needs.json relative to permalink.html
app.add_config_value("needs_permalink_data", "needs.json", "html")
app.add_config_value("needs_permalink_data", "needs_lut.json", "html")
# add config mode permalink
app.add_config_value("needs_lut_mode", False, "html", types=[bool])
# path to needs_report_template file which is based on the conf.py directory.
app.add_config_value("needs_report_template", "", "html", types=[str])

Expand All @@ -281,6 +288,8 @@ def setup(app: Sphinx) -> Dict[str, Any]:
#
app.add_config_value("needs_debug_measurement", False, "html", types=[dict])

app.add_config_value("needs_lut_build", False, "html", types=[bool])

# Define nodes
app.add_node(Need, html=(html_visit, html_depart), latex=(latex_visit, latex_depart))
app.add_node(
Expand Down Expand Up @@ -372,6 +381,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.connect("build-finished", process_warnings)
app.connect("build-finished", build_needs_json)
app.connect("build-finished", build_needumls_pumls)
app.connect("build-finished", build_needs_look_up_json)
app.connect("build-finished", debug.process_timing)
app.connect("env-updated", install_lib_static_files)
app.connect("env-updated", install_permalink_file)
Expand Down
62 changes: 43 additions & 19 deletions sphinx_needs/templates/permalink.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,31 +23,55 @@
function main() {
loadJSON('{{ needs_file }}', function (response) {
const needs = JSON.parse(response);
const current_version = needs['current_version'];
const versions = needs['versions'];
const version = versions[current_version];
const needs_obj = version['needs'];
if(needs.hasOwnProperty("current_version")) {
const needs = JSON.parse(response);
const current_version = needs['current_version'];
const versions = needs['versions'];
const version = versions[current_version];
const needs_obj = version['needs'];

const id = getParameterByName('id');
var pathname = new URL(window.location.href).pathname;
pathname = pathname.substring(0, pathname.lastIndexOf('{{ permalink_file }}'));
const id = getParameterByName('id');
var pathname = new URL(window.location.href).pathname;
pathname = pathname.substring(0, pathname.lastIndexOf('{{ permalink_file }}'));

const keys = Object.keys(needs_obj);
const keys = Object.keys(needs_obj);

var docname = 'index';
var docname = 'index';

keys.forEach((key, index) => {
if (key === id) {
const need = needs_obj[key];
docname = need['docname'];
return;
}
});
keys.forEach((key, index) => {
if (key === id) {
const need = needs_obj[key];
docname = need['docname'];
return;
}
});

window.location.replace(pathname + docname + '.html#' + id);
}
else {
const needs = JSON.parse(response);
const id = getParameterByName('id');
var pathname = new URL(window.location.href).pathname;
pathname = pathname.substring(0, pathname.lastIndexOf('permalink.html'));
const keys = Object.keys(needs);
var docname = 'index';
keys.forEach((key, index) => {
if (key === id) {
docname = needs[key];
return;
}
});

window.location.replace(pathname + docname + '.html#' + id);
if (docname.includes("#" + id)) {
window.location.replace(pathname + docname);
} else {
window.location.replace(pathname + docname + '.html#' + id);
}
}
});
}


}
function getParameterByName(name, url = window.location.href) {
name = name.replace(/[\[\]]/g, '\\$&');
var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
Expand All @@ -65,4 +89,4 @@
<body>
</body>

</html>
</html>
19 changes: 19 additions & 0 deletions tests/test_needs_look_up.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import json
from pathlib import Path

import pytest


@pytest.mark.parametrize(
"test_app", [{"buildername": "needs_lut", "srcdir": "doc_test/doc_needs_builder"}], indirect=True
)
def test_doc_needs_id_builder(test_app):
app = test_app
app.build()

needs_json = Path(app.outdir, "needs_lut.json")
with open(needs_json) as needs_file:
needs_file_content = needs_file.read()

needs_list = json.loads(needs_file_content)
assert needs_list["TC_NEG_001"]