Skip to content

Commit 42827eb

Browse files
jeremymanningclaude
andcommitted
Insert people at their sorted position when onboarding and offboarding
Onboarding put new CV entries at the top of their list and appended lab-manual entries at the bottom; offboarding put new alumni rows at the top of the spreadsheet and appended lab-manual alumni at the end. All of them now go through people_order.insert_item_sorted / order_key: newest start year first, then name, the CV's order. Offboarded alumni rows also take the CV's spelling (McDonald, not the title-cased Mcdonald). Sorts the graduate and other non-undergrad alumni lists into that order in the CV, people.xlsx and the lab manual, and adds tests that the real CV lists stay sorted. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N4s2iBPpdWdLNyRRcGDXUt
1 parent 5aa92be commit 42827eb

15 files changed

Lines changed: 282 additions & 28 deletions

‎CLAUDE.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ Each `build_*.py` follows the same pattern:
7676

7777
### People: three sources, one order
7878

79-
People appear in `data/people.xlsx`, `documents/JRM_CV.tex` and `lab-manual/lab_manual.tex`, and all three should agree on names, years and order. `build_people.py` orders the undergrad alumni on `people.html` by their order in the CV (`parse_cv_undergrad_order`), so the CV's order is the reference: start year newest first, then name. `reconcile_people.py` checks names only, not years or order.
79+
People appear in `data/people.xlsx`, `documents/JRM_CV.tex` and `lab-manual/lab_manual.tex`, and all three should agree on names, years and order. `build_people.py` orders the undergrad alumni on `people.html` by their order in the CV (`parse_cv_undergrad_order`), so the CV's order is the reference: start year newest first, then name. `scripts/people_order.py` holds that rule; onboarding and offboarding insert every CV, lab-manual and alumni-sheet entry at its sorted position through it. `reconcile_people.py` checks names only, not years or order.
8080

8181
### Key Directories
8282

‎data/people.xlsx‎

11 Bytes
Binary file not shown.

‎documents/JRM_CV.html‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -471,20 +471,20 @@ <h4>Postdoctoral Advisees</h4>
471471
<h4>Graduate Advisees</h4>
472472
<ol reversed start="11">
473473
<li>Claudia Gonciulea (Doctoral student; 2025 – )</li>
474+
<li>Mark Taylor (Masters student, Quantitative Biomedical Sciences; 2021)</li>
474475
<li>Paxton Fitzpatrick (Doctoral student; 2021 – )</li>
475476
<li>Xinming Xu (Doctoral student; 2021 – )</li>
476-
<li>Mark Taylor (Masters student, Quantitative Biomedical Sciences; 2021)</li>
477477
<li>Caroline Lee (Doctoral student; 2019 – 2021)</li>
478-
<li>Max Bluestone (Masters student, Quantitative Biomedical
479-
Sciences; 2018 – 2020)</li>
480478
<li>Deepanshi Shokeen (Masters student, Quantitative Biomedical
479+
Sciences; 2018 – 2020)</li>
480+
<li>Max Bluestone (Masters student, Quantitative Biomedical
481481
Sciences; 2018 – 2020)</li>
482482
<li>Kirsten Ziman (Doctoral student; 2017 – 2022; current position: Postdoctoral researcher at Princeton University)</li>
483+
<li>Hanli Li (Masters student, Computer Science; co-advised with Qiang
484+
Liu; 2016)</li>
483485
<li>Lucy Owen (Doctoral student; 2016 – 2021; current position: Assistant Professor at University of Montana)</li>
484486
<li>Tom Hao Chang (Masters student, Computer Science; co-advised with
485487
Qiang Liu; 2016 – 2017; current position: Robinhood)</li>
486-
<li>Hanli Li (Masters student, Computer Science; co-advised with Qiang
487-
Liu; 2016)</li>
488488
</ol>
489489
<h4>Thesis Committees</h4>
490490
<ol reversed start="22">

‎documents/JRM_CV.pdf‎

-10 Bytes
Binary file not shown.

