-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcv.py
282 lines (232 loc) · 10.4 KB
/
cv.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
import json
from datetime import datetime
import requests
from pylatex import Document, Package, Section
from pylatex.utils import NoEscape, bold, escape_latex, italic
from scholarly import scholarly
def education(doc):
with doc.create(Section("Education", numbering=False)):
# Colubmia University
doc.append(bold("Columbia University, New York, NY"))
doc.append(NoEscape(r"\hfill May 2025"))
doc.append("\nM.S. in Materials Science and Engineering")
doc.append("\n- Advisor: Prof. Simon Billinge")
doc.append("\n- Department of Applied Physics and Applied Mathematics")
# Cooper Union
doc.append("\n\n")
doc.append(bold("Cooper Union, New York, NY"))
doc.append(NoEscape(r"\hfill May 2023"))
doc.append(
"\nB.E. in Chemical Engineering, "
"minors in Computer Science and Chemistry"
)
doc.append("\n- Advisor: Prof. Robert Topper")
doc.append("\n- Department of Chemical Engineering")
def interests(doc):
with doc.create(Section("Interests", numbering=False)):
doc.append(
"Data-driven materials discovery, High-throughput, Experimental "
"validation, Open-source development, DFT, Deep neural network "
"potential"
)
def awards(doc):
with doc.create(Section("Awards", numbering=False)):
doc.append(
"2023 American Chemical Society (ACS) "
"New York Outstanding Student Award\n"
)
doc.append("2023 American Institute of Chemists (AIC) Student Award\n")
doc.append("2022 Summer STEM Teaching Fellowship, Cooper Union\n")
doc.append("4-Year Half-tuition Merit Scholarship, Cooper Union\n")
doc.append("4-Year Innovator’s Merit Scholarship, Cooper Union\n")
doc.append("4-Year Corporate Scholarship, Donghwa Enterprise")
def publications(doc):
data = _load_json_file_date("data/publications.json")
with doc.create(Section("Publications", numbering=False)):
doc.append(
f"Google Scholar citations: {str(_get_gscholar_citations())} "
f"from {len(data)} peer-reviewed publications\n\n"
)
doc.append(
"‡ – these authors contributed equally to the work; "
"* – corresponding author\n\n"
)
# Enumerate and format each publication with numbering in reverse order
for i, pub in enumerate(data, start=1):
doc.append(bold(f"{i}. {pub['title']}\n"))
doc.append(", ".join(pub["authors"]) + "\n")
doc.append(
_hyperlink(
f"https://doi.org/{pub['doi']}", italic(pub["journal"])
)
)
doc.append(_add_two_extra_line_break_if_not_last_item(data, i))
def manuscripts(doc):
with doc.create(Section("Manuscripts submitted", numbering=False)):
data = _load_json_file_date("data/manuscripts.json")
for i, entry in enumerate(data, start=1):
doc.append(bold(f"{i}. {entry['title']}\n"))
doc.append(", ".join(entry["authors"]) + "\n")
if (
"doi" in entry and entry["doi"]
): # Check if DOI is present and not empty
doc.append(
_hyperlink(
f"https://doi.org/{entry['doi']}", italic(entry["doi"])
)
)
# Add a new line if not the last publication
doc.append(_add_two_extra_line_break_if_not_last_item(data, i))
def presentations(doc):
with doc.create(Section("Presentations", numbering=False)):
data = _load_json_file_date("data/presentations.json")
for i, item in enumerate(data, start=1):
doc.append(bold(f"{i}.{item['title']}\n"))
doc.append(f"{item['authors']}\n")
doc.append(NoEscape(r"\emph{" + item["conference"] + "}"))
doc.append(
f"\n {item['type']}, {item['location']}, {item['date']}. "
)
if "url" in item and item["url"]:
doc.append(_hyperlink(item["url"], "[pdf]"))
doc.append(_add_two_extra_line_break_if_not_last_item(data, i))
def software(doc):
with doc.create(Section("Research software", numbering=False)):
data = _load_json_file_date("data/software.json", sorted_by_date=False)
for i, software in enumerate(data, start=1):
doc.append(bold(f"{i}. {software['title']}"))
doc.append("\n")
doc.append(_hyperlink(software["url"], "GitHub"))
owner = software["owner"]
repo = software["repo_name"]
star_count = _get_github_repo_stars(owner, repo)
doc.append(f", {star_count} stars")
doc.append(_add_two_extra_line_break_if_not_last_item(data, i))
def coursework(doc):
with doc.create(Section("Graduate coursework", numbering=False)):
doc.append(bold("Computation: "))
doc.append(
"Deep Learning, Natural Language Processing, Introduction to "
"Numerical Analysis "
)
doc.append(bold("Materials Science & Chemical Engineering: "))
doc.append(
"Phonons, Electrons, Condensed Matter Physics I, Crystallography, "
"Electronic & Magnetic Properties of Solids, Mechanical Behaviors "
"of Materials, Materials Thermodynamics, Kinetics of "
"Transformations, Polymer Technology and Engineering"
)
def teaching(doc):
with doc.create(Section("Teaching experience", numbering=False)):
data = _load_json_file_date("data/teaching.json", sorted_by_date=False)
for i, entry in enumerate(data, start=1):
doc.append(bold(entry["title"]))
doc.append(NoEscape(r"\hfill " + entry["duration"]))
detail = f"\n{entry['symbol']}: {entry['name']} {entry['info']}"
doc.append(detail)
location_info = f"\n{entry['org']}, {entry['location']}"
doc.append(location_info)
doc.append(_add_two_extra_line_break_if_not_last_item(data, i))
def leadership(doc):
with doc.create(Section("Leadership", numbering=False)):
data = _load_json_file_date(
"data/leadership.json", sorted_by_date=False
)
for i, entry in enumerate(data, start=1):
doc.append(bold(f"{entry['title']}: "))
doc.append(f"{entry['org']}")
doc.append(NoEscape(r"\hfill " + entry["duration"]))
doc.append(_add_one_extra_line_break_if_not_last_item(data, i))
def service(doc):
with doc.create(Section("Service", numbering=False)):
data = _load_json_file_date("data/service.json")
for i, entry in enumerate(data, start=1):
formatted_date = _format_month_from_number_to_word(entry["date"])
doc.append(f"{entry['event']}, ")
doc.append(f"{entry['role']}")
doc.append(NoEscape(r"\hfill " + formatted_date))
doc.append(_add_one_extra_line_break_if_not_last_item(data, i))
###############################################################################
# Helper functions ex) Google Scholar citation, date format, GitHub star,
# add hyperlink to text, load JSON file, add extra line break
###############################################################################
def _add_one_extra_line_break_if_not_last_item(data: dict, i: int) -> str:
if i != len(data):
return "\n"
return ""
def _add_two_extra_line_break_if_not_last_item(data: dict, i: int) -> str:
if i != len(data):
return "\n\n"
return ""
def _get_github_repo_stars(owner: str, repo: str) -> int:
"Get the number of stars for a GitHub repository."
url = f"https://api.github.com/repos/{owner}/{repo}"
response = requests.get(url)
data = response.json()
return data["stargazers_count"]
def _load_json_file_date(file_path: str, sorted_by_date=True) -> dict:
"""Load a JSON file and return the data."""
with open(file_path, "r") as file:
data = json.load(file)
if sorted_by_date:
data.sort(
key=lambda x: datetime.strptime(x["date"], "%Y-%m"),
reverse=True,
)
return data
def _format_month_from_number_to_word(date_str):
# Convert date string from "YYYY-MM" to "Month YYYY"
return datetime.strptime(date_str, "%Y-%m").strftime("%B %Y")
def _hyperlink(url: str, text: str) -> NoEscape:
"""Add a hpyerlink to the Latex text."""
text = escape_latex(text)
return NoEscape(r"\href{" + url + "}{" + text + "}")
def _get_gscholar_citations(author_id="L07HlVsAAAAJ") -> int:
"""Get the number of citations from Google Scholar."""
author = scholarly.search_author_id(author_id)
return author["citedby"]
###############################################################################
# Entry when you run python cv.py
###############################################################################
if __name__ == "__main__":
"""PLEASE READ BEFORE RUNNING THE SCRIPT:
(1) The education, interests, awards sections are hard-coded to provide
greater flexibility. Other sections are loaded from JSON files under data.
(2) To format automatically run the following command:
pre-commit run --all-files
(3) Visit README https://github.com/bobleesj/python-cv-template for the
latest CV template and dynamic rendering instructions.
"""
# Set the geometry options for the document
geometry_options = {"tmargin": "0.5in", "lmargin": "1.0in"}
doc = Document("basic", geometry_options=geometry_options)
# Required to use hyperlinks when rendered as a pdf
doc.packages.append(Package("hyperref"))
doc.packages.append(Package("microtype"))
# Header (Name, email, phone number)
doc.append(
NoEscape(
r"\moveleft.5\hoffset\centerline{\large\bf Sangjoon (Bob) Lee}"
)
)
doc.append(
NoEscape(
r"\moveleft.5\hoffset\centerline{\small [email protected] | "
r"\href{https://bobleesj.github.io/}{bobleesj.github.io} "
r"| (404) 747-2468}"
)
)
# Sections
education(doc)
interests(doc)
awards(doc)
manuscripts(doc)
publications(doc)
presentations(doc)
software(doc)
coursework(doc)
teaching(doc)
leadership(doc)
service(doc)
# Generate the PDF
doc.generate_pdf("Sangjoon_Lee_CV", clean_tex=True)