import itertools import logging import pandas as pd from .chart import Chart logger = logging.getLogger(__name__) class StackChart(Chart): def __init__(self, chart_conf): super().__init__(chart_conf) self.data = [] self.map_keys = [] self.max_value = 0 def fill_json_template(self): """Filling json template with info from self.conf and data from retrieval_task""" if not self.data: self.data = self.map_module_result() return { "id": self.conf["id"], "type": "StackChart", "label": self.conf["label"], "options": self.conf["options"] if "options" in self.conf else {}, "data": self.data, "keys": self.map_keys, "maxValue": self.max_value, } def get_sort_col(self, x): # This is here to be able to sort "unknown" and have it appear last if x == "unknown": x = "9999-" sort_col = x.split("-")[0] sort_col = sort_col.split("+")[0] return int(sort_col) def map_module_result(self): """Format the query result and make the proper labels for records""" am_data = self._data_retrieval_task.wait_for_result() """ Filling missing labels for all groups with value = 0""" # Sometimes am_data is missing (dataframe 0, 0). """ TODO! Debug why sometimes data is missing. schema: c37f63dc3134324c055a7061b9541ba201f65f1ddcf9d03c968535020 collection: 91 database: fansifter-test """ try: df = pd.DataFrame(am_data) unique1 = list(df[0].unique()) unique2 = list(df[1].unique()) base_df = pd.DataFrame(list(itertools.product(unique1, unique2))) result_df = base_df.merge(df, how="left", on=[0, 1]).fillna(0) except Exception as e: logger.exception(e) # In case data missing, return that missing data result_df = pd.DataFrame(am_data) # Sort by our temporary auxiliary sorting column try: result_df["sort_col"] = result_df[1].apply(lambda x: self.get_sort_col(x)) result_df.sort_values(by=[0, "sort_col"], inplace=True) del result_df["sort_col"] # we don't need the col except Exception: # We skip sorting when it fails, for "female" pass am_map = self.am.get_output_map() data_map = { d: am_map[s].idx for d, s in self.conf["output_maps"]["data"].items() } item_map = { d: am_map[s].idx for d, s in self.conf["output_maps"]["item"].items() } groups = {} for data in result_df.itertuples(index=False): groups.setdefault( data[data_map["group"]], {dest: data[source] for dest, source in data_map.items()}, ).setdefault("items", []).append( {dest: data[source] for dest, source in item_map.items()} ) self.map_keys = list(groups.keys()) self.derive_max_value(groups) return list(groups.values()) def derive_max_value(self, groups): if groups: self.max_value = max( sum(item["value"] for item in items) for items in [group["items"] for group in groups.values()] if items ) if "horizontal" in self.conf["options"]: self.max_value *= 1.1