‎documents/JRM_CV.tex‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -671,20 +671,20 @@ \subsection*{Mentorship (selected)}
671671
\textit{Graduate Advisees}:
672672
\begin{etaremune}
673673
\item Claudia Gonciulea (Doctoral student; 2025 -- )
674+
\item Mark Taylor (Masters student, Quantitative Biomedical Sciences; 2021)
674675
\item Paxton Fitzpatrick (Doctoral student; 2021 -- )
675676
\item Xinming Xu (Doctoral student; 2021 -- )
676-
\item Mark Taylor (Masters student, Quantitative Biomedical Sciences; 2021)
677677
\item Caroline Lee (Doctoral student; 2019 -- 2021)
678-
\item Max Bluestone (Masters student, Quantitative Biomedical
679-
Sciences; 2018 -- 2020)
680678
\item Deepanshi Shokeen (Masters student, Quantitative Biomedical
681679
Sciences; 2018 -- 2020)
680+
\item Max Bluestone (Masters student, Quantitative Biomedical
681+
Sciences; 2018 -- 2020)
682682
\item Kirsten Ziman (Doctoral student; 2017 -- 2022; current position: Postdoctoral researcher at Princeton University)
683+
\item Hanli Li (Masters student, Computer Science; co-advised with Qiang
684+
Liu; 2016)
683685
\item Lucy Owen (Doctoral student; 2016 -- 2021; current position: Assistant Professor at University of Montana)
684686
\item Tom Hao Chang (Masters student, Computer Science; co-advised with
685687
Qiang Liu; 2016 -- 2017; current position: Robinhood)
686-
\item Hanli Li (Masters student, Computer Science; co-advised with Qiang
687-
Liu; 2016)
688688
\end{etaremune}
689689

690690
\textit{Thesis Committees}:

‎lab-manual‎

‎people.html‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,12 +273,12 @@ <h3>Postdoctoral Researchers</h3>
273273
<h3>Graduate Students</h3>
274274
<p>Mark Taylor (2021)<br>
275275
Caroline Lee (2019-2021)<br>
276-
Max Bluestone (2018-2020)<br>
277276
Deepanshi Shokeen (2018-2020)<br>
277+
Max Bluestone (2018-2020)<br>
278278
Kirsten Ziman (2017-2022; now at <a href="https://www.princeton.edu" target="_blank">Princeton University</a>)<br>
279+
Hanli Li (2016; now at <a href="https://www.umt.edu/" target="_blank">University of Montana</a>)<br>
279280
Lucy Owen (2016-2021; now at <a href="https://www.umt.edu/" target="_blank">University of Montana</a>)<br>
280-
Tom Hao Chang (2016-2017)<br>
281-
Hanli Li (2016)</p>
281+
Tom Hao Chang (2016-2017)</p>
282282

283283
<h3>Lab Managers</h3>
284284
<p><a href="http://paxtonfitzpatrick.github.io" target="_blank">Paxton Fitzpatrick</a> (2018-2021; now a <a href="https://context-lab.com" target="_blank">CDL grad student!</a>)<br>

‎scripts/offboard_member.py‎

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import openpyxl
2828

2929
from onboard_member import rebuild_pages
30+
from people_order import order_key
3031

3132

