Skip to content

Commit 1921227

Browse files
committed
Add separate page for each Rust team member and alumni
1 parent c98b7c3 commit 1921227

File tree

4 files changed

+247
-7
lines changed

4 files changed

+247
-7
lines changed

locales/en-US/governance.ftl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ governance-members-header = Members
3434
governance-alumni-header = Alumni
3535
governance-alumni-thanks = We also want to thank all past members for their invaluable contributions!
3636
37-
## governance/all-team-members.mbs
37+
## governance/all-team-members.hbs
3838
governance-all-team-members-title = All Rust team members
3939
governance-all-team-members-intro = This section lists the members of currently active Rust teams.
4040
governance-all-team-members-alumni-intro = This section lists our team alumni.
41+
42+
## govenance/person.hbs
43+
governance-person-title = Rust Project team member
44+
governance-person-team-member = Team member
45+
governance-person-team-alumni = Alumni

src/render.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,14 @@ pub fn render_index(render_ctx: &RenderCtx) -> anyhow::Result<()> {
180180
pub fn render_governance(render_ctx: &RenderCtx) -> anyhow::Result<()> {
181181
let data = render_ctx.teams.index_data();
182182

183+
// Index page
183184
for_all_langs("governance/index.html", |dst_path, lang| {
184185
render_ctx
185186
.page("governance/index", "governance-page-title", &data, lang)
186187
.render(dst_path)
187188
})?;
189+
190+
// Individual teams
188191
for team in data.teams {
189192
let data: PageData = render_ctx
190193
.teams
@@ -207,6 +210,7 @@ pub fn render_governance(render_ctx: &RenderCtx) -> anyhow::Result<()> {
207210
)?;
208211
}
209212

213+
// Archived teams
210214
let archived_teams_data = render_ctx.teams.archived_teams();
211215
for_all_langs("governance/archived-teams.html", |dst_path, lang| {
212216
render_ctx
@@ -219,6 +223,7 @@ pub fn render_governance(render_ctx: &RenderCtx) -> anyhow::Result<()> {
219223
.render(dst_path)
220224
})?;
221225

226+
// Page with all team members
222227
let all_team_members_data = render_ctx.teams.all_team_members();
223228
for_all_langs("governance/all-team-members.html", |dst_path, lang| {
224229
render_ctx
@@ -231,6 +236,28 @@ pub fn render_governance(render_ctx: &RenderCtx) -> anyhow::Result<()> {
231236
.render(dst_path)
232237
})?;
233238

239+
// A specific page for each team member
240+
let all_person_data = render_ctx.teams.all_person_data();
241+
for person in all_person_data {
242+
// Use <username>/index.html for a nicer URL (/people/foo vs /people/foo.html).
243+
for_all_langs(
244+
&format!(
245+
"governance/people/{}/index.html",
246+
person.github.to_lowercase()
247+
),
248+
|dst_path, lang| {
249+
render_ctx
250+
.page(
251+
"governance/person",
252+
"governance-person-title",
253+
&person,
254+
lang,
255+
)
256+
.render(dst_path)
257+
},
258+
)?;
259+
}
260+
234261
Ok(())
235262
}
236263

src/teams.rs

Lines changed: 152 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,11 @@ impl RustTeams {
7171
.filter(|team| team.website_data.is_some())
7272
// On the main page, show the leadership-council and all top-level
7373
// teams.
74-
.filter(|team| team.kind == TeamKind::Team && team.subteam_of.is_none())
74+
.filter(|team| is_toplevel_team(team))
7575
.map(|team| IndexTeam {
7676
section: kind_to_str(team.kind),
7777
page_name: team.website_data.clone().unwrap().page,
78-
url: format!(
79-
"{}/{}",
80-
kind_to_str(team.kind),
81-
team.website_data.as_ref().unwrap().page
82-
),
78+
url: get_team_relative_url(&team),
8379
team,
8480
})
8581
.collect::<Vec<IndexTeam>>();
@@ -234,6 +230,113 @@ impl RustTeams {
234230

235231
AllTeamMembers { active, alumni }
236232
}
233+
234+
pub fn all_person_data(&self) -> Vec<PersonData> {
235+
let mut people: HashMap<String, PersonData> = HashMap::new();
236+
237+
enum TeamMode {
238+
Member,
239+
Alumni,
240+
MemberOfArchivedTeam,
241+
}
242+
243+
fn add_team(
244+
people: &mut HashMap<String, PersonData>,
245+
ctx: &RustTeams,
246+
member: &TeamMember,
247+
team: &Team,
248+
mode: TeamMode,
249+
) {
250+
let person = people
251+
.entry(member.github.clone())
252+
.or_insert_with(move || PersonData {
253+
name: member.name.clone(),
254+
github: member.github.clone(),
255+
active_teams: vec![],
256+
alumni_teams: vec![],
257+
});
258+
let teams = match mode {
259+
TeamMode::Member => &mut person.active_teams,
260+
TeamMode::Alumni | TeamMode::MemberOfArchivedTeam => &mut person.alumni_teams,
261+
};
262+
let url = match mode {
263+
TeamMode::Member | TeamMode::Alumni => ctx.get_toplevel_team_url(team),
264+
TeamMode::MemberOfArchivedTeam => Some("archived-teams.html".to_string()),
265+
};
266+
teams.push(PersonTeam::new(team, url));
267+
}
268+
269+
for team in &self.archived_teams {
270+
for member in team.members.iter().chain(&team.alumni) {
271+
add_team(
272+
&mut people,
273+
self,
274+
member,
275+
team,
276+
TeamMode::MemberOfArchivedTeam,
277+
);
278+
}
279+
}
280+
for team in &self.teams {
281+
if team.name == "all" || team.name == "alumni" || team.name == "leads" {
282+
continue;
283+
}
284+
285+
for member in &team.members {
286+
add_team(&mut people, self, member, team, TeamMode::Member);
287+
}
288+
for member in &team.alumni {
289+
add_team(&mut people, self, member, team, TeamMode::Alumni);
290+
}
291+
}
292+
293+
let mut people: Vec<PersonData> = people.into_values().collect();
294+
people.sort_by(|a, b| a.github.cmp(&b.github));
295+
296+
for person in &mut people {
297+
person
298+
.active_teams
299+
.sort_by(|a, b| a.webpage_name.cmp(&b.webpage_name));
300+
person
301+
.alumni_teams
302+
.sort_by(|a, b| a.webpage_name.cmp(&b.webpage_name));
303+
}
304+
305+
people
306+
}
307+
308+
fn get_toplevel_team_url<'a>(&'a self, mut team: &'a Team) -> Option<String> {
309+
while !is_toplevel_team(team) {
310+
let Some(parent) = &team.subteam_of else {
311+
return None;
312+
};
313+
let Some(parent) = self.teams.iter().find(|t| t.name == *parent) else {
314+
return None;
315+
};
316+
team = parent;
317+
}
318+
319+
if team.website_data.is_some() {
320+
Some(get_team_relative_url(team))
321+
} else {
322+
None
323+
}
324+
}
325+
}
326+
327+
/// Get a relative URL of a team that should be appended to
328+
/// Should only be used for top-level teams.
329+
fn get_team_relative_url(team: &Team) -> String {
330+
assert!(is_toplevel_team(team));
331+
format!(
332+
"{}/{}",
333+
kind_to_str(team.kind),
334+
team.website_data.as_ref().unwrap().page
335+
)
336+
}
337+
338+
fn is_toplevel_team(team: &Team) -> bool {
339+
team.kind == TeamKind::Team && team.subteam_of.is_none()
237340
}
238341

239342
#[derive(Serialize)]
@@ -272,6 +375,49 @@ pub struct AllTeamMembers {
272375
alumni: Vec<TeamMember>,
273376
}
274377

378+
#[derive(Serialize)]
379+
pub struct PersonTeam {
380+
team: Team,
381+
toplevel_url: Option<String>,
382+
webpage_name: String,
383+
}
384+
385+
impl PersonTeam {
386+
fn new(team: &Team, toplevel_url: Option<String>) -> Self {
387+
let webpage_name = team
388+
.website_data
389+
.as_ref()
390+
.map(|w| w.name.clone())
391+
.unwrap_or_else(|| {
392+
// Turn inside-rust-reviewers into Inside Rust Reviewers
393+
team.name
394+
.split("-")
395+
.map(|p| {
396+
p.chars()
397+
.take(1)
398+
.flat_map(|c| c.to_uppercase())
399+
.chain(p.chars().skip(1))
400+
.collect::<String>()
401+
})
402+
.collect::<Vec<String>>()
403+
.join(" ")
404+
});
405+
Self {
406+
team: team.clone(),
407+
toplevel_url: toplevel_url,
408+
webpage_name,
409+
}
410+
}
411+
}
412+
413+
#[derive(Serialize)]
414+
pub struct PersonData {
415+
name: String,
416+
pub github: String,
417+
active_teams: Vec<PersonTeam>,
418+
alumni_teams: Vec<PersonTeam>,
419+
}
420+
275421
pub fn load_rust_teams() -> anyhow::Result<RustTeams> {
276422
println!("Downloading Team API data");
277423

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
{{#*inline "teams"}}
2+
{{#each teams as |team|}}
3+
<li>
4+
{{#if team.toplevel_url }}
5+
<a href="{{../baseurl}}/governance/{{team.toplevel_url}}{{ team-anchor team.team }}">
6+
{{ team.webpage_name }}
7+
</a>
8+
{{else}}
9+
{{ team.webpage_name }}
10+
{{/if}}
11+
</li>
12+
{{/each}}
13+
{{/inline}}
14+
15+
{{#*inline "page"}}
16+
<section class="green" style="padding-bottom: 10px;">
17+
<div class="w-100 mw-none mw-8-m mw9-l center f2">
18+
<div class="w-100 w-50-l mb3 flex flex-row items-center">
19+
<a class="mr4 w4 h4 flex-shrink-0" href="https://github.com/{{data.github}}">
20+
<img class="w-100 h-100 bg-white br2"
21+
src="https://avatars.githubusercontent.com/{{data.github}}"
22+
alt="{{data.name}}">
23+
</a>
24+
<div>
25+
{{data.name}}
26+
<div class="f4">
27+
GitHub: <a href="https://github.com/{{data.github}}">{{data.github}}</a>
28+
</div>
29+
</div>
30+
</div>
31+
</div>
32+
</section>
33+
34+
{{# if data.active_teams }}
35+
<section class="purple" style="padding-bottom: 10px;">
36+
<div class="w-100 mw-none mw-8-m mw9-l center">
37+
<header>
38+
<h2>{{ fluent "governance-person-team-member" }}</h2>
39+
<div class="highlight"></div>
40+
</header>
41+
<ul>
42+
{{> teams teams=data.active_teams }}
43+
</ul>
44+
</div>
45+
</section>
46+
{{/if}}
47+
48+
{{# if data.alumni_teams }}
49+
<section class="red" style="padding-bottom: 10px;">
50+
<div class="w-100 mw-none mw-8-m mw9-l center">
51+
<header>
52+
<h2>{{ fluent "governance-person-team-alumni" }}</h2>
53+
<div class="highlight"></div>
54+
</header>
55+
<ul>
56+
{{> teams teams=data.alumni_teams }}
57+
</ul>
58+
</div>
59+
</section>
60+
{{/if}}
61+
{{/inline}}
62+
{{~> (lookup this "parent")~}}

0 commit comments

Comments
 (0)