@@ -200,12 +200,16 @@ class DataFrameMetadata:
200
200
201
201
row_totals_indexes : List [List [int ]]
202
202
execution_response : BareExecutionResponse
203
+ primary_labels_from_index : Dict [int , Dict [str , str ]]
204
+ primary_labels_from_columns : Dict [int , Dict [str , str ]]
203
205
204
206
@classmethod
205
207
def from_data (
206
208
cls ,
207
209
headers : Tuple [_DataHeaders , Optional [_DataHeaders ]],
208
210
execution_response : BareExecutionResponse ,
211
+ primary_labels_from_index : Dict [int , Dict [str , str ]],
212
+ primary_labels_from_columns : Dict [int , Dict [str , str ]],
209
213
) -> "DataFrameMetadata" :
210
214
"""This method constructs a DataFrameMetadata object from data headers and an execution response.
211
215
@@ -219,6 +223,8 @@ def from_data(
219
223
return cls (
220
224
row_totals_indexes = row_totals_indexes ,
221
225
execution_response = execution_response ,
226
+ primary_labels_from_index = primary_labels_from_index ,
227
+ primary_labels_from_columns = primary_labels_from_columns ,
222
228
)
223
229
224
230
@@ -304,6 +310,7 @@ def _read_complete_execution_result(
304
310
def _create_header_mapper (
305
311
response : BareExecutionResponse ,
306
312
dim : int ,
313
+ primary_attribute_labels_mapping : Dict [int , Dict [str , str ]],
307
314
label_overrides : Optional [LabelOverrides ] = None ,
308
315
use_local_ids_in_headers : bool = False ,
309
316
use_primary_labels_in_attributes : bool = False ,
@@ -315,6 +322,8 @@ def _create_header_mapper(
315
322
Args:
316
323
response (BareExecutionResponse): Response structure to gather dimension header details.
317
324
dim (int): Dimension id.
325
+ primary_attribute_labels_mapping (Dict[int, Dict[str, str]]): Dict to be filled by mapping of primary labels to
326
+ custom labels per level identified by integer.
318
327
label_overrides (Optional[LabelOverrides]): Label overrides. Defaults to None.
319
328
use_local_ids_in_headers (bool): Use local identifiers of header attributes and metrics. Optional.
320
329
Defaults to False.
@@ -336,10 +345,17 @@ def _mapper(header: Any, header_idx: Optional[int]) -> Optional[str]:
336
345
pass
337
346
elif "attributeHeader" in header :
338
347
if "labelValue" in header ["attributeHeader" ]:
348
+ label_value = header ["attributeHeader" ]["labelValue" ]
349
+ primary_label_value = header ["attributeHeader" ]["primaryLabelValue" ]
339
350
if use_primary_labels_in_attributes :
340
- label = header [ "attributeHeader" ][ "primaryLabelValue" ]
351
+ label = primary_label_value
341
352
else :
342
- label = header ["attributeHeader" ]["labelValue" ]
353
+ label = label_value
354
+ if header_idx is not None :
355
+ if header_idx in primary_attribute_labels_mapping :
356
+ primary_attribute_labels_mapping [header_idx ][primary_label_value ] = label_value
357
+ else :
358
+ primary_attribute_labels_mapping [header_idx ] = {primary_label_value : label_value }
343
359
# explicitly handle '(empty value)' if it's None otherwise it's not recognizable in final MultiIndex
344
360
# backend represents ^^^ by "" (datasource value is "") or None (datasource value is NULL) therefore
345
361
# if both representation are used it's necessary to set label to unique header label (space) to avoid
@@ -382,7 +398,7 @@ def _headers_to_index(
382
398
label_overrides : LabelOverrides ,
383
399
use_local_ids_in_headers : bool = False ,
384
400
use_primary_labels_in_attributes : bool = False ,
385
- ) -> Optional [pandas .Index ]:
401
+ ) -> Tuple [ Optional [pandas .Index ], Dict [ int , Dict [ str , str ]] ]:
386
402
"""Converts headers to a pandas MultiIndex.
387
403
388
404
This function converts the headers present in the response to a pandas MultiIndex (can be used in pandas dataframes)
@@ -398,17 +414,22 @@ def _headers_to_index(
398
414
Defaults to False.
399
415
400
416
Returns:
401
- Optional[pandas.Index]: A pandas MultiIndex object created from the headers, or None if the headers are empty.
417
+ Tuple[Optional[pandas.Index], Dict[int, Dict[str, str]]: A pandas MultiIndex object created from the headers
418
+ with primary attribute labels mapping as Dict, or None with empty Dict if the headers are empty.
402
419
"""
420
+ # dict of primary labels and it's custom labels for attributes per level as key
421
+ primary_attribute_labels_mapping : Dict [int , Dict [str , str ]] = {}
422
+
403
423
if len (response .dimensions ) <= dim_idx or not len (response .dimensions [dim_idx ]["headers" ]):
404
- return None
424
+ return None , primary_attribute_labels_mapping
405
425
406
426
mapper = _create_header_mapper (
407
427
response = response ,
408
428
dim = dim_idx ,
409
429
label_overrides = label_overrides ,
410
430
use_local_ids_in_headers = use_local_ids_in_headers ,
411
431
use_primary_labels_in_attributes = use_primary_labels_in_attributes ,
432
+ primary_attribute_labels_mapping = primary_attribute_labels_mapping ,
412
433
)
413
434
414
435
return pandas .MultiIndex .from_arrays (
@@ -417,7 +438,7 @@ def _headers_to_index(
417
438
for header_idx , header_group in enumerate (cast (_DataHeaders , headers [dim_idx ]))
418
439
],
419
440
names = [mapper (dim_header , None ) for dim_header in (response .dimensions [dim_idx ]["headers" ])],
420
- )
441
+ ), primary_attribute_labels_mapping
421
442
422
443
423
444
def _merge_grand_totals_into_data (extract : _DataWithHeaders ) -> Union [_DataArray , List [_DataArray ]]:
@@ -507,24 +528,33 @@ def convert_execution_response_to_dataframe(
507
528
full_data = _merge_grand_totals_into_data (extract )
508
529
full_headers = _merge_grand_total_headers_into_headers (extract )
509
530
531
+ index , primary_labels_from_index = _headers_to_index (
532
+ dim_idx = 0 ,
533
+ headers = full_headers ,
534
+ response = execution_response ,
535
+ label_overrides = label_overrides ,
536
+ use_local_ids_in_headers = use_local_ids_in_headers ,
537
+ use_primary_labels_in_attributes = use_primary_labels_in_attributes ,
538
+ )
539
+
540
+ columns , primary_labels_from_columns = _headers_to_index (
541
+ dim_idx = 1 ,
542
+ headers = full_headers ,
543
+ response = execution_response ,
544
+ label_overrides = label_overrides ,
545
+ use_local_ids_in_headers = use_local_ids_in_headers ,
546
+ use_primary_labels_in_attributes = use_primary_labels_in_attributes ,
547
+ )
548
+
510
549
df = pandas .DataFrame (
511
550
data = full_data ,
512
- index = _headers_to_index (
513
- dim_idx = 0 ,
514
- headers = full_headers ,
515
- response = execution_response ,
516
- label_overrides = label_overrides ,
517
- use_local_ids_in_headers = use_local_ids_in_headers ,
518
- use_primary_labels_in_attributes = use_primary_labels_in_attributes ,
519
- ),
520
- columns = _headers_to_index (
521
- dim_idx = 1 ,
522
- headers = full_headers ,
523
- response = execution_response ,
524
- label_overrides = label_overrides ,
525
- use_local_ids_in_headers = use_local_ids_in_headers ,
526
- use_primary_labels_in_attributes = use_primary_labels_in_attributes ,
527
- ),
551
+ index = index ,
552
+ columns = columns ,
528
553
)
529
554
530
- return df , DataFrameMetadata .from_data (headers = full_headers , execution_response = execution_response )
555
+ return df , DataFrameMetadata .from_data (
556
+ headers = full_headers ,
557
+ execution_response = execution_response ,
558
+ primary_labels_from_index = primary_labels_from_index ,
559
+ primary_labels_from_columns = primary_labels_from_columns ,
560
+ )
0 commit comments