def build_topics_data()

in kitsune/wiki/utils.py [0:0]


def build_topics_data(request: HttpRequest, product: Product, topics: list[Topic]) -> list[dict]:
    """Build topics_data for use in topic cards
    Args:
        request: HttpRequest - The current request
        product: Product - The product to get topics for
        topics: list[Topic] - List of topics to process
    Returns:
        list[dict]: List of topic data dictionaries containing:
            - topic: Topic object
            - topic_url: URL to topic's documents
            - title: Topic title
            - total_articles: Combined count of main and fallback articles
            - image_url: Topic image URL
            - documents: Up to 3 documents to display
    """
    topics_data: list[dict] = []

    featured_articles = get_featured_articles(product, locale=request.LANGUAGE_CODE, topics=topics)

    # Get both main and fallback documents from the faceted search
    main_docs_data, fallback_docs_data = documents_for(
        request.user, request.LANGUAGE_CODE, topics=topics, products=[product]
    )

    main_doc_ids = {doc["id"] for doc in main_docs_data}
    fallback_doc_ids = {doc["id"] for doc in (fallback_docs_data or [])}

    all_documents = (
        Document.objects.filter(id__in=main_doc_ids | fallback_doc_ids)
        .select_related("parent")
        .prefetch_related(
            "topics",
            "parent__topics",
        )
    )

    # topic_id -> (main_docs, fallback_docs) mapping
    topic_docs_map: dict[int, tuple[list[Document], list[Document]]] = {
        topic.id: ([], []) for topic in topics
    }
    doc_topics_map: dict[int, list[Topic]] = {}

    for doc in all_documents:
        doc_topics = set(doc.parent.topics.all()) if doc.parent else set(doc.topics.all())

        doc_topics_map[doc.id] = list(doc_topics)
        for topic in doc_topics:
            if topic.id in topic_docs_map:
                main_list, fallback_list = topic_docs_map[topic.id]
                target_list = main_list if doc.id in main_doc_ids else fallback_list
                target_list.append(doc)

    for topic in topics:
        main_topic_docs, fallback_topic_docs = topic_docs_map[topic.id]

        if not main_topic_docs and not fallback_topic_docs:
            continue

        topic_featured = [
            doc
            for doc in featured_articles
            if doc.id in doc_topics_map and any(t.id == topic.id for t in doc_topics_map[doc.id])
        ]

        # Get remaining main documents excluding featured ones
        remaining_docs = (doc for doc in main_topic_docs if doc not in topic_featured)

        # First try to get documents from featured and main docs
        main_docs = list(islice(chain(topic_featured, remaining_docs), 3))

        # Fall back to fallback documents only if no main documents exist
        documents_to_show = main_docs if main_docs else list(islice(fallback_topic_docs, 3))

        topic_data = {
            "topic": topic,
            "topic_url": reverse("products.documents", args=[product.slug, topic.slug]),
            "title": topic.title,
            "total_articles": len(main_topic_docs) + len(fallback_topic_docs),
            "image_url": topic.image_url,
            "documents": documents_to_show,
        }
        topics_data.append(topic_data)

    return topics_data