oscura 0.6.0__py3-none-any.whl → 0.8.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.
- oscura/__init__.py +1 -1
- oscura/analyzers/eye/__init__.py +5 -1
- oscura/analyzers/eye/generation.py +501 -0
- oscura/analyzers/jitter/__init__.py +6 -6
- oscura/analyzers/jitter/timing.py +419 -0
- oscura/analyzers/patterns/__init__.py +28 -0
- oscura/analyzers/patterns/reverse_engineering.py +991 -0
- oscura/analyzers/power/__init__.py +35 -12
- oscura/analyzers/statistics/__init__.py +4 -0
- oscura/analyzers/statistics/basic.py +149 -0
- oscura/analyzers/statistics/correlation.py +47 -6
- oscura/analyzers/waveform/__init__.py +2 -0
- oscura/analyzers/waveform/measurements.py +145 -23
- oscura/analyzers/waveform/spectral.py +361 -8
- oscura/automotive/__init__.py +1 -1
- oscura/core/config/loader.py +0 -1
- oscura/core/types.py +108 -0
- oscura/loaders/__init__.py +12 -4
- oscura/loaders/tss.py +456 -0
- oscura/reporting/__init__.py +88 -1
- oscura/reporting/automation.py +348 -0
- oscura/reporting/citations.py +374 -0
- oscura/reporting/core.py +54 -0
- oscura/reporting/formatting/__init__.py +11 -0
- oscura/reporting/formatting/measurements.py +279 -0
- oscura/reporting/html.py +57 -0
- oscura/reporting/interpretation.py +431 -0
- oscura/reporting/summary.py +329 -0
- oscura/reporting/visualization.py +542 -0
- oscura/visualization/__init__.py +2 -1
- oscura/visualization/batch.py +521 -0
- oscura/workflows/__init__.py +2 -0
- oscura/workflows/waveform.py +783 -0
- {oscura-0.6.0.dist-info → oscura-0.8.0.dist-info}/METADATA +37 -19
- {oscura-0.6.0.dist-info → oscura-0.8.0.dist-info}/RECORD +38 -26
- {oscura-0.6.0.dist-info → oscura-0.8.0.dist-info}/WHEEL +0 -0
- {oscura-0.6.0.dist-info → oscura-0.8.0.dist-info}/entry_points.txt +0 -0
- {oscura-0.6.0.dist-info → oscura-0.8.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
"""IEEE standard citation system for professional reports.
|
|
2
|
+
|
|
3
|
+
This module provides automatic citation management for IEEE standards
|
|
4
|
+
referenced in analysis reports, with proper DOI links and bibliographic
|
|
5
|
+
formatting.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
# IEEE Standards Database with DOIs
|
|
14
|
+
IEEE_STANDARDS: dict[str, dict[str, str]] = {
|
|
15
|
+
"181": {
|
|
16
|
+
"title": "IEEE Standard for Transitions, Pulses, and Related Waveforms",
|
|
17
|
+
"year": "2011",
|
|
18
|
+
"doi": "10.1109/IEEESTD.2011.6016359",
|
|
19
|
+
"url": "https://doi.org/10.1109/IEEESTD.2011.6016359",
|
|
20
|
+
"full_name": "IEEE Std 181-2011",
|
|
21
|
+
"scope": "Pulse measurement terminology, definitions, and algorithms",
|
|
22
|
+
},
|
|
23
|
+
"1241": {
|
|
24
|
+
"title": "IEEE Standard for Terminology and Test Methods for Analog-to-Digital Converters",
|
|
25
|
+
"year": "2010",
|
|
26
|
+
"doi": "10.1109/IEEESTD.2011.5692956",
|
|
27
|
+
"url": "https://doi.org/10.1109/IEEESTD.2011.5692956",
|
|
28
|
+
"full_name": "IEEE Std 1241-2010",
|
|
29
|
+
"scope": "ADC characterization including SNR, ENOB, and dynamic performance",
|
|
30
|
+
},
|
|
31
|
+
"1057": {
|
|
32
|
+
"title": "IEEE Standard for Digitizing Waveform Recorders",
|
|
33
|
+
"year": "2017",
|
|
34
|
+
"doi": "10.1109/IEEESTD.2017.8291139",
|
|
35
|
+
"url": "https://doi.org/10.1109/IEEESTD.2017.8291139",
|
|
36
|
+
"full_name": "IEEE Std 1057-2017",
|
|
37
|
+
"scope": "Oscilloscope and digitizer performance specifications",
|
|
38
|
+
},
|
|
39
|
+
"2414": {
|
|
40
|
+
"title": "IEEE Standard for Jitter and Phase Noise",
|
|
41
|
+
"year": "2020",
|
|
42
|
+
"doi": "10.1109/IEEESTD.2020.9268529",
|
|
43
|
+
"url": "https://doi.org/10.1109/IEEESTD.2020.9268529",
|
|
44
|
+
"full_name": "IEEE Std 2414-2020",
|
|
45
|
+
"scope": "Jitter measurement and analysis methodologies",
|
|
46
|
+
},
|
|
47
|
+
"1459": {
|
|
48
|
+
"title": "IEEE Standard Definitions for the Measurement of Electric Power Quantities Under Sinusoidal, Nonsinusoidal, Balanced, or Unbalanced Conditions",
|
|
49
|
+
"year": "2010",
|
|
50
|
+
"doi": "10.1109/IEEESTD.2010.5439063",
|
|
51
|
+
"url": "https://doi.org/10.1109/IEEESTD.2010.5439063",
|
|
52
|
+
"full_name": "IEEE Std 1459-2010",
|
|
53
|
+
"scope": "Power measurement definitions and algorithms",
|
|
54
|
+
},
|
|
55
|
+
"829": {
|
|
56
|
+
"title": "IEEE Standard for Software and System Test Documentation",
|
|
57
|
+
"year": "2008",
|
|
58
|
+
"doi": "10.1109/IEEESTD.2008.4578383",
|
|
59
|
+
"url": "https://doi.org/10.1109/IEEESTD.2008.4578383",
|
|
60
|
+
"full_name": "IEEE Std 829-2008",
|
|
61
|
+
"scope": "Test documentation and reporting standards",
|
|
62
|
+
},
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass
|
|
67
|
+
class Citation:
|
|
68
|
+
"""A citation to an IEEE standard.
|
|
69
|
+
|
|
70
|
+
Attributes:
|
|
71
|
+
standard_id: IEEE standard number (e.g., "181", "1241").
|
|
72
|
+
section: Specific section if applicable.
|
|
73
|
+
context: Context where citation is used.
|
|
74
|
+
page: Page number if applicable.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
standard_id: str
|
|
78
|
+
section: str | None = None
|
|
79
|
+
context: str = ""
|
|
80
|
+
page: int | None = None
|
|
81
|
+
|
|
82
|
+
def format_inline(self) -> str:
|
|
83
|
+
"""Format citation for inline use.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Formatted citation string (e.g., "[IEEE 181]" or "[IEEE 181 §3.1]").
|
|
87
|
+
|
|
88
|
+
Example:
|
|
89
|
+
>>> citation = Citation("181", section="3.1")
|
|
90
|
+
>>> citation.format_inline()
|
|
91
|
+
'[IEEE 181 §3.1]'
|
|
92
|
+
"""
|
|
93
|
+
if self.section:
|
|
94
|
+
return f"[IEEE {self.standard_id} §{self.section}]"
|
|
95
|
+
return f"[IEEE {self.standard_id}]"
|
|
96
|
+
|
|
97
|
+
def format_bibliography(self) -> str:
|
|
98
|
+
"""Format citation for bibliography/references section.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Formatted bibliography entry with DOI link.
|
|
102
|
+
|
|
103
|
+
Example:
|
|
104
|
+
>>> citation = Citation("181")
|
|
105
|
+
>>> print(citation.format_bibliography())
|
|
106
|
+
IEEE Std 181-2011, "IEEE Standard for Transitions, Pulses, and Related Waveforms," 2011. DOI: 10.1109/IEEESTD.2011.6016359
|
|
107
|
+
"""
|
|
108
|
+
if self.standard_id not in IEEE_STANDARDS:
|
|
109
|
+
return f"[IEEE {self.standard_id}] - Standard not in database"
|
|
110
|
+
|
|
111
|
+
std = IEEE_STANDARDS[self.standard_id]
|
|
112
|
+
return f'{std["full_name"]}, "{std["title"]}," {std["year"]}. DOI: {std["doi"]}'
|
|
113
|
+
|
|
114
|
+
def get_url(self) -> str:
|
|
115
|
+
"""Get DOI URL for the standard.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
DOI URL string or empty string if not found.
|
|
119
|
+
|
|
120
|
+
Example:
|
|
121
|
+
>>> citation = Citation("181")
|
|
122
|
+
>>> citation.get_url()
|
|
123
|
+
'https://doi.org/10.1109/IEEESTD.2011.6016359'
|
|
124
|
+
"""
|
|
125
|
+
if self.standard_id in IEEE_STANDARDS:
|
|
126
|
+
return IEEE_STANDARDS[self.standard_id]["url"]
|
|
127
|
+
return ""
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@dataclass
|
|
131
|
+
class CitationManager:
|
|
132
|
+
"""Manages citations in a report.
|
|
133
|
+
|
|
134
|
+
Tracks all citations used in a report and generates bibliographies.
|
|
135
|
+
Automatically deduplicates and sorts citations.
|
|
136
|
+
|
|
137
|
+
Attributes:
|
|
138
|
+
citations: List of all citations in the report.
|
|
139
|
+
|
|
140
|
+
Example:
|
|
141
|
+
>>> manager = CitationManager()
|
|
142
|
+
>>> manager.add_citation("181", section="3.1", context="Rise time measurement")
|
|
143
|
+
>>> manager.add_citation("1241", context="SNR calculation")
|
|
144
|
+
>>> html = manager.generate_bibliography_html()
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
citations: list[Citation] = field(default_factory=list)
|
|
148
|
+
|
|
149
|
+
def add_citation(
|
|
150
|
+
self,
|
|
151
|
+
standard_id: str,
|
|
152
|
+
section: str | None = None,
|
|
153
|
+
context: str = "",
|
|
154
|
+
page: int | None = None,
|
|
155
|
+
) -> Citation:
|
|
156
|
+
"""Add a citation to the report.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
standard_id: IEEE standard number (e.g., "181").
|
|
160
|
+
section: Specific section if applicable.
|
|
161
|
+
context: Context where citation is used.
|
|
162
|
+
page: Page number if applicable.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
The created Citation object.
|
|
166
|
+
|
|
167
|
+
Example:
|
|
168
|
+
>>> manager = CitationManager()
|
|
169
|
+
>>> cite = manager.add_citation("181", section="3.1")
|
|
170
|
+
>>> cite.format_inline()
|
|
171
|
+
'[IEEE 181 §3.1]'
|
|
172
|
+
"""
|
|
173
|
+
citation = Citation(
|
|
174
|
+
standard_id=standard_id,
|
|
175
|
+
section=section,
|
|
176
|
+
context=context,
|
|
177
|
+
page=page,
|
|
178
|
+
)
|
|
179
|
+
self.citations.append(citation)
|
|
180
|
+
return citation
|
|
181
|
+
|
|
182
|
+
def get_unique_citations(self) -> list[Citation]:
|
|
183
|
+
"""Get unique citations (deduplicated by standard_id).
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
List of unique citations sorted by standard ID.
|
|
187
|
+
|
|
188
|
+
Example:
|
|
189
|
+
>>> manager = CitationManager()
|
|
190
|
+
>>> manager.add_citation("181")
|
|
191
|
+
>>> manager.add_citation("181", section="3.1")
|
|
192
|
+
>>> manager.add_citation("1241")
|
|
193
|
+
>>> len(manager.get_unique_citations())
|
|
194
|
+
2
|
|
195
|
+
"""
|
|
196
|
+
seen: set[str] = set()
|
|
197
|
+
unique: list[Citation] = []
|
|
198
|
+
|
|
199
|
+
for citation in sorted(self.citations, key=lambda c: c.standard_id):
|
|
200
|
+
if citation.standard_id not in seen:
|
|
201
|
+
seen.add(citation.standard_id)
|
|
202
|
+
unique.append(citation)
|
|
203
|
+
|
|
204
|
+
return unique
|
|
205
|
+
|
|
206
|
+
def generate_bibliography_markdown(self) -> str:
|
|
207
|
+
"""Generate bibliography in Markdown format.
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
Markdown-formatted bibliography section.
|
|
211
|
+
|
|
212
|
+
Example:
|
|
213
|
+
>>> manager = CitationManager()
|
|
214
|
+
>>> manager.add_citation("181")
|
|
215
|
+
>>> md = manager.generate_bibliography_markdown()
|
|
216
|
+
>>> "IEEE 181" in md
|
|
217
|
+
True
|
|
218
|
+
"""
|
|
219
|
+
if not self.citations:
|
|
220
|
+
return ""
|
|
221
|
+
|
|
222
|
+
lines = ["## References", ""]
|
|
223
|
+
|
|
224
|
+
for citation in self.get_unique_citations():
|
|
225
|
+
lines.append(f"- {citation.format_bibliography()}")
|
|
226
|
+
|
|
227
|
+
return "\n".join(lines)
|
|
228
|
+
|
|
229
|
+
def generate_bibliography_html(self) -> str:
|
|
230
|
+
"""Generate bibliography in HTML format with DOI links.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
HTML-formatted bibliography section.
|
|
234
|
+
|
|
235
|
+
Example:
|
|
236
|
+
>>> manager = CitationManager()
|
|
237
|
+
>>> manager.add_citation("181")
|
|
238
|
+
>>> html = manager.generate_bibliography_html()
|
|
239
|
+
>>> "doi.org" in html
|
|
240
|
+
True
|
|
241
|
+
"""
|
|
242
|
+
if not self.citations:
|
|
243
|
+
return ""
|
|
244
|
+
|
|
245
|
+
lines = ['<div class="references">', "<h2>References</h2>", "<ol>"]
|
|
246
|
+
|
|
247
|
+
for citation in self.get_unique_citations():
|
|
248
|
+
url = citation.get_url()
|
|
249
|
+
bib = citation.format_bibliography()
|
|
250
|
+
|
|
251
|
+
if url:
|
|
252
|
+
lines.append(f'<li><a href="{url}" target="_blank">{bib}</a></li>')
|
|
253
|
+
else:
|
|
254
|
+
lines.append(f"<li>{bib}</li>")
|
|
255
|
+
|
|
256
|
+
lines.extend(["</ol>", "</div>"])
|
|
257
|
+
return "\n".join(lines)
|
|
258
|
+
|
|
259
|
+
def get_citation_context(self) -> dict[str, list[str]]:
|
|
260
|
+
"""Get contexts where each standard was cited.
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
Dictionary mapping standard IDs to list of contexts.
|
|
264
|
+
|
|
265
|
+
Example:
|
|
266
|
+
>>> manager = CitationManager()
|
|
267
|
+
>>> manager.add_citation("181", context="Rise time")
|
|
268
|
+
>>> manager.add_citation("181", context="Fall time")
|
|
269
|
+
>>> contexts = manager.get_citation_context()
|
|
270
|
+
>>> len(contexts["181"])
|
|
271
|
+
2
|
|
272
|
+
"""
|
|
273
|
+
context_map: dict[str, list[str]] = {}
|
|
274
|
+
|
|
275
|
+
for citation in self.citations:
|
|
276
|
+
if citation.context:
|
|
277
|
+
if citation.standard_id not in context_map:
|
|
278
|
+
context_map[citation.standard_id] = []
|
|
279
|
+
context_map[citation.standard_id].append(citation.context)
|
|
280
|
+
|
|
281
|
+
return context_map
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def get_standard_info(standard_id: str) -> dict[str, Any]:
|
|
285
|
+
"""Get information about an IEEE standard.
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
standard_id: IEEE standard number (e.g., "181").
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
Dictionary with standard metadata or empty dict if not found.
|
|
292
|
+
|
|
293
|
+
Example:
|
|
294
|
+
>>> info = get_standard_info("181")
|
|
295
|
+
>>> info["year"]
|
|
296
|
+
'2011'
|
|
297
|
+
>>> "pulse" in info["scope"].lower()
|
|
298
|
+
True
|
|
299
|
+
"""
|
|
300
|
+
return IEEE_STANDARDS.get(standard_id, {})
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def list_available_standards() -> list[str]:
|
|
304
|
+
"""List all IEEE standards in the citation database.
|
|
305
|
+
|
|
306
|
+
Returns:
|
|
307
|
+
List of standard IDs.
|
|
308
|
+
|
|
309
|
+
Example:
|
|
310
|
+
>>> standards = list_available_standards()
|
|
311
|
+
>>> "181" in standards
|
|
312
|
+
True
|
|
313
|
+
>>> len(standards) >= 5
|
|
314
|
+
True
|
|
315
|
+
"""
|
|
316
|
+
return sorted(IEEE_STANDARDS.keys())
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def auto_cite_measurement(measurement_name: str) -> str | None:
|
|
320
|
+
"""Automatically determine which IEEE standard to cite for a measurement.
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
measurement_name: Name of measurement (e.g., "rise_time", "snr").
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
Standard ID to cite, or None if no match.
|
|
327
|
+
|
|
328
|
+
Example:
|
|
329
|
+
>>> auto_cite_measurement("rise_time")
|
|
330
|
+
'181'
|
|
331
|
+
>>> auto_cite_measurement("snr")
|
|
332
|
+
'1241'
|
|
333
|
+
>>> auto_cite_measurement("jitter")
|
|
334
|
+
'2414'
|
|
335
|
+
"""
|
|
336
|
+
measurement_lower = measurement_name.lower()
|
|
337
|
+
|
|
338
|
+
# Pulse/waveform measurements -> IEEE 181
|
|
339
|
+
if any(
|
|
340
|
+
term in measurement_lower
|
|
341
|
+
for term in [
|
|
342
|
+
"rise",
|
|
343
|
+
"fall",
|
|
344
|
+
"pulse",
|
|
345
|
+
"transition",
|
|
346
|
+
"overshoot",
|
|
347
|
+
"settling",
|
|
348
|
+
"slew",
|
|
349
|
+
]
|
|
350
|
+
):
|
|
351
|
+
return "181"
|
|
352
|
+
|
|
353
|
+
# ADC/quantization measurements -> IEEE 1241
|
|
354
|
+
if any(
|
|
355
|
+
term in measurement_lower
|
|
356
|
+
for term in ["snr", "sinad", "enob", "thd", "sfdr", "quantization"]
|
|
357
|
+
):
|
|
358
|
+
return "1241"
|
|
359
|
+
|
|
360
|
+
# Jitter measurements -> IEEE 2414
|
|
361
|
+
if any(term in measurement_lower for term in ["jitter", "phase_noise", "timing"]):
|
|
362
|
+
return "2414"
|
|
363
|
+
|
|
364
|
+
# Power measurements -> IEEE 1459
|
|
365
|
+
if any(term in measurement_lower for term in ["power", "rms", "thd", "distortion", "harmonic"]):
|
|
366
|
+
return "1459"
|
|
367
|
+
|
|
368
|
+
# Oscilloscope/digitizer -> IEEE 1057
|
|
369
|
+
if any(
|
|
370
|
+
term in measurement_lower for term in ["bandwidth", "sample_rate", "resolution", "accuracy"]
|
|
371
|
+
):
|
|
372
|
+
return "1057"
|
|
373
|
+
|
|
374
|
+
return None
|
oscura/reporting/core.py
CHANGED
|
@@ -112,6 +112,60 @@ class Report:
|
|
|
112
112
|
self.sections.append(section)
|
|
113
113
|
return section
|
|
114
114
|
|
|
115
|
+
def add_measurements(
|
|
116
|
+
self,
|
|
117
|
+
title: str,
|
|
118
|
+
measurements: dict[str, float | int],
|
|
119
|
+
unit_map: dict[str, str] | None = None,
|
|
120
|
+
level: int = 2,
|
|
121
|
+
html: bool = True,
|
|
122
|
+
**kwargs: Any,
|
|
123
|
+
) -> Section:
|
|
124
|
+
"""Add a measurement section with automatic formatting.
|
|
125
|
+
|
|
126
|
+
This convenience method automatically formats measurement dictionaries
|
|
127
|
+
using the framework's measurement formatting system, including SI prefix
|
|
128
|
+
auto-scaling and proper unit handling.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
title: Section title.
|
|
132
|
+
measurements: Dictionary of measurement names to values.
|
|
133
|
+
unit_map: Optional dictionary mapping measurement names to units.
|
|
134
|
+
If not provided, uses framework metadata for common measurements.
|
|
135
|
+
level: Heading level.
|
|
136
|
+
html: Whether to format as HTML (True) or plain text (False).
|
|
137
|
+
**kwargs: Additional section options.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
The created Section.
|
|
141
|
+
|
|
142
|
+
Example:
|
|
143
|
+
>>> from oscura.reporting import Report
|
|
144
|
+
>>> report = Report()
|
|
145
|
+
>>> measurements = {"amplitude": 1.5, "frequency": 440.0, "duty_cycle": 0.5}
|
|
146
|
+
>>> unit_map = {"amplitude": "V", "frequency": "Hz", "duty_cycle": "ratio"}
|
|
147
|
+
>>> report.add_measurements("Time Domain", measurements, unit_map)
|
|
148
|
+
"""
|
|
149
|
+
from oscura.reporting.formatting import (
|
|
150
|
+
convert_to_measurement_dict,
|
|
151
|
+
format_measurement_dict,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# If no unit map provided, try to use framework metadata
|
|
155
|
+
if unit_map is None:
|
|
156
|
+
from oscura.analyzers.waveform import MEASUREMENT_METADATA
|
|
157
|
+
|
|
158
|
+
unit_map = {
|
|
159
|
+
key: MEASUREMENT_METADATA.get(key, {}).get("unit", "") for key in measurements
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
# Convert to measurement dict and format
|
|
163
|
+
meas_dict = convert_to_measurement_dict(measurements, unit_map)
|
|
164
|
+
formatted_content = format_measurement_dict(meas_dict, html=html)
|
|
165
|
+
|
|
166
|
+
# Add as section
|
|
167
|
+
return self.add_section(title, formatted_content, level, **kwargs)
|
|
168
|
+
|
|
115
169
|
def add_table(
|
|
116
170
|
self,
|
|
117
171
|
data: list[list[Any]] | NDArray[Any],
|
|
@@ -5,6 +5,12 @@ from oscura.reporting.formatting.emphasis import (
|
|
|
5
5
|
format_callout_box,
|
|
6
6
|
format_severity,
|
|
7
7
|
)
|
|
8
|
+
from oscura.reporting.formatting.measurements import (
|
|
9
|
+
MeasurementFormatter,
|
|
10
|
+
convert_to_measurement_dict,
|
|
11
|
+
format_measurement,
|
|
12
|
+
format_measurement_dict,
|
|
13
|
+
)
|
|
8
14
|
from oscura.reporting.formatting.numbers import (
|
|
9
15
|
NumberFormatter,
|
|
10
16
|
format_percentage,
|
|
@@ -109,15 +115,20 @@ __all__ = [
|
|
|
109
115
|
"ColorScheme",
|
|
110
116
|
# Standards
|
|
111
117
|
"FormatStandards",
|
|
118
|
+
# Measurements
|
|
119
|
+
"MeasurementFormatter",
|
|
112
120
|
# Numbers
|
|
113
121
|
"NumberFormatter",
|
|
114
122
|
"Severity",
|
|
115
123
|
# Emphasis
|
|
116
124
|
"VisualEmphasis",
|
|
117
125
|
"apply_formatting_standards",
|
|
126
|
+
"convert_to_measurement_dict",
|
|
118
127
|
"format_callout_box",
|
|
119
128
|
# Convenience
|
|
120
129
|
"format_margin",
|
|
130
|
+
"format_measurement",
|
|
131
|
+
"format_measurement_dict",
|
|
121
132
|
"format_pass_fail",
|
|
122
133
|
"format_percentage",
|
|
123
134
|
"format_range",
|