Coverage for install/scipp/visualization/formatting_datagroup_html.py: 71%

96 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-04-28 01:28 +0000

1# SPDX-License-Identifier: BSD-3-Clause 

2# Copyright (c) 2023 Scipp contributors (https://github.com/scipp) 

3 

4import uuid 

5from string import Template 

6from typing import Optional, Union 

7 

8import numpy as np 

9 

10from ..core.cpp_classes import DataArray, Dataset, Variable 

11from ..core.data_group import DataGroup 

12from ..units import dimensionless 

13from .formatting_html import escape, inline_variable_repr 

14from .resources import ( 

15 load_atomic_row_tpl, 

16 load_collapsible_row_tpl, 

17 load_dg_detail_list_tpl, 

18 load_dg_repr_tpl, 

19 load_dg_style, 

20) 

21 

22 

23def _format_shape(var: Union[Variable, DataArray, Dataset, DataGroup], br_at=30) -> str: 

24 """Return HTML Component that represents the shape of ``var``""" 

25 shape_list = [f"{escape(str(dim))}: {size}" for dim, size in var.sizes.items()] 

26 if sum([len(line) - line.count('\\') for line in shape_list]) < br_at: 

27 return f"({', '.join(shape_list)})" 

28 else: 

29 return f"({', <br>&nbsp'.join(shape_list)})" 

30 

31 

32def _format_atomic_value(value, maxidx: int = 5) -> str: 

33 """Inline preview of single value""" 

34 value_repr = str(value)[:maxidx] 

35 if len(value_repr) < len(str(value)): 

36 value_repr += "..." 

37 return value_repr 

38 

39 

40def _format_dictionary_item(name_item: tuple, maxidx: int = 10) -> str: 

41 """Inline preview of a dictionary""" 

42 name, item = name_item 

43 name = _format_atomic_value(name, maxidx=maxidx) 

44 type_repr = _format_atomic_value(type(item).__name__, maxidx=maxidx) 

45 return "(" + ": ".join((name, type_repr)) + ")" 

46 

47 

48def _format_multi_dim_data(var: Union[Dataset, np.ndarray]) -> str: 

49 """Inline preview of single or multi-dimensional data""" 

50 if isinstance(var, Dataset): 

51 view_iterable = list(var.items()) 

52 var_len = len(var) 

53 first_idx, last_idx = 0, -1 

54 format_item = _format_dictionary_item 

55 elif isinstance(var, np.ndarray): 

56 view_iterable = var 

57 var_len = var.size 

58 first_idx = tuple(np.zeros(var.ndim, dtype=int)) 

59 last_idx = tuple(np.array(var.shape, dtype=int) - np.ones(var.ndim, dtype=int)) 

60 format_item = _format_atomic_value 

61 

62 view_items = [] 

63 if var_len > 0: 

64 view_items.append(format_item(view_iterable[first_idx])) 

65 if var_len > 2: 

66 view_items.append('... ') 

67 if var_len > 1: 

68 view_items.append(format_item(view_iterable[last_idx])) 

69 

70 return ', '.join(view_items) 

71 

72 

73def _summarize_atomic_variable(var, name: str, depth: int = 0) -> str: 

74 """Return HTML Component that contains summary of ``var``""" 

75 shape_repr = escape("()") 

76 unit = '' 

77 dtype_str = '' 

78 preview = '' 

79 parent_obj_str = '' 

80 objtype_str = type(var).__name__ 

81 if isinstance(var, (Dataset, DataArray, Variable)): 

82 parent_obj_str = "scipp" 

83 shape_repr = _format_shape(var) 

84 if isinstance(var, (DataArray, Variable)): 

85 preview = inline_variable_repr(var) 

86 dtype_str = str(var.dtype) 

87 if var.unit is not None: 

88 unit = '𝟙' if var.unit == dimensionless else str(var.unit) # noqa: RUF001 

89 elif isinstance(var, Dataset): 

90 preview = escape(_format_multi_dim_data(var)) 

91 elif isinstance(var, np.ndarray): 

92 parent_obj_str = "numpy" 

93 preview = f"shape={var.shape}, dtype={var.dtype}, values=" 

94 preview += escape(_format_multi_dim_data(var)) 

95 

96 elif preview == '' and hasattr(var, "__str__"): 

97 preview = escape(_format_atomic_value(var, maxidx=80)) 

98 

99 html_tpl = load_atomic_row_tpl() 

100 return Template(html_tpl).substitute( 

101 depth=depth, 

102 name=escape(name), 

103 parent=escape(parent_obj_str), 

104 objtype=escape(objtype_str), 

105 shape_repr=shape_repr, 

106 dtype=escape(dtype_str), 

107 unit=escape(unit), 

108 preview=preview, 

109 ) 

110 

111 

112def _collapsible_summary(var: DataGroup, name: str, name_spaces: list) -> str: 

113 parent_type = "scipp" 

114 objtype = type(var).__name__ 

115 shape_repr = _format_shape(var) 

116 checkbox_id = escape("summary-" + str(uuid.uuid4())) 

117 depth = len(name_spaces) 

118 subsection = _datagroup_detail(var, [*name_spaces, name]) 

119 html_tpl = load_collapsible_row_tpl() 

120 

121 return Template(html_tpl).substitute( 

122 name=escape(str(name)), 

123 parent=escape(parent_type), 

124 objtype=escape(objtype), 

125 shape_repr=shape_repr, 

126 summary_section_id=checkbox_id, 

127 depth=depth, 

128 checkbox_status='', 

129 subsection=subsection, 

130 ) 

131 

132 

133def _datagroup_detail(dg: DataGroup, name_spaces: Optional[list] = None) -> str: 

134 if name_spaces is None: 

135 name_spaces = [] 

136 summary_rows = [] 

137 for name, item in dg.items(): 

138 if isinstance(item, DataGroup): 

139 collapsible_row = _collapsible_summary(item, name, name_spaces) 

140 summary_rows.append(collapsible_row) 

141 else: 

142 summary_rows.append( 

143 _summarize_atomic_variable(item, name, depth=len(name_spaces)) 

144 ) 

145 

146 dg_detail_tpl = Template(load_dg_detail_list_tpl()) 

147 return dg_detail_tpl.substitute(summary_rows=''.join(summary_rows)) 

148 

149 

150def datagroup_repr(dg: DataGroup) -> str: 

151 """Return HTML Component containing details of ``dg``""" 

152 obj_type = "scipp.{} ".format(type(dg).__name__) 

153 checkbox_status = "checked" if len(dg) < 15 else '' 

154 header_id = "datagroup-view-" + str(uuid.uuid4()) 

155 details = _datagroup_detail(dg) 

156 html = Template(load_dg_repr_tpl()) 

157 return html.substitute( 

158 style_sheet=load_dg_style(), 

159 header_id=header_id, 

160 checkbox_status=checkbox_status, 

161 obj_type=obj_type, 

162 shape_repr=_format_shape(dg, br_at=200), 

163 details=details, 

164 )