def calculate_flow(self, atomic_params): """ checking if the shelf is sorted like the brands list. :return: 100 if it's fine, 0 otherwise. """ population_entity_type = Converters.convert_type(atomic_params[Const.ENTITY_TYPE]) progression_list = map(lambda x: x.strip(), atomic_params[Const.ENTITY_VAL].split(',')) location_entity_type = Converters.convert_type(atomic_params[Const.TYPE_FILTER]) location_values = map(lambda x: x.strip(), atomic_params[Const.VALUE_FILTER].split(',')) is_in_param = False if atomic_params[Const.IN_NOT_IN] == u'Not in' else True filter_loc_param = self.scif[location_entity_type].isin(location_values) if is_in_param else\ ~self.scif[location_entity_type].isin(location_values) filtered_scif = self.scif[ filter_loc_param & (self.scif['tagged'] >= 1) & (self.scif[population_entity_type].isin(progression_list))] join_on = ['scene_fk', 'product_fk'] match_product_join_scif = pd.merge(filtered_scif, self.match_product_in_scene, on=join_on, how='left', suffixes=('_x', '_matches')) progression_field = 'brand_name' group_column = 'scene_fk' progression_cross_shelves_true = (self.tool_box_for_flow.progression( df=match_product_join_scif, progression_list=progression_list, progression_field=progression_field, at_least_one=False, left_to_right=False, cross_bays=True, cross_shelves=True, include_stacking=False, group_by=group_column)) progression_cross_shelves_false = (self.tool_box_for_flow.progression( df=match_product_join_scif, progression_list=progression_list, progression_field=progression_field, at_least_one=False, left_to_right=False, cross_bays=True, cross_shelves=False, include_stacking=False, group_by=group_column)) return 100.0 * (progression_cross_shelves_true or progression_cross_shelves_false)
def calculate_sos(self, atomic_params): """ :param atomic_params: dict - atomic kpi line from the template :return: the percent of SOS (if it's binary - 100 if more than target, otherwise 0). """ numerator_type = atomic_params[Const.ENTITY_TYPE_NUMERATOR] numerator_value = atomic_params[Const.NUMERATOR] denominator_type = atomic_params[Const.ENTITY_TYPE_DENOMINATOR] denominator_value = atomic_params[Const.DENOMINATOR] in_or_not = atomic_params[Const.IN_NOT_IN] filter_type = Converters.convert_type(atomic_params[Const.TYPE_FILTER]) filter_value = atomic_params[Const.VALUE_FILTER] denominator_filters = self.get_default_filters(denominator_type, denominator_value) numerator_filters = self.get_default_filters(numerator_type, numerator_value) if in_or_not: numerator_filters = self.update_filters(numerator_filters, in_or_not, filter_type, filter_value) denominator_filters = self.update_filters(denominator_filters, in_or_not, filter_type, filter_value) atomic_score = self.tools.calculate_share_of_shelf( sos_filters=numerator_filters, **denominator_filters) * 100 if atomic_params[Const.SCORE] == Const.BINARY: try: return 100 * (atomic_score >= float(atomic_params[Const.targets_line][ self.store_type])) except ValueError: Log.warning('The target for {} is bad in store {}'.format( atomic_params[Const.ATOMIC_NAME], self.store_type)) return 0.0 elif atomic_params[Const.SCORE] != Const.NUMERIC: Log.error('The score is not numeric and not binary.') return atomic_score
def calculate_scene_count(self, atomic_params): """ :param atomic_params: dict - atomic kpi line from the template :return: int - amount of scenes. """ filters = {Converters.convert_type( atomic_params[Const.ENTITY_TYPE]): map(lambda x: x.strip(), atomic_params[Const.ENTITY_VAL].split(','))} scene_count = self.tools.calculate_number_of_scenes(**filters) return scene_count
def get_default_filters(type_name, value_name): """ :param type_name: string that contains list of types :param value_name: string that contains list of values in the same length :return: filter as dict. """ if ',' in type_name: types = type_name.split(',') types = map(lambda x: x.strip(), types) values = value_name.split(',') values = map(lambda x: x.strip(), values) filters = {} if len(types) != len(values): Log.warning('there are {} types and {} values, should be the same amount'.format( len(types), len(values))) else: for i in xrange(len(types)): filters[Converters.convert_type(types[i])] = values[i] else: filters = {Converters.convert_type(type_name): map(lambda x: x.strip(), value_name.split(','))} return filters
def calculate_all_availability(self, type1, value1, type2, value2, in_or_not, filter_type, filter_value): """ :param atomic_params: dict - atomic kpi line from the template. checks the kind of the survey and sends it to the match function :return: float - score. """ if not type1 or not value1: Log.warning('There is no type and value in the atomic availability') return 0.0 type1 = Converters.convert_type(type1) value1 = value1.split(',') value1 = map(lambda x: x.strip(), value1) type2 = Converters.convert_type(type2) value2 = value2 filters = {type1: value1} if type2 and value2: value2 = value2.split(',') value2 = map(lambda x: x.strip(), value2) filters[type2] = value2 if in_or_not: filters = self.update_filters(filters, in_or_not, filter_type, filter_value) return self.tools.calculate_availability(**filters)
def calculate_availability(self, atomic_params): """ :param atomic_params: dict - atomic kpi line from the template :return: 100 if this product is available, 0 otherwise. """ availability = self.calculate_all_availability(atomic_params[Const.ENTITY_TYPE], atomic_params[Const.ENTITY_VAL], atomic_params[Const.ENTITY_TYPE2], atomic_params[Const.ENTITY_VAL2], atomic_params[Const.IN_NOT_IN], Converters.convert_type(atomic_params[Const.TYPE_FILTER]), atomic_params[Const.VALUE_FILTER]) return 100.0 * (availability > 0)
def update_filters(self, filters, in_or_not, filter_type, filter_value): """ :param filters: the source filters as dict. :param in_or_not: boolean if it should include or not include :param filter_type: str :param filter_value: str adds to exist filter if to include/exclude one more condition. :return: dict - the updated filter. """ filter_type = Converters.convert_type(filter_type) if "Not" in in_or_not: list_of_negative = list(self.scif[~(self.scif[filter_type] == filter_value)][filter_type].unique()) filters[filter_type] = list_of_negative elif "In" in in_or_not: filters[filter_type] = filter_value else: Log.warning('The value in "In/Not In" in the template should be "Not in", "In" or empty') return filters
def calculate_planogram_new(self, atomic_params): """ :param atomic_params: dict - atomic kpi line from the template :return: 100 if there is scene which has at least one correctly positioned product, 0 otherwise. """ type_name = Converters.convert_type(atomic_params[Const.ENTITY_TYPE]) values = map(lambda x: x.strip(), atomic_params[Const.ENTITY_VAL].split(',')) wanted_answer = float(atomic_params[Const.ACCEPTED_ANSWER_RESULT]) filtered_scenes = self.scif[self.scif[type_name].isin(values)][ScifConsts.SCENE_FK].unique() scenes_passing = 0 for scene in filtered_scenes: incor_tags = self.match_product_in_scene[(self.match_product_in_scene[ScifConsts.SCENE_FK] == scene) & (~(self.match_product_in_scene[MatchesConsts.COMPLIANCE_STATUS_FK] == 3))] if len(incor_tags) == 0: scenes_passing += 1 score = 100 if scenes_passing >= wanted_answer else 0 return score
def calculate_planogram(self, atomic_params): """ :param atomic_params: dict - atomic kpi line from the template :return: 100 if there is planogram, 0 otherwise. """ type_name = Converters.convert_type(atomic_params[Const.ENTITY_TYPE]) values = atomic_params[Const.ENTITY_VAL].split(',') values = map(lambda x: x.strip(), values) wanted_answer = float(atomic_params[Const.ACCEPTED_ANSWER_RESULT]) filtered_scenes = self.scif[self.scif[type_name].isin(values)]['scene_id'].unique() count = 0 for scene_id in filtered_scenes: query = CCZAQueries.getPlanogramByTemplateName(scene_id) planogram = pd.read_sql_query(query, self.rds_conn.db) if 1 in planogram['match_compliance_status'].unique().tolist(): count += 1 if count >= wanted_answer: return 100.0 return 0.0