Skip to content

Commit ea40847

Browse files
author
Harry (Roman Hartig)
authored
Merge pull request #660 from gooddata/rha/cq-345/collect-primary-labels-md
fix(gooddata-pandas): Store primary labels mapping in DF metadata
2 parents 96b5fce + 77d669c commit ea40847

File tree

1 file changed

+53
-23
lines changed

1 file changed

+53
-23
lines changed

gooddata-pandas/gooddata_pandas/result_convertor.py

+53-23
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,16 @@ class DataFrameMetadata:
200200

201201
row_totals_indexes: List[List[int]]
202202
execution_response: BareExecutionResponse
203+
primary_labels_from_index: Dict[int, Dict[str, str]]
204+
primary_labels_from_columns: Dict[int, Dict[str, str]]
203205

204206
@classmethod
205207
def from_data(
206208
cls,
207209
headers: Tuple[_DataHeaders, Optional[_DataHeaders]],
208210
execution_response: BareExecutionResponse,
211+
primary_labels_from_index: Dict[int, Dict[str, str]],
212+
primary_labels_from_columns: Dict[int, Dict[str, str]],
209213
) -> "DataFrameMetadata":
210214
"""This method constructs a DataFrameMetadata object from data headers and an execution response.
211215
@@ -219,6 +223,8 @@ def from_data(
219223
return cls(
220224
row_totals_indexes=row_totals_indexes,
221225
execution_response=execution_response,
226+
primary_labels_from_index=primary_labels_from_index,
227+
primary_labels_from_columns=primary_labels_from_columns,
222228
)
223229

224230

@@ -304,6 +310,7 @@ def _read_complete_execution_result(
304310
def _create_header_mapper(
305311
response: BareExecutionResponse,
306312
dim: int,
313+
primary_attribute_labels_mapping: Dict[int, Dict[str, str]],
307314
label_overrides: Optional[LabelOverrides] = None,
308315
use_local_ids_in_headers: bool = False,
309316
use_primary_labels_in_attributes: bool = False,
@@ -315,6 +322,8 @@ def _create_header_mapper(
315322
Args:
316323
response (BareExecutionResponse): Response structure to gather dimension header details.
317324
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.
318327
label_overrides (Optional[LabelOverrides]): Label overrides. Defaults to None.
319328
use_local_ids_in_headers (bool): Use local identifiers of header attributes and metrics. Optional.
320329
Defaults to False.
@@ -336,10 +345,17 @@ def _mapper(header: Any, header_idx: Optional[int]) -> Optional[str]:
336345
pass
337346
elif "attributeHeader" in header:
338347
if "labelValue" in header["attributeHeader"]:
348+
label_value = header["attributeHeader"]["labelValue"]
349+
primary_label_value = header["attributeHeader"]["primaryLabelValue"]
339350
if use_primary_labels_in_attributes:
340-
label = header["attributeHeader"]["primaryLabelValue"]
351+
label = primary_label_value
341352
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}
343359
# explicitly handle '(empty value)' if it's None otherwise it's not recognizable in final MultiIndex
344360
# backend represents ^^^ by "" (datasource value is "") or None (datasource value is NULL) therefore
345361
# 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(
382398
label_overrides: LabelOverrides,
383399
use_local_ids_in_headers: bool = False,
384400
use_primary_labels_in_attributes: bool = False,
385-
) -> Optional[pandas.Index]:
401+
) -> Tuple[Optional[pandas.Index], Dict[int, Dict[str, str]]]:
386402
"""Converts headers to a pandas MultiIndex.
387403
388404
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(
398414
Defaults to False.
399415
400416
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.
402419
"""
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+
403423
if len(response.dimensions) <= dim_idx or not len(response.dimensions[dim_idx]["headers"]):
404-
return None
424+
return None, primary_attribute_labels_mapping
405425

406426
mapper = _create_header_mapper(
407427
response=response,
408428
dim=dim_idx,
409429
label_overrides=label_overrides,
410430
use_local_ids_in_headers=use_local_ids_in_headers,
411431
use_primary_labels_in_attributes=use_primary_labels_in_attributes,
432+
primary_attribute_labels_mapping=primary_attribute_labels_mapping,
412433
)
413434

414435
return pandas.MultiIndex.from_arrays(
@@ -417,7 +438,7 @@ def _headers_to_index(
417438
for header_idx, header_group in enumerate(cast(_DataHeaders, headers[dim_idx]))
418439
],
419440
names=[mapper(dim_header, None) for dim_header in (response.dimensions[dim_idx]["headers"])],
420-
)
441+
), primary_attribute_labels_mapping
421442

422443

423444
def _merge_grand_totals_into_data(extract: _DataWithHeaders) -> Union[_DataArray, List[_DataArray]]:
@@ -507,24 +528,33 @@ def convert_execution_response_to_dataframe(
507528
full_data = _merge_grand_totals_into_data(extract)
508529
full_headers = _merge_grand_total_headers_into_headers(extract)
509530

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+
510549
df = pandas.DataFrame(
511550
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,
528553
)
529554

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

Comments
 (0)