sigima 0.0.1.dev0__py3-none-any.whl → 1.0.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.
- sigima/__init__.py +142 -2
- sigima/client/__init__.py +105 -0
- sigima/client/base.py +780 -0
- sigima/client/remote.py +469 -0
- sigima/client/stub.py +814 -0
- sigima/client/utils.py +90 -0
- sigima/config.py +444 -0
- sigima/data/logo/Sigima.svg +135 -0
- sigima/data/tests/annotations.json +798 -0
- sigima/data/tests/curve_fitting/exponential_fit.txt +511 -0
- sigima/data/tests/curve_fitting/gaussian_fit.txt +100 -0
- sigima/data/tests/curve_fitting/piecewiseexponential_fit.txt +1022 -0
- sigima/data/tests/curve_fitting/polynomial_fit.txt +100 -0
- sigima/data/tests/curve_fitting/twohalfgaussian_fit.txt +1000 -0
- sigima/data/tests/curve_formats/bandwidth.txt +201 -0
- sigima/data/tests/curve_formats/boxcar.npy +0 -0
- sigima/data/tests/curve_formats/datetime.txt +1001 -0
- sigima/data/tests/curve_formats/dynamic_parameters.txt +4000 -0
- sigima/data/tests/curve_formats/fw1e2.txt +301 -0
- sigima/data/tests/curve_formats/fwhm.txt +319 -0
- sigima/data/tests/curve_formats/multiple_curves.csv +29 -0
- sigima/data/tests/curve_formats/noised_saw.mat +0 -0
- sigima/data/tests/curve_formats/oscilloscope.csv +111 -0
- sigima/data/tests/curve_formats/other/other2/recursive2.txt +5 -0
- sigima/data/tests/curve_formats/other/recursive1.txt +5 -0
- sigima/data/tests/curve_formats/paracetamol.npy +0 -0
- sigima/data/tests/curve_formats/paracetamol.txt +1010 -0
- sigima/data/tests/curve_formats/paracetamol_dx_dy.csv +1000 -0
- sigima/data/tests/curve_formats/paracetamol_dy.csv +1001 -0
- sigima/data/tests/curve_formats/pulse1.npy +0 -0
- sigima/data/tests/curve_formats/pulse2.npy +0 -0
- sigima/data/tests/curve_formats/simple.txt +5 -0
- sigima/data/tests/curve_formats/spectrum.mca +2139 -0
- sigima/data/tests/curve_formats/square2.npy +0 -0
- sigima/data/tests/curve_formats/step.npy +0 -0
- sigima/data/tests/fabry-perot1.jpg +0 -0
- sigima/data/tests/fabry-perot2.jpg +0 -0
- sigima/data/tests/flower.npy +0 -0
- sigima/data/tests/image_formats/NF 180338201.scor-data +11003 -0
- sigima/data/tests/image_formats/binary_image.npy +0 -0
- sigima/data/tests/image_formats/binary_image.png +0 -0
- sigima/data/tests/image_formats/centroid_test.npy +0 -0
- sigima/data/tests/image_formats/coordinated_text/complex_image.txt +10011 -0
- sigima/data/tests/image_formats/coordinated_text/complex_ref_image.txt +10010 -0
- sigima/data/tests/image_formats/coordinated_text/image.txt +15 -0
- sigima/data/tests/image_formats/coordinated_text/image2.txt +14 -0
- sigima/data/tests/image_formats/coordinated_text/image_no_unit_no_label.txt +14 -0
- sigima/data/tests/image_formats/coordinated_text/image_with_nan.txt +15 -0
- sigima/data/tests/image_formats/coordinated_text/image_with_unit.txt +14 -0
- sigima/data/tests/image_formats/fiber.csv +480 -0
- sigima/data/tests/image_formats/fiber.jpg +0 -0
- sigima/data/tests/image_formats/fiber.png +0 -0
- sigima/data/tests/image_formats/fiber.txt +480 -0
- sigima/data/tests/image_formats/gaussian_spot_with_noise.npy +0 -0
- sigima/data/tests/image_formats/mr-brain.dcm +0 -0
- sigima/data/tests/image_formats/noised_gaussian.mat +0 -0
- sigima/data/tests/image_formats/sif_reader/nd_lum_image_no_glue.sif +0 -0
- sigima/data/tests/image_formats/sif_reader/raman1.sif +0 -0
- sigima/data/tests/image_formats/tiling.txt +10 -0
- sigima/data/tests/image_formats/uint16.tiff +0 -0
- sigima/data/tests/image_formats/uint8.tiff +0 -0
- sigima/data/tests/laser_beam/TEM00_z_13.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_18.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_23.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_30.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_35.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_40.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_45.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_50.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_55.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_60.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_65.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_70.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_75.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_80.jpg +0 -0
- sigima/enums.py +195 -0
- sigima/io/__init__.py +123 -0
- sigima/io/base.py +311 -0
- sigima/io/common/__init__.py +5 -0
- sigima/io/common/basename.py +164 -0
- sigima/io/common/converters.py +189 -0
- sigima/io/common/objmeta.py +181 -0
- sigima/io/common/textreader.py +58 -0
- sigima/io/convenience.py +157 -0
- sigima/io/enums.py +17 -0
- sigima/io/ftlab.py +395 -0
- sigima/io/image/__init__.py +9 -0
- sigima/io/image/base.py +177 -0
- sigima/io/image/formats.py +1016 -0
- sigima/io/image/funcs.py +414 -0
- sigima/io/signal/__init__.py +9 -0
- sigima/io/signal/base.py +129 -0
- sigima/io/signal/formats.py +290 -0
- sigima/io/signal/funcs.py +723 -0
- sigima/objects/__init__.py +260 -0
- sigima/objects/base.py +937 -0
- sigima/objects/image/__init__.py +88 -0
- sigima/objects/image/creation.py +556 -0
- sigima/objects/image/object.py +524 -0
- sigima/objects/image/roi.py +904 -0
- sigima/objects/scalar/__init__.py +57 -0
- sigima/objects/scalar/common.py +215 -0
- sigima/objects/scalar/geometry.py +502 -0
- sigima/objects/scalar/table.py +784 -0
- sigima/objects/shape.py +290 -0
- sigima/objects/signal/__init__.py +133 -0
- sigima/objects/signal/constants.py +27 -0
- sigima/objects/signal/creation.py +1428 -0
- sigima/objects/signal/object.py +444 -0
- sigima/objects/signal/roi.py +274 -0
- sigima/params.py +405 -0
- sigima/proc/__init__.py +96 -0
- sigima/proc/base.py +381 -0
- sigima/proc/decorator.py +330 -0
- sigima/proc/image/__init__.py +513 -0
- sigima/proc/image/arithmetic.py +335 -0
- sigima/proc/image/base.py +260 -0
- sigima/proc/image/detection.py +519 -0
- sigima/proc/image/edges.py +329 -0
- sigima/proc/image/exposure.py +406 -0
- sigima/proc/image/extraction.py +458 -0
- sigima/proc/image/filtering.py +219 -0
- sigima/proc/image/fourier.py +147 -0
- sigima/proc/image/geometry.py +661 -0
- sigima/proc/image/mathops.py +340 -0
- sigima/proc/image/measurement.py +195 -0
- sigima/proc/image/morphology.py +155 -0
- sigima/proc/image/noise.py +107 -0
- sigima/proc/image/preprocessing.py +182 -0
- sigima/proc/image/restoration.py +235 -0
- sigima/proc/image/threshold.py +217 -0
- sigima/proc/image/transformations.py +393 -0
- sigima/proc/signal/__init__.py +376 -0
- sigima/proc/signal/analysis.py +206 -0
- sigima/proc/signal/arithmetic.py +551 -0
- sigima/proc/signal/base.py +262 -0
- sigima/proc/signal/extraction.py +60 -0
- sigima/proc/signal/features.py +310 -0
- sigima/proc/signal/filtering.py +484 -0
- sigima/proc/signal/fitting.py +276 -0
- sigima/proc/signal/fourier.py +259 -0
- sigima/proc/signal/mathops.py +420 -0
- sigima/proc/signal/processing.py +580 -0
- sigima/proc/signal/stability.py +175 -0
- sigima/proc/title_formatting.py +227 -0
- sigima/proc/validation.py +272 -0
- sigima/tests/__init__.py +7 -0
- sigima/tests/common/__init__.py +0 -0
- sigima/tests/common/arithmeticparam_unit_test.py +26 -0
- sigima/tests/common/basename_unit_test.py +126 -0
- sigima/tests/common/client_unit_test.py +412 -0
- sigima/tests/common/converters_unit_test.py +77 -0
- sigima/tests/common/decorator_unit_test.py +176 -0
- sigima/tests/common/examples_unit_test.py +104 -0
- sigima/tests/common/kernel_normalization_unit_test.py +242 -0
- sigima/tests/common/roi_basic_unit_test.py +73 -0
- sigima/tests/common/roi_geometry_unit_test.py +171 -0
- sigima/tests/common/scalar_builder_unit_test.py +142 -0
- sigima/tests/common/scalar_unit_test.py +991 -0
- sigima/tests/common/shape_unit_test.py +183 -0
- sigima/tests/common/stat_unit_test.py +138 -0
- sigima/tests/common/title_formatting_unit_test.py +338 -0
- sigima/tests/common/tools_coordinates_unit_test.py +60 -0
- sigima/tests/common/transformations_unit_test.py +178 -0
- sigima/tests/common/validation_unit_test.py +205 -0
- sigima/tests/conftest.py +129 -0
- sigima/tests/data.py +998 -0
- sigima/tests/env.py +280 -0
- sigima/tests/guiutils.py +163 -0
- sigima/tests/helpers.py +532 -0
- sigima/tests/image/__init__.py +28 -0
- sigima/tests/image/binning_unit_test.py +128 -0
- sigima/tests/image/blob_detection_unit_test.py +312 -0
- sigima/tests/image/centroid_unit_test.py +170 -0
- sigima/tests/image/check_2d_array_unit_test.py +63 -0
- sigima/tests/image/contour_unit_test.py +172 -0
- sigima/tests/image/convolution_unit_test.py +178 -0
- sigima/tests/image/datatype_unit_test.py +67 -0
- sigima/tests/image/edges_unit_test.py +155 -0
- sigima/tests/image/enclosingcircle_unit_test.py +88 -0
- sigima/tests/image/exposure_unit_test.py +223 -0
- sigima/tests/image/fft2d_unit_test.py +189 -0
- sigima/tests/image/filtering_unit_test.py +166 -0
- sigima/tests/image/geometry_unit_test.py +654 -0
- sigima/tests/image/hough_circle_unit_test.py +147 -0
- sigima/tests/image/imageobj_unit_test.py +737 -0
- sigima/tests/image/morphology_unit_test.py +71 -0
- sigima/tests/image/noise_unit_test.py +57 -0
- sigima/tests/image/offset_correction_unit_test.py +72 -0
- sigima/tests/image/operation_unit_test.py +518 -0
- sigima/tests/image/peak2d_limits_unit_test.py +41 -0
- sigima/tests/image/peak2d_unit_test.py +133 -0
- sigima/tests/image/profile_unit_test.py +159 -0
- sigima/tests/image/projections_unit_test.py +121 -0
- sigima/tests/image/restoration_unit_test.py +141 -0
- sigima/tests/image/roi2dparam_unit_test.py +53 -0
- sigima/tests/image/roi_advanced_unit_test.py +588 -0
- sigima/tests/image/roi_grid_unit_test.py +279 -0
- sigima/tests/image/spectrum2d_unit_test.py +40 -0
- sigima/tests/image/threshold_unit_test.py +91 -0
- sigima/tests/io/__init__.py +0 -0
- sigima/tests/io/addnewformat_unit_test.py +125 -0
- sigima/tests/io/convenience_funcs_unit_test.py +470 -0
- sigima/tests/io/coordinated_text_format_unit_test.py +495 -0
- sigima/tests/io/datetime_csv_unit_test.py +198 -0
- sigima/tests/io/imageio_formats_test.py +41 -0
- sigima/tests/io/ioregistry_unit_test.py +69 -0
- sigima/tests/io/objmeta_unit_test.py +87 -0
- sigima/tests/io/readobj_unit_test.py +130 -0
- sigima/tests/io/readwriteobj_unit_test.py +67 -0
- sigima/tests/signal/__init__.py +0 -0
- sigima/tests/signal/analysis_unit_test.py +135 -0
- sigima/tests/signal/check_1d_arrays_unit_test.py +169 -0
- sigima/tests/signal/convolution_unit_test.py +404 -0
- sigima/tests/signal/datetime_unit_test.py +176 -0
- sigima/tests/signal/fft1d_unit_test.py +303 -0
- sigima/tests/signal/filters_unit_test.py +403 -0
- sigima/tests/signal/fitting_unit_test.py +929 -0
- sigima/tests/signal/fwhm_unit_test.py +111 -0
- sigima/tests/signal/noise_unit_test.py +128 -0
- sigima/tests/signal/offset_correction_unit_test.py +34 -0
- sigima/tests/signal/operation_unit_test.py +489 -0
- sigima/tests/signal/peakdetection_unit_test.py +145 -0
- sigima/tests/signal/processing_unit_test.py +657 -0
- sigima/tests/signal/pulse/__init__.py +112 -0
- sigima/tests/signal/pulse/crossing_times_unit_test.py +123 -0
- sigima/tests/signal/pulse/plateau_detection_unit_test.py +102 -0
- sigima/tests/signal/pulse/pulse_unit_test.py +1824 -0
- sigima/tests/signal/roi_advanced_unit_test.py +392 -0
- sigima/tests/signal/signalobj_unit_test.py +603 -0
- sigima/tests/signal/stability_unit_test.py +431 -0
- sigima/tests/signal/uncertainty_unit_test.py +611 -0
- sigima/tests/vistools.py +1030 -0
- sigima/tools/__init__.py +59 -0
- sigima/tools/checks.py +290 -0
- sigima/tools/coordinates.py +308 -0
- sigima/tools/datatypes.py +26 -0
- sigima/tools/image/__init__.py +97 -0
- sigima/tools/image/detection.py +451 -0
- sigima/tools/image/exposure.py +77 -0
- sigima/tools/image/extraction.py +48 -0
- sigima/tools/image/fourier.py +260 -0
- sigima/tools/image/geometry.py +190 -0
- sigima/tools/image/preprocessing.py +165 -0
- sigima/tools/signal/__init__.py +86 -0
- sigima/tools/signal/dynamic.py +254 -0
- sigima/tools/signal/features.py +135 -0
- sigima/tools/signal/filtering.py +171 -0
- sigima/tools/signal/fitting.py +1171 -0
- sigima/tools/signal/fourier.py +466 -0
- sigima/tools/signal/interpolation.py +70 -0
- sigima/tools/signal/peakdetection.py +126 -0
- sigima/tools/signal/pulse.py +1626 -0
- sigima/tools/signal/scaling.py +50 -0
- sigima/tools/signal/stability.py +258 -0
- sigima/tools/signal/windowing.py +90 -0
- sigima/worker.py +79 -0
- sigima-1.0.0.dist-info/METADATA +233 -0
- sigima-1.0.0.dist-info/RECORD +262 -0
- {sigima-0.0.1.dev0.dist-info → sigima-1.0.0.dist-info}/licenses/LICENSE +29 -29
- sigima-0.0.1.dev0.dist-info/METADATA +0 -60
- sigima-0.0.1.dev0.dist-info/RECORD +0 -6
- {sigima-0.0.1.dev0.dist-info → sigima-1.0.0.dist-info}/WHEEL +0 -0
- {sigima-0.0.1.dev0.dist-info → sigima-1.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Scalar results subpackage
|
|
5
|
+
=========================
|
|
6
|
+
|
|
7
|
+
This subpackage provides classes and functions for handling scalar results in Sigima.
|
|
8
|
+
|
|
9
|
+
The subpackage is split into two main modules:
|
|
10
|
+
|
|
11
|
+
- :mod:`sigima.objects.scalar.table`: Table results and related utilities
|
|
12
|
+
- :mod:`sigima.objects.scalar.geometry`: Geometry results and related utilities
|
|
13
|
+
|
|
14
|
+
For backward compatibility, all public symbols are re-exported from this __init__.py
|
|
15
|
+
file, so existing imports like:
|
|
16
|
+
|
|
17
|
+
.. code-block:: python
|
|
18
|
+
|
|
19
|
+
from sigima.objects.scalar import TableResult, GeometryResult
|
|
20
|
+
|
|
21
|
+
continue to work as expected.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
# Import all public symbols from both modules
|
|
25
|
+
from sigima.objects.scalar.geometry import (
|
|
26
|
+
GeometryResult,
|
|
27
|
+
KindShape,
|
|
28
|
+
concat_geometries,
|
|
29
|
+
filter_geometry_by_roi,
|
|
30
|
+
)
|
|
31
|
+
from sigima.objects.scalar.table import (
|
|
32
|
+
NO_ROI,
|
|
33
|
+
ResultHtmlGenerator,
|
|
34
|
+
TableKind,
|
|
35
|
+
TableResult,
|
|
36
|
+
TableResultBuilder,
|
|
37
|
+
calc_table_from_data,
|
|
38
|
+
concat_tables,
|
|
39
|
+
filter_table_by_roi,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Define __all__ to specify what gets imported with
|
|
43
|
+
# "from sigima.objects.scalar import *"
|
|
44
|
+
__all__ = [
|
|
45
|
+
"NO_ROI",
|
|
46
|
+
"GeometryResult",
|
|
47
|
+
"KindShape",
|
|
48
|
+
"ResultHtmlGenerator",
|
|
49
|
+
"TableKind",
|
|
50
|
+
"TableResult",
|
|
51
|
+
"TableResultBuilder",
|
|
52
|
+
"calc_table_from_data",
|
|
53
|
+
"concat_geometries",
|
|
54
|
+
"concat_tables",
|
|
55
|
+
"filter_geometry_by_roi",
|
|
56
|
+
"filter_table_by_roi",
|
|
57
|
+
]
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Common utilities for scalar result objects
|
|
5
|
+
==========================================
|
|
6
|
+
|
|
7
|
+
This module provides shared functionality for TableResult and GeometryResult classes
|
|
8
|
+
without using inheritance or mixins, maintaining their dataclass integrity.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import TYPE_CHECKING
|
|
14
|
+
|
|
15
|
+
import pandas as pd
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from sigima.objects import GeometryResult, ImageObj, SignalObj, TableResult
|
|
19
|
+
|
|
20
|
+
# Sentinel value for "full signal/image / no ROI" rows in result tables
|
|
21
|
+
NO_ROI: int = -1
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class DisplayPreferencesManager:
|
|
25
|
+
"""Manages display preferences for result objects."""
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def get_display_preferences(
|
|
29
|
+
result: GeometryResult | TableResult,
|
|
30
|
+
headers: list[str],
|
|
31
|
+
attr_name: str = "hidden_headers",
|
|
32
|
+
) -> dict[str, bool]:
|
|
33
|
+
"""Get display preferences for headers.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
result: The result object containing attrs
|
|
37
|
+
headers: List of header names
|
|
38
|
+
attr_name: Name of the attribute storing hidden headers
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Dictionary mapping header names to visibility (True=visible, False=hidden)
|
|
42
|
+
"""
|
|
43
|
+
prefs = {}
|
|
44
|
+
hidden_headers = result.attrs.get(attr_name, set())
|
|
45
|
+
if isinstance(hidden_headers, (list, tuple)):
|
|
46
|
+
hidden_headers = set(hidden_headers)
|
|
47
|
+
|
|
48
|
+
for header in headers:
|
|
49
|
+
prefs[header] = header not in hidden_headers
|
|
50
|
+
return prefs
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def set_display_preferences(
|
|
54
|
+
result: GeometryResult | TableResult,
|
|
55
|
+
preferences: dict[str, bool],
|
|
56
|
+
headers: list[str],
|
|
57
|
+
attr_name: str = "hidden_headers",
|
|
58
|
+
) -> None:
|
|
59
|
+
"""Set display preferences for headers.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
result: The result object to modify
|
|
63
|
+
preferences: Dictionary mapping header names to visibility
|
|
64
|
+
headers: List of valid header names
|
|
65
|
+
attr_name: Name of the attribute to store hidden headers
|
|
66
|
+
"""
|
|
67
|
+
hidden_headers = {
|
|
68
|
+
header
|
|
69
|
+
for header, visible in preferences.items()
|
|
70
|
+
if not visible and header in headers
|
|
71
|
+
}
|
|
72
|
+
if hidden_headers:
|
|
73
|
+
result.attrs[attr_name] = list(hidden_headers)
|
|
74
|
+
elif attr_name in result.attrs:
|
|
75
|
+
del result.attrs[attr_name]
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def get_visible_headers(
|
|
79
|
+
result: GeometryResult | TableResult,
|
|
80
|
+
headers: list[str],
|
|
81
|
+
attr_name: str = "hidden_headers",
|
|
82
|
+
) -> list[str]:
|
|
83
|
+
"""Get list of currently visible headers.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
result: The result object
|
|
87
|
+
headers: List of all header names
|
|
88
|
+
attr_name: Name of the attribute storing hidden headers
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
List of header names that should be displayed
|
|
92
|
+
"""
|
|
93
|
+
prefs = DisplayPreferencesManager.get_display_preferences(
|
|
94
|
+
result, headers, attr_name
|
|
95
|
+
)
|
|
96
|
+
return [header for header in headers if prefs.get(header, True)]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class DataFrameManager:
|
|
100
|
+
"""Manages DataFrame operations for result objects."""
|
|
101
|
+
|
|
102
|
+
@staticmethod
|
|
103
|
+
def apply_visible_only_filter(
|
|
104
|
+
df: pd.DataFrame, visible_headers: list[str]
|
|
105
|
+
) -> pd.DataFrame:
|
|
106
|
+
"""Apply visible-only filter to a DataFrame.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
df: DataFrame to filter
|
|
110
|
+
visible_headers: List of headers that should be visible
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Filtered DataFrame with only visible columns
|
|
114
|
+
"""
|
|
115
|
+
# Keep roi_index column if present
|
|
116
|
+
if "roi_index" in df.columns:
|
|
117
|
+
visible_headers = ["roi_index"] + visible_headers
|
|
118
|
+
|
|
119
|
+
# Filter to only available visible columns
|
|
120
|
+
available_headers = [col for col in visible_headers if col in df.columns]
|
|
121
|
+
if available_headers:
|
|
122
|
+
return df[available_headers]
|
|
123
|
+
return df
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class ResultHtmlGenerator:
|
|
127
|
+
"""Utility class for generating HTML from result objects using composition."""
|
|
128
|
+
|
|
129
|
+
@staticmethod
|
|
130
|
+
def generate_html(
|
|
131
|
+
result: GeometryResult | TableResult,
|
|
132
|
+
obj: SignalObj | ImageObj,
|
|
133
|
+
visible_only: bool = True,
|
|
134
|
+
transpose_single_row: bool = True,
|
|
135
|
+
**kwargs,
|
|
136
|
+
) -> str:
|
|
137
|
+
"""Generate HTML from a result object.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
result: The result object (TableResult or GeometryResult)
|
|
141
|
+
obj: SignalObj or ImageObj for ROI title extraction
|
|
142
|
+
visible_only: If True, include only visible headers based on display
|
|
143
|
+
preferences. Default is False.
|
|
144
|
+
transpose_single_row: If True, transpose the table when there's only one row
|
|
145
|
+
**kwargs: Additional arguments passed to DataFrame.to_html()
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
HTML representation of the result
|
|
149
|
+
"""
|
|
150
|
+
df = result.to_dataframe(visible_only=visible_only)
|
|
151
|
+
|
|
152
|
+
# Remove roi_index column for display
|
|
153
|
+
if "roi_index" in df.columns:
|
|
154
|
+
roi_indices = df["roi_index"].tolist()
|
|
155
|
+
df = df.drop(columns=["roi_index"])
|
|
156
|
+
else:
|
|
157
|
+
roi_indices = None
|
|
158
|
+
|
|
159
|
+
# Create row headers
|
|
160
|
+
row_headers = ResultHtmlGenerator._get_row_headers(result, roi_indices, obj)
|
|
161
|
+
|
|
162
|
+
# Transpose if single row and flag is set
|
|
163
|
+
if transpose_single_row and len(df) == 1:
|
|
164
|
+
# Transpose the dataframe
|
|
165
|
+
df_t = df.T
|
|
166
|
+
df_t.columns = [row_headers[0] if row_headers[0] else "Value"]
|
|
167
|
+
df_t.index.name = "Item"
|
|
168
|
+
# Get labels for the transposed view
|
|
169
|
+
display_labels = list(df.columns)
|
|
170
|
+
df_t.index = display_labels
|
|
171
|
+
text = f'<u><b style="color: blue">{result.title}</b></u>:'
|
|
172
|
+
html_kwargs = {"border": 0}
|
|
173
|
+
html_kwargs.update(kwargs)
|
|
174
|
+
# Format numeric columns only, avoiding float_format on mixed data types
|
|
175
|
+
for col in df_t.select_dtypes(include=["number"]).columns:
|
|
176
|
+
df_t[col] = df_t[col].map(lambda x: f"{x:.3g}" if pd.notna(x) else x)
|
|
177
|
+
text += df_t.to_html(**html_kwargs)
|
|
178
|
+
else:
|
|
179
|
+
# Standard horizontal layout
|
|
180
|
+
df.index = row_headers
|
|
181
|
+
text = f'<u><b style="color: blue">{result.title}</b></u>:'
|
|
182
|
+
html_kwargs = {"border": 0}
|
|
183
|
+
html_kwargs.update(kwargs)
|
|
184
|
+
# Format numeric columns only, avoiding float_format on mixed data types
|
|
185
|
+
for col in df.select_dtypes(include=["number"]).columns:
|
|
186
|
+
df[col] = df[col].map(lambda x: f"{x:.3g}" if pd.notna(x) else x)
|
|
187
|
+
text += df.to_html(**html_kwargs)
|
|
188
|
+
|
|
189
|
+
return text
|
|
190
|
+
|
|
191
|
+
@staticmethod
|
|
192
|
+
def _get_row_headers(
|
|
193
|
+
result: TableResult | "GeometryResult",
|
|
194
|
+
roi_indices: list[int] | None,
|
|
195
|
+
obj: SignalObj | ImageObj,
|
|
196
|
+
) -> list[str]:
|
|
197
|
+
"""Create row headers from ROI indices."""
|
|
198
|
+
row_headers = []
|
|
199
|
+
if roi_indices is not None:
|
|
200
|
+
for roi_idx in roi_indices:
|
|
201
|
+
if roi_idx == NO_ROI:
|
|
202
|
+
header = ""
|
|
203
|
+
else:
|
|
204
|
+
header = f"ROI {roi_idx}"
|
|
205
|
+
# Try to get ROI title from object if available
|
|
206
|
+
if obj.roi is not None:
|
|
207
|
+
header = obj.roi.get_single_roi_title(roi_idx)
|
|
208
|
+
row_headers.append(header)
|
|
209
|
+
else:
|
|
210
|
+
# Need to get DataFrame to know the number of rows
|
|
211
|
+
df = result.to_dataframe()
|
|
212
|
+
if "roi_index" in df.columns:
|
|
213
|
+
df = df.drop(columns=["roi_index"])
|
|
214
|
+
row_headers = [""] * len(df)
|
|
215
|
+
return row_headers
|