23
23
from __future__ import (absolute_import , division , print_function )
24
24
from six .moves import (filter , input , map , range , zip ) # noqa
25
25
26
+ import re
27
+
26
28
27
29
class CubeRepresentation (object ):
28
30
"""
@@ -31,23 +33,49 @@ class CubeRepresentation(object):
31
33
This includes:
32
34
33
35
* ``_html_repr_``: a representation of the cube as an html object,
34
- available in jupyter notebooks.
36
+ available in Jupyter notebooks. Specifically, this is presented as an
37
+ html table.
35
38
36
39
"""
40
+
37
41
_template = """
38
42
<style>
39
43
a.iris {{
40
44
text-decoration: none !important;
41
45
}}
42
- .iris {{
46
+ table .iris {{
43
47
white-space: pre;
44
- }}
45
- .iris-panel-group {{
46
- display: block;
47
- overflow: visible;
48
- width: max-content;
48
+ border: 1px solid;
49
+ border-color: #9c9c9c;
49
50
font-family: monaco, monospace;
50
51
}}
52
+ th.iris {{
53
+ background: #303f3f;
54
+ color: #e0e0e0;
55
+ border-left: 1px solid;
56
+ border-color: #9c9c9c;
57
+ font-size: 1.05em;
58
+ min-width: 50px;
59
+ max-width: 125px;
60
+ }}
61
+ tr.iris :first-child {{
62
+ border-right: 1px solid #9c9c9c !important;
63
+ }}
64
+ td.iris-title {{
65
+ background: #d5dcdf;
66
+ border-top: 1px solid #9c9c9c;
67
+ font-weight: bold;
68
+ }}
69
+ .iris-word-cell {{
70
+ text-align: left !important;
71
+ white-space: pre;
72
+ }}
73
+ .iris-subheading-cell {{
74
+ padding-left: 2em !important;
75
+ }}
76
+ .iris-inclusion-cell {{
77
+ padding-right: 1em !important;
78
+ }}
51
79
.iris-panel-body {{
52
80
padding-top: 0px;
53
81
}}
@@ -58,55 +86,19 @@ class CubeRepresentation(object):
58
86
margin-top: 7px;
59
87
}}
60
88
</style>
61
- <div class="panel-group iris-panel-group">
62
- <div class="panel panel-default">
63
- <div class="panel-heading">
64
- <h4 class="panel-title">
65
- <a class="iris" data-toggle="collapse" href="#collapse1-{obj_id}">
66
- {summary}
67
- </a>
68
- </h4>
69
- </div>
70
- <div id="collapse1-{obj_id}" class="panel-collapse collapse in">
71
- {content}
72
- </div>
73
- </div>
74
- </div>
89
+ <table class="iris" id="{id}">
90
+ {header}
91
+ {shape}
92
+ {content}
93
+ </table>
75
94
"""
76
95
77
- # Need to format the keywords:
78
- # `emt_id`, `obj_id`, `str_heading`, `opened`, `content`.
79
- _insert_content = """
80
- <div class="panel-body iris-panel-body">
81
- <h4 class="panel-title iris-panel-title">
82
- <a class="iris" data-toggle="collapse" href="#{emt_id}-{obj_id}">
83
- {str_heading}
84
- </a>
85
- </h4>
86
- </div>
87
- <div id="{emt_id}-{obj_id}" class="panel-collapse collapse{opened}">
88
- <div class="panel-body iris-panel-body">
89
- <p class="iris">{content}</p>
90
- </div>
91
- </div>
92
- """
93
-
94
96
def __init__ (self , cube ):
95
- """
96
- Produce different representations of a :class:`~iris.cube.Cube`.
97
-
98
- Args:
99
-
100
- * cube
101
- the cube to produce representations of.
102
-
103
- """
104
-
105
97
self .cube = cube
106
98
self .cube_id = id (self .cube )
107
99
self .cube_str = str (self .cube )
108
100
109
- self .summary = None
101
+ self .summary = self . cube . summary ( True )
110
102
self .str_headings = {
111
103
'Dimension coordinates:' : None ,
112
104
'Auxiliary coordinates:' : None ,
@@ -115,18 +107,32 @@ def __init__(self, cube):
115
107
'Attributes:' : None ,
116
108
'Cell methods:' : None ,
117
109
}
118
- self .major_headings = ['Dimension coordinates:' ,
119
- 'Auxiliary coordinates:' ,
120
- 'Attributes :' ]
110
+ self .dim_desc_coords = ['Dimension coordinates:' ,
111
+ 'Auxiliary coordinates:' ,
112
+ 'Derived coordinates :' ]
121
113
122
- def _get_bits (self ):
114
+ def _summary_content (self ):
123
115
"""
124
- Parse the str representation of the cube to retrieve the elements
125
- to add to an html representation of the cube.
116
+ Deal with the content in the summary (the first line of printout).
117
+
118
+ This contains:
119
+ * name (unit),
120
+ * dim name (len of dim) for each dim.
126
121
127
122
"""
123
+ emts = re .findall (r'\w+' , self .summary )
124
+ self .names = [' ' .join (name .split ('_' )) for name in emts [::2 ]]
125
+ self .shapes = emts [1 ::2 ]
126
+
127
+ # Name and unit are the first item in names and descs respectively.
128
+ self .name = self .names .pop (0 ).title ()
129
+ self .units = self .shapes .pop (0 )
130
+ self .ndims = self .cube .ndim
131
+
132
+ def _get_bits (self ):
128
133
bits = self .cube_str .split ('\n ' )
129
- self .summary = bits [0 ]
134
+ # self.summary = bits[0]
135
+ self ._summary_content ()
130
136
left_indent = bits [1 ].split ('D' )[0 ]
131
137
132
138
# Get heading indices within the printout.
@@ -139,41 +145,123 @@ def _get_bits(self):
139
145
continue
140
146
else :
141
147
start_inds .append (start_ind )
142
- # Make sure the indices are in order.
143
- start_inds = sorted (start_inds )
144
148
# Mark the end of the file.
145
- start_inds .append (None )
149
+ start_inds .append (0 )
146
150
147
151
# Retrieve info for each heading from the printout.
148
152
for i0 , i1 in zip (start_inds [:- 1 ], start_inds [1 :]):
149
153
str_heading_name = bits [i0 ].strip ()
150
- if i1 is not None :
154
+ if i1 != 0 :
151
155
content = bits [i0 + 1 : i1 ]
152
156
else :
153
157
content = bits [i0 + 1 :]
154
158
self .str_headings [str_heading_name ] = content
155
159
160
+ def _make_header (self ):
161
+ """
162
+ Make the table header. This is similar to the summary of the cube,
163
+ but does not include dim shapes. These are included on the next table
164
+ row down, and produced with `make_shapes_row`.
165
+
166
+ """
167
+ # Header row.
168
+ tlc_template = \
169
+ '<th class="iris iris-word-cell">{self.name} ({self.units})</th>'
170
+ top_left_cell = tlc_template .format (self = self )
171
+ cells = ['<tr class="iris">' , top_left_cell ]
172
+ for dim_name in self .names :
173
+ cells .append (
174
+ '<th class="iris iris-word-cell">{}</th>' .format (dim_name ))
175
+ cells .append ('</tr>' )
176
+ return '\n ' .join (cell for cell in cells )
177
+
178
+ def _make_shapes_row (self ):
179
+ """Add a row to show data / dimensions shape."""
180
+ title_cell = \
181
+ '<td class="iris-word-cell iris-subheading-cell">Shape</td>'
182
+ cells = ['<tr class="iris">' , title_cell ]
183
+ for shape in self .shapes :
184
+ cells .append (
185
+ '<td class="iris iris-inclusion-cell">{}</td>' .format (shape ))
186
+ cells .append ('</td>' )
187
+ return '\n ' .join (cell for cell in cells )
188
+
189
+ def _make_row (self , title , body = None , col_span = 0 ):
190
+ """
191
+ Produce one row for the table body; i.e.
192
+ <tr><td>Coord name</td><td>x</td><td>-</td>...</tr>
193
+
194
+ `body` contains the content for each cell not in the left-most (title)
195
+ column.
196
+ If None, indicates this row is a title row (see below).
197
+ `title` contains the row heading. If `body` is None, indicates
198
+ that the row contains a sub-heading;
199
+ e.g. 'Dimension coordinates:'.
200
+ `col_span` indicates how many columns the string should span.
201
+
202
+ """
203
+ row = ['<tr class="iris">' ]
204
+ template = ' <td{html_cls}>{content}</td>'
205
+ if body is None :
206
+ # This is a title row.
207
+ # Strip off the trailing ':' from the title string.
208
+ title = title .strip ()[:- 1 ]
209
+ row .append (
210
+ template .format (html_cls = ' class="iris-title iris-word-cell"' ,
211
+ content = title ))
212
+ # Add blank cells for the rest of the rows.
213
+ for _ in range (self .ndims ):
214
+ row .append (template .format (html_cls = ' class="iris-title"' ,
215
+ content = '' ))
216
+ else :
217
+ # This is not a title row.
218
+ # Deal with name of coord/attr etc. first.
219
+ sub_title = '\t {}' .format (title )
220
+ row .append (template .format (
221
+ html_cls = ' class="iris-word-cell iris-subheading-cell"' ,
222
+ content = sub_title ))
223
+ # One further item or more than that?
224
+ if col_span != 0 :
225
+ html_cls = ' class="{}" colspan="{}"' .format ('iris-word-cell' ,
226
+ col_span )
227
+ row .append (template .format (html_cls = html_cls , content = body ))
228
+ else :
229
+ # "Inclusion" - `x` or `-`.
230
+ for itm in body :
231
+ row .append (template .format (
232
+ html_cls = ' class="iris-inclusion-cell"' ,
233
+ content = itm ))
234
+ row .append ('</tr>' )
235
+ return row
236
+
156
237
def _make_content (self ):
157
238
elements = []
158
239
for k , v in self .str_headings .items ():
159
240
if v is not None :
160
- html_id = k .split (' ' )[0 ].lower ().strip (':' )
161
- content = '\n ' .join (line for line in v )
162
- collapse = ' in' if k in self .major_headings else ''
163
- element = self ._insert_content .format (emt_id = html_id ,
164
- obj_id = self .cube_id ,
165
- str_heading = k ,
166
- opened = collapse ,
167
- content = content )
168
- elements .append (element )
241
+ # Add the sub-heading title.
242
+ elements .extend (self ._make_row (k ))
243
+ for line in v :
244
+ # Add every other row in the sub-heading.
245
+ if k in self .dim_desc_coords :
246
+ body = re .findall (r'[\w-]+' , line )
247
+ title = body .pop (0 )
248
+ colspan = 0
249
+ else :
250
+ split_point = line .index (':' )
251
+ title = line [:split_point ].strip ()
252
+ body = line [split_point + 2 :].strip ()
253
+ colspan = self .ndims
254
+ elements .extend (
255
+ self ._make_row (title , body = body , col_span = colspan ))
169
256
return '\n ' .join (element for element in elements )
170
257
171
258
def repr_html (self ):
172
- """Produce an html representation of a cube and return it ."""
259
+ """The `repr` interface to Jupyter ."""
173
260
self ._get_bits ()
174
- summary = self .summary
261
+ header = self ._make_header ()
262
+ shape = self ._make_shapes_row ()
175
263
content = self ._make_content ()
176
- return self ._template .format (summary = summary ,
177
- content = content ,
178
- obj_id = self . cube_id ,
179
- )
264
+ return self ._template .format (header = header ,
265
+ id = self . cube_id ,
266
+ shape = shape ,
267
+ content = content )
0 commit comments