|
1 | 1 | from collections import defaultdict |
2 | 2 | from typing import Dict, List |
3 | 3 |
|
4 | | -import bibtexparser |
| 4 | +from pybtex.database import parse_file |
| 5 | +from pybtex.scanner import PybtexSyntaxError |
| 6 | +from pylatexenc.latex2text import LatexNodes2Text |
5 | 7 |
|
6 | 8 | # Define the categories to be printed in the README |
7 | 9 | VALID_CATEGORIES = ["overview", "software", "paper", "uncategorized"] |
8 | 10 |
|
9 | | -README_HEADER = """ |
| 11 | +README_INTRO = """ |
10 | 12 | Welcome to the Awesome Amortized Inference repository! |
11 | 13 | This is a curated list of resources, including overviews, software, papers, and other resources related to amortized inference. |
12 | 14 | Feel free to explore the entries below and use the provided BibTeX information for citation purposes. |
13 | | -Contributioons always welcome, this shall be a community-driven project. |
14 | | -Contribution guide will follow ASAP. |
| 15 | +Contributions are always welcome, this is a community-driven project. |
15 | 16 | """ |
16 | 17 |
|
17 | 18 |
|
18 | 19 | # Define Entry class |
19 | 20 | class Entry: |
20 | | - def __init__(self, title: str, authors: str, url: str, category: str, bibtex: str): |
| 21 | + def __init__( |
| 22 | + self, |
| 23 | + title: str, |
| 24 | + authors: str, |
| 25 | + url: str, |
| 26 | + category: str, |
| 27 | + awesome_fields: Dict[str, str], |
| 28 | + bibtex: str, |
| 29 | + ): |
21 | 30 | self.title = title |
22 | 31 | self.authors = authors |
23 | 32 | self.url = url |
24 | 33 | self.category = category |
| 34 | + self.awesome_fields = awesome_fields |
25 | 35 | self.bibtex = bibtex |
26 | 36 |
|
27 | 37 | @classmethod |
28 | | - def from_bibtex(cls, entry: dict) -> "Entry": |
29 | | - title = entry.get("title", "No title").replace("{", "").replace("}", "") |
30 | | - authors = entry.get("author", "No author").replace(" and ", ", ") |
31 | | - url = entry.get("url", "") |
32 | | - category = entry.get("category", "uncategorized").lower() |
| 38 | + def from_bibtex(cls, entry) -> "Entry": |
| 39 | + title = entry.fields.get("title", "No title").replace("{", "").replace("}", "") |
| 40 | + authors = ( |
| 41 | + ", ".join( |
| 42 | + cls.person_to_first_last(person) |
| 43 | + for person in entry.persons.get("author", []) |
| 44 | + ) |
| 45 | + or "No author" |
| 46 | + ) |
| 47 | + url = entry.fields.get("url", "") |
| 48 | + category = entry.fields.get("category", "uncategorized").lower() |
| 49 | + |
| 50 | + # get all fields that start with 'awesome-' and save them in a separate field |
| 51 | + awesome_fields = { |
| 52 | + key[len("awesome-") :]: value |
| 53 | + for key, value in entry.fields.items() |
| 54 | + if key.startswith("awesome-") |
| 55 | + } |
| 56 | + |
| 57 | + if category not in VALID_CATEGORIES: |
| 58 | + print( |
| 59 | + f"Warning: Unrecognized category '{category}' for entry '{title}'. Categorizing as 'uncategorized'." |
| 60 | + ) |
| 61 | + category = "uncategorized" |
33 | 62 | bibtex = cls.format_bibtex(entry) |
34 | | - return cls(title, authors, url, category, bibtex) |
| 63 | + return cls(title, authors, url, category, awesome_fields, bibtex) |
| 64 | + |
| 65 | + @staticmethod |
| 66 | + def person_to_first_last(person) -> str: |
| 67 | + name_parts = [ |
| 68 | + " ".join(part) |
| 69 | + for part in [ |
| 70 | + person.first_names, |
| 71 | + person.middle_names, |
| 72 | + person.prelast_names, |
| 73 | + person.last_names, |
| 74 | + person.lineage_names, |
| 75 | + ] |
| 76 | + if part |
| 77 | + ] |
| 78 | + name = " ".join(name_parts) |
| 79 | + |
| 80 | + return LatexNodes2Text().latex_to_text(name) |
35 | 81 |
|
36 | 82 | @staticmethod |
37 | | - def format_bibtex(entry: dict) -> str: |
38 | | - bibtex_type = entry.get("ENTRYTYPE", "misc") |
39 | | - bibtex_key = entry.get("ID", "unknown") |
| 83 | + def format_bibtex(entry) -> str: |
| 84 | + bibtex_type = entry.type if entry.type else "misc" |
| 85 | + bibtex_key = entry.key if entry.key else "unknown" |
40 | 86 | bibtex_fields = [ |
41 | | - f" {key} = {{{value}}}" |
42 | | - for key, value in entry.items() |
43 | | - if key not in ["ENTRYTYPE", "ID"] |
| 87 | + f"{key} = {{{value}}}" |
| 88 | + for key, value in entry.fields.items() |
| 89 | + if not key.startswith("awesome-") # exclude awesome fields from BibTeX |
44 | 90 | ] |
| 91 | + if "author" in entry.persons: |
| 92 | + bibtex_fields.append( |
| 93 | + f"author = {{{' and '.join(str(person) for person in entry.persons.get('author', []))}}}" |
| 94 | + ) |
45 | 95 | bibtex_str = ( |
46 | | - f"@{bibtex_type}{{{bibtex_key},\n " + ",\n ".join(bibtex_fields) + "\n}" |
| 96 | + f"@{bibtex_type}{{{bibtex_key},\n " + ",\n ".join(bibtex_fields) + "\n}" |
47 | 97 | ) |
48 | 98 | return bibtex_str |
49 | 99 |
|
50 | 100 | def to_string(self) -> str: |
51 | | - entry_str = f"- **{self.title}**. {self.authors}." |
52 | | - if self.url: |
53 | | - entry_str += f" [[Link]]({self.url})" |
54 | | - entry_str += f" <details><summary>Show BibTeX</summary><pre><code>{self.bibtex}</code></pre></details>" |
55 | | - entry_str += "\n" |
| 101 | + entry_str = f"- **{self.title}**." |
| 102 | + if self.awesome_fields: |
| 103 | + entry_str += " " |
| 104 | + for key, value in self.awesome_fields.items(): |
| 105 | + entry_str += f"[[{key.capitalize()}]]({value}) " |
| 106 | + entry_str += f"<br /> {self.authors}" |
| 107 | + entry_str += f""" |
| 108 | + <details> |
| 109 | + <summary>Show BibTeX</summary> |
| 110 | + <pre><code> |
| 111 | + {self.bibtex} |
| 112 | + </code> |
| 113 | + </pre></details> |
| 114 | +""" |
| 115 | + # entry_str += "<br />" |
56 | 116 | return entry_str |
57 | 117 |
|
58 | 118 |
|
59 | 119 | def organize_entries(bib_database) -> Dict[str, List[Entry]]: |
60 | 120 | entries_by_category: Dict[str, List[Entry]] = defaultdict(list) |
61 | | - for entry in bib_database.entries: |
| 121 | + for entry_key, entry in bib_database.entries.items(): |
62 | 122 | entry_obj = Entry.from_bibtex(entry) |
63 | 123 | entries_by_category[entry_obj.category].append(entry_obj) |
64 | 124 | return entries_by_category |
65 | 125 |
|
66 | 126 |
|
67 | 127 | def create_readme(entries_by_category: Dict[str, List[Entry]]) -> str: |
68 | 128 | readme_content = "# Awesome Amortized Inference\n\n" |
69 | | - readme_content += README_HEADER |
| 129 | + readme_content += README_INTRO |
70 | 130 |
|
71 | 131 | for category in VALID_CATEGORIES: |
72 | 132 | if category in entries_by_category: |
73 | 133 | readme_content += f"## {category.capitalize()}\n\n" |
74 | 134 | readme_content += "\n".join( |
75 | 135 | [entry.to_string() for entry in entries_by_category[category]] |
76 | 136 | ) |
77 | | - readme_content += "\n" |
78 | 137 | return readme_content |
79 | 138 |
|
80 | 139 |
|
81 | 140 | def main(): |
82 | 141 | try: |
83 | | - with open("data.bib") as bibtex_file: |
84 | | - bib_database = bibtexparser.load(bibtex_file) |
| 142 | + bib_database = parse_file("data.bib") |
85 | 143 | except FileNotFoundError: |
86 | 144 | print("The data.bib file was not found.") |
87 | 145 | exit(1) |
| 146 | + except PybtexSyntaxError as e: |
| 147 | + print(f"Error parsing BibTeX file: {e}") |
| 148 | + exit(1) |
88 | 149 |
|
89 | 150 | entries_by_category = organize_entries(bib_database) |
90 | 151 | readme_content = create_readme(entries_by_category) |
|
0 commit comments