senfd 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- senfd/__init__.py +1 -0
- senfd/cli.py +81 -0
- senfd/documents.py +228 -0
- senfd/figures.py +179 -0
- senfd/schema/__init__.py +1 -0
- senfd/schema/checker.py +21 -0
- senfd/schema/enriched.figures.schema.json +110 -0
- senfd/schema/figures.schema.json +123 -0
- senfd/tables.py +16 -0
- senfd/templates/__init__.py +0 -0
- senfd/utils.py +12 -0
- senfd-0.1.0.dist-info/LICENSE +504 -0
- senfd-0.1.0.dist-info/METADATA +37 -0
- senfd-0.1.0.dist-info/RECORD +17 -0
- senfd-0.1.0.dist-info/WHEEL +5 -0
- senfd-0.1.0.dist-info/entry_points.txt +2 -0
- senfd-0.1.0.dist-info/top_level.txt +1 -0
senfd/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.4.1"
|
senfd/cli.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command-Line Interface
|
|
3
|
+
======================
|
|
4
|
+
|
|
5
|
+
Produces organized and semantically enriched ``.json`` documents from
|
|
6
|
+
"figure-documents".
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from argparse import ArgumentParser, Namespace
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
import senfd
|
|
13
|
+
import senfd.schema
|
|
14
|
+
from senfd.documents import CategorizedFigureDocument
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def parse_args() -> Namespace:
|
|
18
|
+
"""Return command-line arguments"""
|
|
19
|
+
|
|
20
|
+
parser = ArgumentParser(description="Semantically organize and enrich figures")
|
|
21
|
+
parser.add_argument(
|
|
22
|
+
"document", nargs="*", type=Path, help="path to one or more .json document(s)"
|
|
23
|
+
)
|
|
24
|
+
parser.add_argument(
|
|
25
|
+
"--output",
|
|
26
|
+
type=Path,
|
|
27
|
+
help="directory where the output will be saved",
|
|
28
|
+
default=Path("output"),
|
|
29
|
+
)
|
|
30
|
+
parser.add_argument(
|
|
31
|
+
"--skip-validate",
|
|
32
|
+
action="store_true",
|
|
33
|
+
help="skip post-parse validation",
|
|
34
|
+
)
|
|
35
|
+
parser.add_argument(
|
|
36
|
+
"--dump-schema",
|
|
37
|
+
action="store_true",
|
|
38
|
+
help=f"dump schema({senfd.schema.FILENAME}) and exit",
|
|
39
|
+
)
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
"--version",
|
|
42
|
+
action="store_true",
|
|
43
|
+
help="print the version and exit",
|
|
44
|
+
)
|
|
45
|
+
args = parser.parse_args()
|
|
46
|
+
if not args.document and not args.dump_schema and not args.version:
|
|
47
|
+
parser.error("the following arguments are required: document")
|
|
48
|
+
|
|
49
|
+
return args
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def main() -> int:
|
|
53
|
+
"""Command-line entrypoint"""
|
|
54
|
+
|
|
55
|
+
args = parse_args()
|
|
56
|
+
args.output.mkdir(parents=True, exist_ok=True)
|
|
57
|
+
|
|
58
|
+
if args.version:
|
|
59
|
+
print(senfd.__version__)
|
|
60
|
+
return 0
|
|
61
|
+
|
|
62
|
+
if args.dump_schema:
|
|
63
|
+
CategorizedFigureDocument.to_schema_file(
|
|
64
|
+
args.output / CategorizedFigureDocument.FILENAME_SCHEMA
|
|
65
|
+
)
|
|
66
|
+
return 0
|
|
67
|
+
|
|
68
|
+
for path in args.document:
|
|
69
|
+
document = CategorizedFigureDocument.from_figure_document_file(path)
|
|
70
|
+
document.to_json_file(
|
|
71
|
+
args.output
|
|
72
|
+
/ Path(path.name).with_suffix(CategorizedFigureDocument.SUFFIX_JSON).name
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if args.skip_validate:
|
|
76
|
+
continue
|
|
77
|
+
|
|
78
|
+
if not document.is_valid():
|
|
79
|
+
print("Document is invalid, see above for details.")
|
|
80
|
+
|
|
81
|
+
return 0
|
senfd/documents.py
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Documents
|
|
3
|
+
=========
|
|
4
|
+
|
|
5
|
+
The following classes model documents at different stages of parsing / processing an
|
|
6
|
+
NVMe specification document from a raw extract into something semantically rich.
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import inspect
|
|
11
|
+
import json
|
|
12
|
+
import re
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import ClassVar, Dict, List, Optional
|
|
15
|
+
|
|
16
|
+
from jinja2 import Environment, PackageLoader, select_autoescape
|
|
17
|
+
from pydantic import BaseModel, Field, ValidationError
|
|
18
|
+
|
|
19
|
+
import senfd.figures
|
|
20
|
+
import senfd.tables
|
|
21
|
+
|
|
22
|
+
TRANSLATION_TABLE: Dict[int, str] = str.maketrans(
|
|
23
|
+
{
|
|
24
|
+
"–": "-", # en dash
|
|
25
|
+
"—": "-", # em dash
|
|
26
|
+
"‘": "'", # left single quotation mark
|
|
27
|
+
"’": "'", # right single quotation mark
|
|
28
|
+
"“": '"', # left double quotation mark
|
|
29
|
+
"”": '"', # right double quotation mark
|
|
30
|
+
"…": "...", # ellipsis
|
|
31
|
+
"é": "e", # e with acute accent
|
|
32
|
+
"á": "a", # a with acute accent
|
|
33
|
+
"í": "i", # i with acute accent
|
|
34
|
+
"ó": "o", # o with acute accent
|
|
35
|
+
"ú": "u", # u with acute accent
|
|
36
|
+
"ü": "u", # u with diaeresis
|
|
37
|
+
"ñ": "n", # n with tilde
|
|
38
|
+
"ç": "c", # c with cedilla
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class DocumentMeta(BaseModel):
|
|
44
|
+
version: str = senfd.__version__
|
|
45
|
+
stem: str = Field(default_factory=str)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Document(BaseModel):
|
|
49
|
+
"""
|
|
50
|
+
Base document - providing functionality for describing and persisting documents
|
|
51
|
+
|
|
52
|
+
The intent here is that all Documents should be representable as JSON and HTML
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
SUFFIX_JSON: ClassVar[str] = ".document.json"
|
|
56
|
+
SUFFIX_HTML: ClassVar[str] = ".document.html"
|
|
57
|
+
|
|
58
|
+
FILENAME_SCHEMA: ClassVar[str] = "document.schema.json"
|
|
59
|
+
FILENAME_HTML_TEMPLATE: ClassVar[str] = "document.html.jinja2"
|
|
60
|
+
|
|
61
|
+
meta: DocumentMeta = Field(default_factory=DocumentMeta)
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def to_schema_file(cls, path: Optional[Path] = None):
|
|
65
|
+
"""Writes the document JSON schema to file at the given 'path'"""
|
|
66
|
+
|
|
67
|
+
if path is None:
|
|
68
|
+
path = Path.cwd() / cls.FILENAME_HTML_TEMPLATE
|
|
69
|
+
|
|
70
|
+
with path.open("w") as schema_file:
|
|
71
|
+
json.dump(cls.schema(), schema_file, indent=4)
|
|
72
|
+
|
|
73
|
+
def get_json_filename(self):
|
|
74
|
+
return f"{self.meta.stem}.{self.SUFFIX_JSON}"
|
|
75
|
+
|
|
76
|
+
def to_json(self) -> str:
|
|
77
|
+
"""Returns the document as a JSON-formated string"""
|
|
78
|
+
|
|
79
|
+
return self.model_dump_json(indent=4)
|
|
80
|
+
|
|
81
|
+
def to_json_file(self, path: Optional[Path] = None):
|
|
82
|
+
"""Writes the document, formated as JSON, to file at the given 'path'"""
|
|
83
|
+
|
|
84
|
+
if path is None:
|
|
85
|
+
path = (Path.cwd() / self.meta.stem).with_suffix(self.SUFFIX_JSON)
|
|
86
|
+
path.write_text(self.to_json())
|
|
87
|
+
|
|
88
|
+
def get_html_filename(self):
|
|
89
|
+
return f"{self.meta.stem}.{self.SUFFIX_HTML}"
|
|
90
|
+
|
|
91
|
+
def to_html(self) -> str:
|
|
92
|
+
"""Returns the document as a HTML-formatted string"""
|
|
93
|
+
|
|
94
|
+
env = Environment(
|
|
95
|
+
loader=PackageLoader("senfd", "templates"),
|
|
96
|
+
autoescape=select_autoescape(["html", "xml"]),
|
|
97
|
+
)
|
|
98
|
+
template = env.get_template(self.FILENAME_HTML_TEMPLATES)
|
|
99
|
+
|
|
100
|
+
return template.render(document=self)
|
|
101
|
+
|
|
102
|
+
def to_html_file(self, path: Optional[Path] = None):
|
|
103
|
+
"""Writes the document to HTML-formatted file"""
|
|
104
|
+
|
|
105
|
+
if path is None:
|
|
106
|
+
path = (Path.cwd() / self.meta.stem).with_suffix(self.SUFFIX_HTML)
|
|
107
|
+
path.write_text(self.to_html())
|
|
108
|
+
|
|
109
|
+
def is_valid(self):
|
|
110
|
+
"""Returns True when validator raises no exceptions, False otherwise"""
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
self.validate(self.dict())
|
|
114
|
+
except ValidationError as e:
|
|
115
|
+
print(e)
|
|
116
|
+
return False
|
|
117
|
+
|
|
118
|
+
return True
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class FigureDocument(Document):
|
|
122
|
+
|
|
123
|
+
SUFFIX_JSON: ClassVar[str] = ".figure.document.json"
|
|
124
|
+
SUFFIX_HTML: ClassVar[str] = ".figure.document.html"
|
|
125
|
+
|
|
126
|
+
FILENAME_SCHEMA: ClassVar[str] = "figure.document.schema.json"
|
|
127
|
+
FILENAME_HTML_TEMPLATE: ClassVar[str] = "figure.document.html.jinja2"
|
|
128
|
+
|
|
129
|
+
figures: List[senfd.figures.Figure] = Field(default_factory=list)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class CategorizedFigureDocumentMeta(DocumentMeta):
|
|
133
|
+
tabular: List[int] = Field(default_factory=list)
|
|
134
|
+
nontabular: List[senfd.figures.Figure] = Field(default_factory=list)
|
|
135
|
+
uncategorized: List[senfd.figures.Figure] = Field(default_factory=list)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class CategorizedFigureDocument(Document):
|
|
139
|
+
|
|
140
|
+
SUFFIX_JSON: ClassVar[str] = ".categorized.figure.document.json"
|
|
141
|
+
SUFFIX_HTML: ClassVar[str] = ".categorized.figure.document.html"
|
|
142
|
+
|
|
143
|
+
FILENAME_SCHEMA: ClassVar[str] = "categorized.figure.document.schema.json"
|
|
144
|
+
FILENAME_HTML_TEMPLATE: ClassVar[str] = "categorized.figure.document.html.jinja2"
|
|
145
|
+
|
|
146
|
+
meta: CategorizedFigureDocumentMeta = Field(
|
|
147
|
+
default_factory=CategorizedFigureDocumentMeta
|
|
148
|
+
)
|
|
149
|
+
acronyms: List[senfd.figures.Acronyms] = Field(default_factory=list)
|
|
150
|
+
io_controller_command_set_support: List[
|
|
151
|
+
senfd.figures.IoControllerCommandSetSupport
|
|
152
|
+
] = Field(default_factory=list)
|
|
153
|
+
command_set_opcodes: List[senfd.figures.CommandSetOpcodes] = Field(
|
|
154
|
+
default_factory=list
|
|
155
|
+
)
|
|
156
|
+
command_support_requirements: List[senfd.figures.CommandSupportRequirements] = (
|
|
157
|
+
Field(default_factory=list)
|
|
158
|
+
)
|
|
159
|
+
command_sqe_dword: List[senfd.figures.CommandSqeDword] = Field(default_factory=list)
|
|
160
|
+
command_sqe_dwords: List[senfd.figures.CommandSqeDwords] = Field(
|
|
161
|
+
default_factory=list
|
|
162
|
+
)
|
|
163
|
+
command_sqe_data_pointer: List[senfd.figures.CommandSqeDataPointer] = Field(
|
|
164
|
+
default_factory=list
|
|
165
|
+
)
|
|
166
|
+
command_cqe_dword: List[senfd.figures.CommandCqeDword] = Field(default_factory=list)
|
|
167
|
+
command_specific_status_values: List[senfd.figures.CommandSpecificStatusValues] = (
|
|
168
|
+
Field(default_factory=list)
|
|
169
|
+
)
|
|
170
|
+
general_command_status_values: List[senfd.figures.GeneralCommandStatusValues] = (
|
|
171
|
+
Field(default_factory=list)
|
|
172
|
+
)
|
|
173
|
+
cns_values: List[senfd.figures.CnsValues] = Field(default_factory=list)
|
|
174
|
+
feature_support: List[senfd.figures.FeatureSupport] = Field(default_factory=list)
|
|
175
|
+
feature_identifiers: List[senfd.figures.FeatureIdentifiers] = Field(
|
|
176
|
+
default_factory=list
|
|
177
|
+
)
|
|
178
|
+
log_page_identifiers: List[senfd.figures.LogPageIdentifiers] = Field(
|
|
179
|
+
default_factory=list
|
|
180
|
+
)
|
|
181
|
+
offset: List[senfd.figures.Offset] = Field(default_factory=list)
|
|
182
|
+
property_definition: List[senfd.figures.PropertyDefinition] = Field(
|
|
183
|
+
default_factory=list
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
@classmethod
|
|
187
|
+
def from_figure_document_file(cls, path: Path):
|
|
188
|
+
"""Instantiate an 'organized' Document from a 'figure' document"""
|
|
189
|
+
|
|
190
|
+
def get_figure_enriching_classes():
|
|
191
|
+
"""
|
|
192
|
+
To avoid manually crafting a list of classes, this function
|
|
193
|
+
introspectively examines this module for applicable
|
|
194
|
+
classes with "REGEX_FIGURE_DESCRIPTION" class attribute.
|
|
195
|
+
"""
|
|
196
|
+
return [
|
|
197
|
+
cls
|
|
198
|
+
for _, cls in inspect.getmembers(senfd.figures, inspect.isclass)
|
|
199
|
+
if issubclass(cls, senfd.figures.FromFigureDescriptionMatch)
|
|
200
|
+
and hasattr(cls, "REGEX_FIGURE_DESCRIPTION")
|
|
201
|
+
]
|
|
202
|
+
|
|
203
|
+
figure_document = senfd.documents.FigureDocument.parse_file(path)
|
|
204
|
+
|
|
205
|
+
document = cls()
|
|
206
|
+
|
|
207
|
+
figure_organizers = get_figure_enriching_classes()
|
|
208
|
+
for figure in figure_document.figures:
|
|
209
|
+
if not figure.table:
|
|
210
|
+
document.meta.nontabular.append(figure)
|
|
211
|
+
continue
|
|
212
|
+
|
|
213
|
+
match = None
|
|
214
|
+
description = figure.description.translate(TRANSLATION_TABLE)
|
|
215
|
+
for candidate in figure_organizers:
|
|
216
|
+
match = re.match(
|
|
217
|
+
candidate.REGEX_FIGURE_DESCRIPTION, description, flags=re.IGNORECASE
|
|
218
|
+
)
|
|
219
|
+
if match:
|
|
220
|
+
obj = candidate.from_figure_description(figure, match)
|
|
221
|
+
obj.into_document(document)
|
|
222
|
+
document.meta.tabular.append(obj.figure_nr)
|
|
223
|
+
break
|
|
224
|
+
|
|
225
|
+
if not match:
|
|
226
|
+
document.meta.uncategorized.append(figure)
|
|
227
|
+
|
|
228
|
+
return document
|
senfd/figures.py
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Figures
|
|
3
|
+
=======
|
|
4
|
+
|
|
5
|
+
The following classses model the figures found in documents.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
from typing import ClassVar, Optional
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel
|
|
12
|
+
|
|
13
|
+
import senfd.tables
|
|
14
|
+
from senfd.utils import pascal_to_snake
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Figure(BaseModel):
|
|
18
|
+
"""
|
|
19
|
+
A figure as captioned in a table of figures in the NVMe Specification Documents.
|
|
20
|
+
|
|
21
|
+
This is a minimally enriched figure representation. The intent is that via regular
|
|
22
|
+
expressions, it should be possible to construct instances when feeding it with
|
|
23
|
+
the text from a table of figures or a table that conventionally contains a figure
|
|
24
|
+
caption in the first row.
|
|
25
|
+
|
|
26
|
+
The page number is only retrievable from table of figure captions and thus optional.
|
|
27
|
+
The table data is similarly represented in a form with minimal enrichment.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
REGEX_TABLE_OF_FIGURES: ClassVar[str] = (
|
|
31
|
+
r"^(?P<caption>Figure\s+(?P<figure_nr>\d+)\s*:"
|
|
32
|
+
r"\s*(?P<description>.*?))(?P<page_nr>\d+)?$"
|
|
33
|
+
)
|
|
34
|
+
REGEX_TABLE_ROW: ClassVar[str] = (
|
|
35
|
+
r"^(?P<caption>Figure\s+(?P<figure_nr>\d+)\s*:" r"\s*(?P<description>.*?))$"
|
|
36
|
+
)
|
|
37
|
+
figure_nr: int # Figure as numbered in the specification document
|
|
38
|
+
caption: str # The entire figure caption
|
|
39
|
+
description: str # The part of figure caption without the "Fig X:" prefix
|
|
40
|
+
|
|
41
|
+
page_nr: Optional[int] = None
|
|
42
|
+
table: Optional[senfd.tables.Table] = None
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def from_regex(cls, regex, text):
|
|
46
|
+
match = re.match(regex, text)
|
|
47
|
+
if not match:
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
data = {
|
|
51
|
+
"figure_nr": int(match.group("figure_nr")),
|
|
52
|
+
"caption": match.group("caption").strip(),
|
|
53
|
+
"description": match.group("description").strip(),
|
|
54
|
+
"table": None,
|
|
55
|
+
"page_nr": None,
|
|
56
|
+
}
|
|
57
|
+
if "page_nr" in match.groupdict():
|
|
58
|
+
data["page_nr"] = int(match.group("page_nr"))
|
|
59
|
+
|
|
60
|
+
return cls(**data)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class FromFigureDescriptionMatch(Figure):
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def from_figure_description(cls, figure, match):
|
|
67
|
+
shared = set(figure.dict().keys()).intersection(set(match.groupdict().keys()))
|
|
68
|
+
if shared:
|
|
69
|
+
raise RuntimeError(f"cls({cls.__name__}) has overlap({shared})")
|
|
70
|
+
|
|
71
|
+
data = figure.dict()
|
|
72
|
+
mdict = match.groupdict()
|
|
73
|
+
if mdict:
|
|
74
|
+
data.update(mdict if mdict else {})
|
|
75
|
+
|
|
76
|
+
return cls(**data)
|
|
77
|
+
|
|
78
|
+
def into_document(self, document):
|
|
79
|
+
key = pascal_to_snake(self.__class__.__name__)
|
|
80
|
+
getattr(document, key).append(self)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class Acronyms(FromFigureDescriptionMatch):
|
|
84
|
+
REGEX_FIGURE_DESCRIPTION: ClassVar[str] = r".*Acronym\s+(definitions|Descriptions)"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class IoControllerCommandSetSupport(FromFigureDescriptionMatch):
|
|
88
|
+
REGEX_FIGURE_DESCRIPTION: ClassVar[str] = (
|
|
89
|
+
r".*-\s+(?P<command_set_name>.*)Command\s+Set\s+Support"
|
|
90
|
+
)
|
|
91
|
+
command_set_name: str
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class CnsValues(FromFigureDescriptionMatch):
|
|
95
|
+
"""Enrichment of a: 'Command Dword N' table"""
|
|
96
|
+
|
|
97
|
+
REGEX_FIGURE_DESCRIPTION: ClassVar[str] = r".*CNS\s+Values.*"
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class CommandSupportRequirements(FromFigureDescriptionMatch):
|
|
101
|
+
REGEX_FIGURE_DESCRIPTION: ClassVar[str] = (
|
|
102
|
+
r"\s*(?P<command_span>.*)\s+Command\s*Support\s*Requirements.*"
|
|
103
|
+
)
|
|
104
|
+
command_span: str
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class CommandSqeDataPointer(FromFigureDescriptionMatch):
|
|
108
|
+
REGEX_FIGURE_DESCRIPTION: ClassVar[str] = (
|
|
109
|
+
r"(?P<command_name>[\w\s]+)\s+-\s+Data\s+Pointer"
|
|
110
|
+
)
|
|
111
|
+
command_name: str
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class CommandSqeDwords(FromFigureDescriptionMatch):
|
|
115
|
+
REGEX_FIGURE_DESCRIPTION: ClassVar[str] = (
|
|
116
|
+
r"(?P<command_name>[\w\s]+)\s*-\s*Command\s*Dword\s*"
|
|
117
|
+
r"(?P<command_dword_lower>\d+)"
|
|
118
|
+
r".*and.*?\s(?P<command_dword_upper>\d+)"
|
|
119
|
+
)
|
|
120
|
+
command_name: str
|
|
121
|
+
command_dword_lower: str
|
|
122
|
+
command_dword_upper: Optional[str]
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class CommandSqeDword(FromFigureDescriptionMatch):
|
|
126
|
+
REGEX_FIGURE_DESCRIPTION: ClassVar[str] = (
|
|
127
|
+
r"(?P<command_name>[\w\s]+)\s*-\s*Command\s*Dword\s*(?P<command_dword>\d+).*?"
|
|
128
|
+
)
|
|
129
|
+
command_name: str
|
|
130
|
+
command_dword: str
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class CommandCqeDword(FromFigureDescriptionMatch):
|
|
134
|
+
REGEX_FIGURE_DESCRIPTION: ClassVar[str] = (
|
|
135
|
+
r"(?P<command_name>[\w\s]+)\s+-\s+"
|
|
136
|
+
r"Completion\sQueue\sEntry\sDword\s(?P<command_dword>\d+)"
|
|
137
|
+
)
|
|
138
|
+
command_name: str
|
|
139
|
+
command_dword: str
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class CommandSetOpcodes(FromFigureDescriptionMatch):
|
|
143
|
+
REGEX_FIGURE_DESCRIPTION: ClassVar[str] = (
|
|
144
|
+
r"Opcodes\sfor\s(?P<command_set_name>.*)\sCommands"
|
|
145
|
+
)
|
|
146
|
+
command_set_name: str
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class GeneralCommandStatusValues(FromFigureDescriptionMatch):
|
|
150
|
+
REGEX_FIGURE_DESCRIPTION: ClassVar[str] = (
|
|
151
|
+
r".*(Generic|General)\s+Command\s+Status\s+Values.*"
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class CommandSpecificStatusValues(FromFigureDescriptionMatch):
|
|
156
|
+
REGEX_FIGURE_DESCRIPTION: ClassVar[str] = (
|
|
157
|
+
r"(?P<command_name>[\w\s]+)\s+-\s+Command\s+Specific\s+Status\s+Values"
|
|
158
|
+
)
|
|
159
|
+
command_name: str
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class FeatureIdentifiers(FromFigureDescriptionMatch):
|
|
163
|
+
REGEX_FIGURE_DESCRIPTION: ClassVar[str] = r".*Feature\s*Identifiers.*"
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class FeatureSupport(FromFigureDescriptionMatch):
|
|
167
|
+
REGEX_FIGURE_DESCRIPTION: ClassVar[str] = r".*Feature\s*Support.*"
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class LogPageIdentifiers(FromFigureDescriptionMatch):
|
|
171
|
+
REGEX_FIGURE_DESCRIPTION: ClassVar[str] = r".*Log\s+Page\s+Identifiers.*"
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class Offset(FromFigureDescriptionMatch):
|
|
175
|
+
REGEX_FIGURE_DESCRIPTION: ClassVar[str] = r".*offset"
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class PropertyDefinition(FromFigureDescriptionMatch):
|
|
179
|
+
REGEX_FIGURE_DESCRIPTION: ClassVar[str] = r".*Property Definition.*"
|
senfd/schema/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
FILENAME = "enriched.figures.schema.json"
|
senfd/schema/checker.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import importlib.resources as pkg_resources
|
|
6
|
+
import json
|
|
7
|
+
from typing import Any, Dict
|
|
8
|
+
|
|
9
|
+
import senfd.schema
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_schema() -> Dict[str, Any]:
|
|
13
|
+
"""
|
|
14
|
+
Load the JSON Schema for the stage2 document from the package as a dict
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
Dict[str, Any]: The JSON Schema as a dictionary.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
with pkg_resources.open_text(senfd.schema, "enhanced.schema.json") as content:
|
|
21
|
+
return json.load(content)
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"path": {
|
|
6
|
+
"type": "string"
|
|
7
|
+
},
|
|
8
|
+
"figures": {
|
|
9
|
+
"type": "array",
|
|
10
|
+
"items": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"properties": {
|
|
13
|
+
"figure_nr": {
|
|
14
|
+
"type": "integer"
|
|
15
|
+
},
|
|
16
|
+
"page_nr": {
|
|
17
|
+
"type": "integer"
|
|
18
|
+
},
|
|
19
|
+
"title": {
|
|
20
|
+
"type": "string"
|
|
21
|
+
},
|
|
22
|
+
"description": {
|
|
23
|
+
"type": "string"
|
|
24
|
+
},
|
|
25
|
+
"table": {
|
|
26
|
+
"anyOf": [
|
|
27
|
+
{
|
|
28
|
+
"type": "null"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"type": "array",
|
|
32
|
+
"items": {
|
|
33
|
+
"type": "array",
|
|
34
|
+
"items": {
|
|
35
|
+
"type": "object",
|
|
36
|
+
"properties": {
|
|
37
|
+
"text": {
|
|
38
|
+
"type": "string"
|
|
39
|
+
},
|
|
40
|
+
"tables": {
|
|
41
|
+
"type": "array",
|
|
42
|
+
"items": {
|
|
43
|
+
"type": "array",
|
|
44
|
+
"items": {
|
|
45
|
+
"type": "array",
|
|
46
|
+
"items": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"properties": {
|
|
49
|
+
"text": {
|
|
50
|
+
"type": "string"
|
|
51
|
+
},
|
|
52
|
+
"tables": {
|
|
53
|
+
"type": "array",
|
|
54
|
+
"items": {
|
|
55
|
+
"type": "array",
|
|
56
|
+
"items": {
|
|
57
|
+
"type": "array",
|
|
58
|
+
"items": {
|
|
59
|
+
"type": "object",
|
|
60
|
+
"properties": {
|
|
61
|
+
"text": {
|
|
62
|
+
"type": "string"
|
|
63
|
+
},
|
|
64
|
+
"tables": {
|
|
65
|
+
"type": "array"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"required": [
|
|
69
|
+
"tables",
|
|
70
|
+
"text"
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"required": [
|
|
78
|
+
"tables",
|
|
79
|
+
"text"
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"required": [
|
|
87
|
+
"tables",
|
|
88
|
+
"text"
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"required": [
|
|
97
|
+
"description",
|
|
98
|
+
"figure_nr",
|
|
99
|
+
"page_nr",
|
|
100
|
+
"table",
|
|
101
|
+
"title"
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"required": [
|
|
107
|
+
"figures",
|
|
108
|
+
"path"
|
|
109
|
+
]
|
|
110
|
+
}
|