Get concept which have same changes and have to be recalculated

        Args:
            users (list of users or user): users whose user stats we are interesting in
            lang (str): language of used concepts
            concepts (Optional[list of concepts]): list of primary keys of concepts or concepts
                Defaults to None meaning all concepts.

        Returns:
            dict: user -> set of concepts (int) - in case of list of users
            list of stats (str) - in case of one user

def get_concepts_to_recalculate(self, users, lang, concepts=None):
        """
        Get concept which have same changes and have to be recalculated

        Args:
            users (list of users or user): users whose user stats we are interesting in
            lang (str): language of used concepts
            concepts (Optional[list of concepts]): list of primary keys of concepts or concepts
                Defaults to None meaning all concepts.

        Returns:
            dict: user -> set of concepts (int) - in case of list of users
            list of stats (str) - in case of one user
        """
        only_one_user = False
        if not isinstance(users, list):
            only_one_user = True
            users = [users]

        mapping = self.get_item_concept_mapping(lang)
        current_user_stats = defaultdict(lambda: {})
        user_stats_qs = UserStat.objects.filter(user__in=users, stat="answer_count")     # we need only one type
        if concepts is not None:
            user_stats_qs = user_stats_qs.filter(concept__in=concepts)
        for user_stat in user_stats_qs:
            current_user_stats[user_stat.user_id][user_stat.concept_id] = user_stat

        concepts_to_recalculate = defaultdict(lambda: set())
        for user, item, time in Answer.objects.filter(user__in=users)\
                .values_list("user_id", "item").annotate(Max("time")):
            if item not in mapping:
                # in reality this should by corner case, so it is efficient to not filter Answers
                continue    # item is not in concept
            time_expiration_lower_bound = get_config('proso_models', 'knowledge_overview.time_shift_hours', default=4)
            time_expiration_factor = get_config('proso_models', 'knowledge_overview.time_expiration_factor', default=2)
            for concept in mapping[item]:
                if user in current_user_stats and concept in current_user_stats[user] \
                        and current_user_stats[user][concept].time > time:
                    if not self.has_time_expired(current_user_stats[user][concept].time, time, time_expiration_lower_bound, time_expiration_factor):
                        continue  # cache is up to date

                if concepts is None or concept in ([c.pk for c in concepts] if type(concepts[0]) == Concept else Concept):
                    concepts_to_recalculate[user].add(concept)

        if only_one_user:
            return concepts_to_recalculate[users[0]]
        return concepts_to_recalculate