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