Recalculated given concepts for given users

        Args:
            concepts (dict): user id (int -> set of concepts to recalculate)
            lang(Optional[str]): language used to get items in all concepts (cached).
                Defaults to None, in that case are get items only in used concepts

def recalculate_concepts(self, concepts, lang=None):
        """
        Recalculated given concepts for given users

        Args:
            concepts (dict): user id (int -> set of concepts to recalculate)
            lang(Optional[str]): language used to get items in all concepts (cached).
                Defaults to None, in that case are get items only in used concepts
        """
        if len(concepts) == 0:
            return

        if lang is None:
            items = Concept.objects.get_concept_item_mapping(concepts=Concept.objects.filter(pk__in=set(flatten(concepts.values()))))
        else:
            items = Concept.objects.get_concept_item_mapping(lang=lang)

        environment = get_environment()
        mastery_threshold = get_mastery_trashold()
        for user, concepts in concepts.items():
            all_items = list(set(flatten([items[c] for c in concepts])))
            answer_counts = environment.number_of_answers_more_items(all_items, user)
            correct_answer_counts = environment.number_of_correct_answers_more_items(all_items, user)
            predictions = dict(list(zip(all_items, get_predictive_model().
                                        predict_more_items(environment, user, all_items, time=get_time_for_knowledge_overview()))))
            new_user_stats = []
            stats_to_delete_condition = Q()
            for concept in concepts:
                answer_aggregates = Answer.objects.filter(user=user, item__in=items[concept]).aggregate(
                    time_spent=Sum("response_time"),
                    sessions=Count("session", True),
                    time_first=Min("time"),
                    time_last=Max("time"),
                )
                stats = {
                    "answer_count": sum(answer_counts[i] for i in items[concept]),
                    "correct_answer_count": sum(correct_answer_counts[i] for i in items[concept]),
                    "item_count": len(items[concept]),
                    "practiced_items_count": sum([answer_counts[i] > 0 for i in items[concept]]),
                    "mastered_items_count": sum([predictions[i] >= mastery_threshold for i in items[concept]]),
                    "prediction": sum([predictions[i] for i in items[concept]]) / len(items[concept]),
                    "time_spent": answer_aggregates["time_spent"] / 1000,
                    "session_count": answer_aggregates["sessions"],
                    "time_first": answer_aggregates["time_first"].timestamp(),
                    "time_last": answer_aggregates["time_last"].timestamp(),
                }
                stats_to_delete_condition |= Q(user=user, concept=concept)
                for stat_name, value in stats.items():
                    new_user_stats.append(UserStat(user_id=user, concept_id=concept, stat=stat_name, value=value))
            self.filter(stats_to_delete_condition).delete()
            self.bulk_create(new_user_stats)