Skip to content

Commit e1f84fd

Browse files
Merge pull request #2327 from gitautoai/wes
Sync get_first_name with website parseName and add display_name_override support
2 parents 8af75ad + e958cf4 commit e1f84fd

File tree

3 files changed

+292
-84
lines changed

3 files changed

+292
-84
lines changed

services/resend/get_first_name.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,33 @@
1+
import re
2+
3+
4+
# Cross-ref: website/utils/parse-name.ts
15
def get_first_name(user_name: str) -> str:
26
if not user_name:
37
return "there"
48

5-
# Split by spaces and take the first part
6-
parts = user_name.strip().split()
7-
if parts:
8-
return parts[0]
9+
# Strip parenthesized content e.g. "John (Johnny) Doe" → "John Doe"
10+
cleaned = re.sub(r"\s*\([^)]*\)\s*", " ", user_name)
11+
parts = cleaned.strip().split()
12+
if not parts:
13+
return "there"
14+
15+
# Skip title prefixes like "Dr." or initials like "L." when followed by an actual name
16+
idx = 1 if parts[0].endswith(".") and len(parts) > 1 else 0
17+
first = parts[idx]
18+
19+
# Handle dot-separated tokens (e.g. "Frater.nul" → "Frater", "M.Rama" → "Rama")
20+
dot_parts = first.split(".")
21+
if len(dot_parts) > 1 and dot_parts[1]:
22+
first = max(dot_parts, key=len)
23+
24+
# Single-token hyphenated names are firstname-lastname (e.g. "cuong-tran" → "cuong")
25+
# Multi-token keeps hyphens (e.g. "Mary-Jane Watson" → "Mary-Jane")
26+
if len(parts) == 1 and "-" in first:
27+
first = first.split("-")[0]
28+
29+
# Names containing digits are likely GitHub usernames (e.g. "St119848"), not real names
30+
if not first or re.search(r"\d", first):
31+
return "there"
932

10-
return "there"
33+
return first[0].upper() + first[1:]

services/resend/test_get_first_name.py

Lines changed: 254 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -3,92 +3,269 @@
33
from services.resend.get_first_name import get_first_name
44

55

