diff --git a/dev/githubanalysis/lib/main.dart b/dev/githubanalysis/lib/main.dart index 6f49b4bf2..e5e7b9a71 100644 --- a/dev/githubanalysis/lib/main.dart +++ b/dev/githubanalysis/lib/main.dart @@ -61,6 +61,11 @@ bool issueIsClosed(final FullIssue issue) { return issue.metadata.isClosed || issue.metadata.state.toUpperCase() == 'CLOSED'; } +// Turns a username into an internal canonicalized form. +// We add a "👤" emoji here so that if we accidentally use the canonicalized form +// in the output, we will catch it. +String canon(final String? s) => '👤${(s ?? "").toLowerCase()}'; + Future full(final Directory cache, final GitHub github) async { try { // FETCH USER AND TEAM DATA @@ -72,18 +77,22 @@ Future full(final Directory cache, final GitHub github) async { cacheEpoch: maxAge(rosterMaxAge), ); final Set allMembers = {}; - final Set currentMembers = roster.teams[primaryTeam]!.keys.toSet(); + final Set currentMembers = roster.teams[primaryTeam]!.keys.map(canon).toSet(); final Set expectedMembers = (await membersFile.readAsString()) .trimRight() .split('\n') .where((final String name) => !name.endsWith(' (DO NOT ADD)')) + .map(canon) + .toSet(); + final Set expectedExmembers = (await exmembersFile.readAsString()) + .trimRight() + .split('\n') + .map(canon) .toSet(); - final Set expectedExmembers = (await exmembersFile.readAsString()).trimRight().split('\n').toSet(); try { - Set canon(final Set set) => set.map((final String s) => s.toLowerCase()).toSet(); - final Set unexpectedMembers = canon(currentMembers).difference(canon(expectedMembers)); - final Set memberExmembers = canon(expectedExmembers).intersection(canon(currentMembers)); - final Set missingMembers = canon(expectedMembers).difference(canon(currentMembers)); + final Set unexpectedMembers = currentMembers.difference(expectedMembers); + final Set memberExmembers = expectedExmembers.intersection(currentMembers); + final Set missingMembers = expectedMembers.difference(currentMembers); if (unexpectedMembers.isNotEmpty) { print( 'WARNING: The following users are currently members of $primaryTeam but not expected: ${unexpectedMembers.join(', ')}', @@ -144,11 +153,11 @@ Future full(final Directory cache, final GitHub github) async { UserActivity forUser(final User? user) { return activityMetrics.putIfAbsent(user!.login!, () { final UserActivity result = UserActivity(); - if (expectedMembers.contains(user.login)) { + if (expectedMembers.contains(canon(user.login))) { result ..isMember = true ..isActiveMember = true; - } else if (expectedExmembers.contains(user.login)) { + } else if (expectedExmembers.contains(canon(user.login))) { result.isMember = true; } return result; @@ -170,9 +179,7 @@ Future full(final Directory cache, final GitHub github) async { activity.characters += body.length; } - for (final String user in currentMembers) { - forUser(User(login: user)); - } + roster.teams[primaryTeam]!.values.forEach(forUser); final List allIssues = issues.values .expand((final Map issues) => issues.values) .where((final FullIssue issue) => issue.isValid) @@ -276,7 +283,7 @@ Future full(final Directory cache, final GitHub github) async { .toList(); for (final FullIssue issue in primaryIssues.where((final FullIssue issue) => issue.priority != null)) { final PriorityResults priorityResults = priorityAnalysis[issue.priority!]!; - final bool teamIssue = allMembers.contains(issue.metadata.user!.login); + final bool teamIssue = allMembers.contains(canon(issue.metadata.user!.login)); priorityResults.total += 1; if (teamIssue) { priorityResults.openedByTeam += 1; @@ -387,8 +394,8 @@ Future full(final Directory cache, final GitHub github) async { ..write(',${issue.labels.contains('new feature')}') ..write(',${issue.labels.contains('proposal')}') ..write(',${issue.labels.contains('waiting for customer response')}') - ..write(',${allMembers.contains(issue.metadata.user!.login)}') - ..write(',${expectedExmembers.contains(issue.metadata.user!.login)}') + ..write(',${allMembers.contains(canon(issue.metadata.user!.login))}') + ..write(',${expectedExmembers.contains(canon(issue.metadata.user!.login))}') ..writeln(); if ((daysToTwentyVotes == null || daysToTwentyVotes > 60) && issue.labels.contains('new feature') &&