Flatten categories into individual library entries for table display. Entries appearing in multiple categories are merged into a single entry with lists of categories and groups.
(
categories: list[ParsedSection],
groups: list[ParsedGroup],
)
| 455 | |
| 456 | |
| 457 | def extract_entries( |
| 458 | categories: list[ParsedSection], |
| 459 | groups: list[ParsedGroup], |
| 460 | ) -> list[TemplateEntry]: |
| 461 | """Flatten categories into individual library entries for table display. |
| 462 | |
| 463 | Entries appearing in multiple categories are merged into a single entry |
| 464 | with lists of categories and groups. |
| 465 | """ |
| 466 | cat_to_group = {cat["name"]: group["name"] for group in groups for cat in group["categories"]} |
| 467 | |
| 468 | seen: dict[tuple[str, str], TemplateEntry] = {} # (url, name) -> entry |
| 469 | entries: list[TemplateEntry] = [] |
| 470 | for cat in categories: |
| 471 | group_name = cat_to_group.get(cat["name"], "Other") |
| 472 | for entry in cat["entries"]: |
| 473 | key = (entry["url"], entry["name"]) |
| 474 | existing = seen.get(key) |
| 475 | if existing is None: |
| 476 | existing = TemplateEntry( |
| 477 | name=entry["name"], |
| 478 | url=entry["url"], |
| 479 | description=entry["description"], |
| 480 | categories=[], |
| 481 | groups=[], |
| 482 | subcategories=[], |
| 483 | stars=None, |
| 484 | owner=None, |
| 485 | last_commit_at=None, |
| 486 | source_type=detect_source_type(entry["url"]), |
| 487 | also_see=entry["also_see"], |
| 488 | ) |
| 489 | seen[key] = existing |
| 490 | entries.append(existing) |
| 491 | if cat["name"] not in existing["categories"]: |
| 492 | existing["categories"].append(cat["name"]) |
| 493 | if group_name not in existing["groups"]: |
| 494 | existing["groups"].append(group_name) |
| 495 | subcat = entry["subcategory"] |
| 496 | if subcat: |
| 497 | scoped = f"{cat['name']} > {subcat}" |
| 498 | if not any(s["value"] == scoped for s in existing["subcategories"]): |
| 499 | sub_slug = slugify(subcat) |
| 500 | existing["subcategories"].append( |
| 501 | TemplateSubcategory( |
| 502 | name=subcat, |
| 503 | value=scoped, |
| 504 | slug=sub_slug, |
| 505 | url=f"/categories/{cat['slug']}/{sub_slug}/", |
| 506 | ) |
| 507 | ) |
| 508 | return entries |
| 509 | |
| 510 | |
| 511 | def build(repo_root: Path) -> None: |