6-
def test_get_first_name_with_empty_string():
7-
"""Test that empty string returns 'there'."""
8-
result = get_first_name("")
9-
assert result == "there"
10-
11-
12-
def test_get_first_name_with_single_name():
13-
"""Test that single name returns the name itself."""
14-
result = get_first_name("John")
15-
assert result == "John"
16-
17-
18-
def test_get_first_name_with_full_name():
19-
"""Test that full name returns only the first name."""
20-
result = get_first_name("John Doe")
21-
assert result == "John"
22-
23-
24-
def test_get_first_name_with_multiple_names():
25-
"""Test that multiple names returns only the first name."""
26-
result = get_first_name("John Michael Doe")
27-
assert result == "John"
28-
29-
30-
def test_get_first_name_with_leading_whitespace():
31-
"""Test that leading whitespace is stripped before processing."""
32-
result = get_first_name(" John Doe")
33-
assert result == "John"
34-
35-
36-
def test_get_first_name_with_trailing_whitespace():
37-
"""Test that trailing whitespace is stripped before processing."""
38-
result = get_first_name("John Doe ")
39-
assert result == "John"
40-
41-
42-
def test_get_first_name_with_surrounding_whitespace():
43-
"""Test that surrounding whitespace is stripped before processing."""
44-
result = get_first_name(" John Doe ")
45-
assert result == "John"
46-
47-
48-
def test_get_first_name_with_multiple_spaces_between_names():
49-
"""Test that multiple spaces between names are handled correctly."""
50-
result = get_first_name("John Doe")
51-
assert result == "John"
52-
53-
54-
def test_get_first_name_with_only_whitespace():
55-
"""Test that string with only whitespace returns 'there'."""
56-
result = get_first_name(" ")
57-
assert result == "there"
58-
59-
60-
def test_get_first_name_with_tabs_and_newlines():
61-
"""Test that tabs and newlines are treated as whitespace."""
62-
result = get_first_name("\t\nJohn\t\nDoe\t\n")
63-
assert result == "John"
64-
65-
66-
def test_get_first_name_with_special_characters():
67-
"""Test that names with special characters are handled correctly."""
68-
result = get_first_name("Jean-Pierre Dupont")
69-
assert result == "Jean-Pierre"
70-
71-
726
@pytest.mark.parametrize(
737
"input_name,expected",
748
[
9+
# Falsy inputs
7510
("", "there"),
7611
(None, "there"),
7712
(" ", "there"),
78-
("Alice", "Alice"),
79-
("Alice Bob", "Alice"),
80-
("Alice Bob Charlie", "Alice"),
81-
(" Alice Bob ", "Alice"),
82-
("Mary-Jane Watson", "Mary-Jane"),
13+
("()", "there"),
14+
(" (test) ", "there"),
15+
# Real display names from production
16+
("Ringo De Smet", "Ringo"),
17+
("Narek Gevorgyan", "Narek"),
18+
("Eduardo Gonzalez", "Eduardo"),
19+
("Noah Zoschke", "Noah"),
20+
("Jameson Nash", "Jameson"),
21+
("Dan Haber", "Dan"),
22+
("Hai Rao", "Hai"),
23+
("Alex Chaplinsky", "Alex"),
24+
("Alexander Shapiotko", "Alexander"),
25+
("Tadeu Maia", "Tadeu"),
26+
("Lacy Morrow", "Lacy"),
27+
("Andrew Coven", "Andrew"),
28+
("Nirdesh Dwa", "Nirdesh"),
29+
("Almaz Murzabekov", "Almaz"),
30+
("Misha Druzhinin", "Misha"),
31+
("Chad Pritchett", "Chad"),
32+
("Ariel Kanterewicz", "Ariel"),
33+
("Ronak Bansal", "Ronak"),
34+
("Atouch Mohamed", "Atouch"),
35+
("Masahiro Nakahashi", "Masahiro"),
36+
("Ankit Tolia", "Ankit"),
37+
("Eling Pramuatmaja", "Eling"),
38+
("Josh VanAllen", "Josh"),
39+
("Shlomy Sheps", "Shlomy"),
40+
("Marco Fernandez", "Marco"),
41+
("Tatsuya Nakamura", "Tatsuya"),
42+
("Vladimir de Turckheim", "Vladimir"),
43+
("Peter Somerville", "Peter"),
44+
("Camila Macedo", "Camila"),
45+
("Shinya Takada", "Shinya"),
46+
("Alexander Shcherbakov", "Alexander"),
47+
("Greg Harris", "Greg"),
48+
("Alexey Kuznetsov", "Alexey"),
49+
("Siddhant Badola", "Siddhant"),
50+
("Matt Healy", "Matt"),
51+
("Mayuresh Jakhotia", "Mayuresh"),
52+
("Sushnata Sarkar", "Sushnata"),
53+
("Pierre Collinet", "Pierre"),
54+
("Aleksandr Smyshliaev", "Aleksandr"),
55+
("Ryutaro Sugiyama", "Ryutaro"),
56+
("Aran Leite", "Aran"),
57+
("Brandon Hosley", "Brandon"),
58+
("Mohammad Hossine Rezazadeh", "Mohammad"),
59+
("Zhipeng Luo", "Zhipeng"),
60+
("Adit Chawdhary", "Adit"),
61+
("Thomas Bouffard", "Thomas"),
62+
("Hiroki Tashima", "Hiroki"),
63+
("Satyam Singh Niranjan", "Satyam"),
64+
("Eder Ramos", "Eder"),
65+
("Oersted Brion", "Oersted"),
66+
("Xavier Defrang", "Xavier"),
67+
("Nemoto Masaya", "Nemoto"),
68+
("Matas Mat", "Matas"),
69+
("Arturo Navarro", "Arturo"),
70+
("Marek Küthe", "Marek"),
71+
("Klaudijus Mackonis", "Klaudijus"),
72+
("Pedro Henrique Diniz", "Pedro"),
73+
("Ryo Kobashiri", "Ryo"),
74+
("Sagi Faumi", "Sagi"),
75+
("Keita Katahira", "Keita"),
76+
("Albert Pangilinan", "Albert"),
77+
("Batuhan Celasun", "Batuhan"),
78+
("Sunil Kumar HS", "Sunil"),
79+
("Ohira Shunpei", "Ohira"),
80+
("Chaker Ben Said", "Chaker"),
81+
("David Brochero", "David"),
82+
("Suraj Bhattarai", "Suraj"),
83+
("Nikita Malinovsky", "Nikita"),
84+
("Isaac Kearse", "Isaac"),
85+
("Webster Alk", "Webster"),
86+
("Rohit Mane", "Rohit"),
87+
("Mitsuhiko Yamamoto", "Mitsuhiko"),
88+
("Michael Yao", "Michael"),
89+
("Tai Dang", "Tai"),
90+
("Eita Nawaji", "Eita"),
91+
("Hoàng Phi Hùng", "Hoàng"),
92+
("Ammar Ahmed Butt", "Ammar"),
93+
("Omkar Hankare", "Omkar"),
94+
("Satyam Raj", "Satyam"),
95+
("Marco Kazama", "Marco"),
96+
("Davi Souza", "Davi"),
97+
("Naman Joshi", "Naman"),
98+
("Soo Kim", "Soo"),
99+
("Erick Bueno", "Erick"),
100+
("Ryan Mudryk", "Ryan"),
101+
("Takumi Sasada", "Takumi"),
102+
("Yang Qu", "Yang"),
103+
("Honda Jun", "Honda"),
104+
("Robin Junior Rodriguez Henao", "Robin"),
105+
("Joshua Chennault", "Joshua"),
106+
("Yuma Nunoya", "Yuma"),
107+
("Jeko Paul", "Jeko"),
108+
("Hamza Rebb", "Hamza"),
109+
("Ryan Townsend", "Ryan"),
110+
("Jakhangir Esanov", "Jakhangir"),
111+
("Shuhei Hikosaka", "Shuhei"),
112+
("Mike Harrison", "Mike"),
113+
("Yuya Takemasa", "Yuya"),
114+
("Takahiro Nakagawa", "Takahiro"),
115+
("Matan Coiffman", "Matan"),
116+
("Taichi Masakazu", "Taichi"),
117+
("Masakiyo Nishikawa", "Masakiyo"),
118+
("Akshay Nair A", "Akshay"),
119+
("Girma Wakeyo", "Girma"),
120+
("Yoshiharu Hirose", "Yoshiharu"),
121+
("Ashley Casey", "Ashley"),
122+
("Kawata Hiroki", "Kawata"),
123+
("Hideaki Shiina", "Hideaki"),
124+
("Oladoye Heritage", "Oladoye"),
125+
("Anadi Mishra", "Anadi"),
126+
("Andrew Li", "Andrew"),
127+
("Diksha Wagh", "Diksha"),
128+
("Calvin Fernandes", "Calvin"),
129+
("Pamela Ardana", "Pamela"),
130+
("Manuel Carter", "Manuel"),
131+
("Débora Lutz", "Débora"),
132+
("Artem Filin", "Artem"),
133+
("Richard Kindler", "Richard"),
134+
("Mirza Asadullah", "Mirza"),
135+
("David Burns", "David"),
136+
("Alex Scott", "Alex"),
137+
("Muhammad Anas", "Muhammad"),
138+
("Mohammad Al Amin Sheikh", "Mohammad"),
139+
("David Chen", "David"),
140+
("Fre Dy", "Fre"),
141+
("Alexis Placencia - the schizo", "Alexis"),
142+
# Single display names
143+
("Memory", "Memory"),
144+
("Glow", "Glow"),
145+
("Yumenosuke", "Yumenosuke"),
146+
("Holden", "Holden"),
147+
("Roman", "Roman"),
148+
("Yaovi", "Yaovi"),
149+
("Nils", "Nils"),
150+
("David", "David"),
151+
("Lg", "Lg"),
152+
("Sam", "Sam"),
153+
("Samzong", "Samzong"),
154+
("Daemon", "Daemon"),
155+
("Yuns", "Yuns"),
156+
("Flasic", "Flasic"),
157+
("Miguel", "Miguel"),
158+
("Specs", "Specs"),
159+
("Azit", "Azit"),
160+
("Vandy", "Vandy"),
161+
("Armand", "Armand"),
162+
("Young", "Young"),
163+
("Abhi", "Abhi"),
164+
("Brandon", "Brandon"),
165+
("Kazumi", "Kazumi"),
166+
("Kit", "Kit"),
167+
("Mathis", "Mathis"),
168+
("Corazon", "Corazon"),
169+
("Death", "Death"),
170+
("Eric", "Eric"),
171+
("Yuta", "Yuta"),
172+
("Jenny", "Jenny"),
173+
("Cody", "Cody"),
174+
("Victor", "Victor"),
175+
("Mat", "Mat"),
176+
("Oriya", "Oriya"),
177+
# CJK and unicode names
178+
("嘤嘤", "嘤嘤"),
179+
("何鑫", "何鑫"),
180+
("纯粹", "纯粹"),
181+
("高森松太郎", "高森松太郎"),
182+
("André Goulart Nogueira", "André"),
83183
("José María García", "José"),
84-
("李小明 李", "李小明"),
184+
("Müller Schmidt", "Müller"),
185+
("Nendō", "Nendō"),
186+
# Parentheses
187+
("Hiroshi (Wes) Nishio", "Hiroshi"),
188+
("(Mr.) Noah Zoschke", "Noah"),
189+
("Dan Haber (Jr.)", "Dan"),
190+
("(Nickname)", "there"),
191+
# Title/initial prefix skipping
192+
("Dr. John Doe", "John"),
193+
("L. Dayrit", "Dayrit"),
194+
("Milind A. Joshi", "Milind"),
195+
# Dot-separated handles
196+
("Frater.nul", "Frater"),
197+
("M.Rama Karthik", "Rama"),
198+
# Handle-style display names
199+
("Homero CA", "Homero"),
200+
("AdamN", "AdamN"),
201+
("BlackbriX", "BlackbriX"),
202+
# Usernames (no display name) — title-cased
203+
("fourcolors", "Fourcolors"),
204+
("sree", "Sree"),
205+
("Carsaig", "Carsaig"),
206+
("Efreak", "Efreak"),
207+
("Hexaf", "Hexaf"),
208+
("scherenhaenden", "Scherenhaenden"),
209+
("atriede", "Atriede"),
210+
("keeeener", "Keeeener"),
211+
("Jellebels", "Jellebels"),
212+
("lordmage", "Lordmage"),
213+
("seigot", "Seigot"),
214+
("koheitech", "Koheitech"),
215+
("mozzaru", "Mozzaru"),
216+
# Hyphenated usernames — take first segment
217+
("cuong-tran", "Cuong"),
218+
("toshimasa-sekine", "Toshimasa"),
219+
("kana-shii", "Kana"),
220+
("hazem-hosny", "Hazem"),
221+
("matthew-heartful", "Matthew"),
222+
("ken-shiozawa", "Ken"),
223+
("airi-nakamura", "Airi"),
224+
("kawaguchi-ryosuke", "Kawaguchi"),
225+
("Three-summers", "Three"),
226+
# Multi-token hyphenated names — keep hyphen
227+
("Kaelig Deloumeau-Prigent", "Kaelig"),
228+
("Mary-Jane Watson", "Mary-Jane"),
229+
("Jean-Pierre Dupont", "Jean-Pierre"),
85230
("O'Connor Smith", "O'Connor"),
86-
("van der Berg", "van"),
87-
("123 456", "123"),
88-
("@username display", "@username"),
231+
# Usernames with digits — return "there"
232+
("afc163", "there"),
233+
("apis3445", "there"),
234+
("parthi2929", "there"),
235+
("w7989363", "there"),
236+
("tbowman01", "there"),
237+
("Dark25", "there"),
238+
("broli95", "there"),
239+
("St119848", "there"),
240+
("RyoFuji619", "there"),
241+
("Khan285", "there"),
242+
("Guts98", "there"),
243+
("Itz4Blitz", "there"),
244+
("Gugan22", "there"),
245+
("Coldtrigon66", "there"),
246+
("ONE223", "there"),
247+
("niraj876", "there"),
248+
("Mr2Cool", "there"),
249+
("Elegy233", "there"),
250+
("93Pd9s8Jt", "there"),
251+
("AhJi26", "there"),
252+
("NoFace33", "there"),
253+
("curry798", "there"),
254+
("Sket1374@Gmail.Com", "there"),
255+
("Coolguy1211", "there"),
256+
("Toyro967", "there"),
257+
("devils6669", "there"),
258+
("R4fa3l2008", "there"),
259+
("Mehandsome9", "there"),
260+
("psluca911", "there"),
261+
("Esequiel122", "there"),
262+
("Da3m0N0", "there"),
263+
("MilosKerkez123", "there"),
264+
("2025ss", "there"),
265+
("Lamed12", "there"),
266+
("alzaem3000", "there"),
89267
],
90268
)
91-
def test_get_first_name_parametrized(input_name, expected):
92-
"""Test various input scenarios with parametrized test cases."""
269+
def test_get_first_name(input_name, expected):
93270
result = get_first_name(input_name)
94271
assert result == expected

0 commit comments

Comments
 (0)