3233
def get_project_root() -> Path:
@@ -105,8 +106,18 @@ def prompt_for_selection(matches: List[Dict[str, Any]]) -> Optional[Dict[str, An
105106
print("Please enter a valid number")
106107

107108

108-
def move_to_alumni(xlsx_path: Path, member: Dict[str, Any], years_string: str) -> bool:
109-
"""Move member to alumni sheet with the given years string (e.g., '2024-2026' or '2026')."""
109+
def move_to_alumni(
110+
xlsx_path: Path,
111+
member: Dict[str, Any],
112+
years_string: str,
113+
display_name: Optional[str] = None,
114+
) -> bool:
115+
"""Move member to alumni sheet with the given years string (e.g., '2024-2026' or '2026').
116+
117+
The row goes at its sorted position (newest start year first, then name;
118+
see people_order.py) under display_name -- the CV's spelling -- falling
119+
back to a title-cased spreadsheet name.
120+
"""
110121
if member_already_alumni(xlsx_path, member["name"]):
111122
print(
112123
f" {member['name']} is already in alumni list, skipping spreadsheet update"
@@ -119,17 +130,37 @@ def move_to_alumni(xlsx_path: Path, member: Dict[str, Any], years_string: str) -
119130
members_sheet.delete_rows(member["_row_idx"])
120131

121132
alumni_sheet = wb["alumni_undergrads"]
133+
name = display_name or member["name"].title()
134+
key = order_key(name, years_string[:4])
122135

123-
alumni_sheet.insert_rows(2)
124-
alumni_sheet.cell(row=2, column=1, value=member["name"].title())
125-
alumni_sheet.cell(row=2, column=2, value=years_string)
136+
row = alumni_sheet.max_row + 1
137+
for r in range(2, alumni_sheet.max_row + 1):
138+
existing, years = (alumni_sheet.cell(r, c).value for c in (1, 2))
139+
start = re.search(r"\d{4}", str(years or ""))
140+
if existing and start and order_key(existing, start.group(0)) > key:
141+
row = r
142+
break
143+
144+
alumni_sheet.insert_rows(row)
145+
alumni_sheet.cell(row=row, column=1, value=name)
146+
alumni_sheet.cell(row=row, column=2, value=years_string)
126147

127148
wb.save(xlsx_path)
128149
wb.close()
129150
print(f" Moved {member['name']} to alumni_undergrads with years {years_string}")
130151
return True
131152

132153

154+
def get_cv_display_name(cv_path: Path, member_name: str) -> Optional[str]:
155+
"""The member's name as the CV spells it ('Miles McDonald'), without the
156+
thesis asterisk; the spreadsheet stores names lowercase."""
157+
content = cv_path.read_text(encoding="utf-8")
158+
match = re.search(
159+
r"\\item\s+(" + re.escape(member_name) + r")\*?\s*\(", content, re.IGNORECASE
160+
)
161+
return match.group(1) if match else None
162+
163+
133164
def get_cv_start_year(cv_path: Path, member_name: str) -> Optional[str]:
134165
"""Extract the start year from the CV entry for a member."""
135166
content = cv_path.read_text(encoding="utf-8")
@@ -270,7 +301,9 @@ def offboard_member(
270301
else:
271302
years_string = end_year
272303

273-
move_to_alumni(xlsx_path, member, years_string)
304+
move_to_alumni(
305+
xlsx_path, member, years_string, get_cv_display_name(cv_path, member["name"])
306+
)
274307
update_cv_entry(cv_path, member["name"], end_year)
275308

276309
# Update lab-manual (best-effort; failure doesn't block offboarding)

‎scripts/onboard_member.py‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
# same people issue #14 was about. The hosted service costs the lab nothing,
4545
# needs no download, and answers in about a second.
4646
from dartmouth_chat import DartmouthChatError, chat as dartmouth_chat
47+
from people_order import insert_item_sorted
4748

4849
# =============================================================================
4950
# Constants
@@ -1090,8 +1091,10 @@ def add_to_cv(cv_path: Path, name: str, role: str, year: str) -> bool:
10901091
return False
10911092

10921093
entry = section["entry"].format(name=name, year=year)
1093-
insert_pos = match.end()
1094-
new_content = content[:insert_pos] + f"\n {entry}" + content[insert_pos:]
1094+
list_start = match.end()
1095+
list_end = content.index("\\end{etaremune}", list_start)
1096+
items = insert_item_sorted(content[list_start:list_end], entry, name, year)
1097+
new_content = content[:list_start] + items + content[list_end:]
10951098

10961099
cv_path.write_text(new_content, encoding="utf-8")
10971100
print(f" Added {name} to CV under {section['label']}")

‎scripts/parse_lab_manual.py‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import subprocess
99
from pathlib import Path
1010

11+
from people_order import insert_item_sorted
12+
1113

1214
def parse_members_chapter(tex_path):
1315
"""Extract all member/alumni entries from lab_manual.tex.
@@ -365,9 +367,9 @@ def add_member_to_lab_manual(tex_path, name, role, start_year):
365367
if _has_item(match.group(2), name):
366368
return False
367369

368-
before = match.group(1) + match.group(2).rstrip()
370+
items = insert_item_sorted(match.group(2), new_item, name, start_year)
369371
new_section = (
370-
section[:match.start()] + before + '\n' + new_item + '\n'
372+
section[:match.start()] + match.group(1) + items
371373
+ match.group(3) + section[match.end():]
372374
)
373375

@@ -441,10 +443,12 @@ def move_member_to_alumni(tex_path, name, end_year):
441443
if not alumni_block:
442444
new_alumni = _insert_role_block(alumni_section, role_category, alumni_item)
443445
else:
444-
before = alumni_block.group(1) + alumni_block.group(2).rstrip()
446+
items = insert_item_sorted(
447+
alumni_block.group(2), alumni_item, manual_name, start_year
448+
)
445449
new_alumni = (
446-
alumni_section[:alumni_block.start()] + before + '\n' + alumni_item
447-
+ '\n' + alumni_block.group(3) + alumni_section[alumni_block.end():]
450+
alumni_section[:alumni_block.start()] + alumni_block.group(1) + items
451+
+ alumni_block.group(3) + alumni_section[alumni_block.end():]
448452
)
449453

450454
content = content[:alumni_start] + new_alumni + content[alumni_end:]

0 commit comments

Comments
 (0)