Repository: scipy/scipy

Issue:
REL: list contributors using GitHub handles
When we do the release on GitHub, I guess we copy/paste the text we put in the rst files. If yes, then can we adjust the contributor list to use the proper GitHub handles? Because right now GitHub only list/link a few contributors instead of the >100 contributors who effectively contributed. It would help the recognition of smaller contributors.

As we can see in the screenshot, only if you use the GH handle, the UI will pick it up.

<img width="426" alt="Screenshot 2022-02-07 at 13 22 20" src="https://user-images.githubusercontent.com/23188539/152787310-528aca70-e460-4a83-a279-75cf40bf838d.png">




Discussion:
> GitHub only list/link a few contributors instead of the >100 contributors who effectively contributed.

We list all of the contributors using the release scripts and `.mailmap` machinery. They are clearly visible on the release page:

![image](https://user-images.githubusercontent.com/7903078/152817550-2b4451d2-516f-4004-b8b1-7c03b5fda117.png)

etc..

The separate GitHub feature that automatically parses out contributors to produce a separate list could be taken advantage of, though I'd actually prefer to go in the other direction--not use GitHub handles at all and stick to the "real names" for folks who share them, at least in the short term it is fair bit less effort--we only have 1 or 2 handles in there and they are mostly accidental.
Also worth noting that even the basic `.mailmap` machinery we have now still requires a fair bit of manual maintenance, and adding a second mechanism isn't likely to reduce that.
I agree with @tylerjereddy here. The list of names is generated with:
```
python tools/authors.py v1.7.0..v1.8.0 
```
The aim is to include people's real names, and for consistency we shouldn't change that. For people who do not want their real name included, we use GitHub handles. Here all that is needed is adjust `.mailmap` to ensure the `@` is removed from `@endolith`'s name.
Ok, If we stick with the current solution, I can change the script to remove `@`.

@tylerjereddy we discussed a bit the topic during today's meeting. I will summarise my whole intent/idea here.

I see two points of improvements on the release to give contributors (especially small ones) credit.

1. Make sure that everyone in the list has it's GitHub handle. I would add it instead of removing it, since GitHub seems to care about it and create a special section for that. Another possibility is to not put the whole list as other projects are doing (pandas, sklearn for instance. ex. https://github.com/scikit-learn/scikit-learn/releases/tag/1.0.2). They just give a 2 line install instruction and redirect to the Changelog. It's actually easier to navigate and see the dates of releases.
2. Improve the Changelog itself. Looking at sklearn example again, to me, what they have is great in terms of completeness of the information, clarity, presentation and also attribution. All the points are listed with direct links to PR and the authors name is right there which clearly gives a sense of the amount/type of work done by different people. I don't know if sklearn is doing this work manually though and I agree that we don't want to have more work to do with our limited resources 😃 We do have this information right now but scattered. Except for the "quantity/type" which is not visible without going through the PRs/issues full list on the long version of the changelog, this could also be an "easy" fix to list the PRs/issues and the author right next to them (happy to have a look too if we want to do this).





@tupui points (1) and (2) are very different. You started with only (1), which (as you mentioned in the meeting) is a _very minor thing_. Please let's just take the lowest-effort solution here, remove the single `@`, and be done with it.

(2) is different. The scikit-learn Changelog is quite nice, but it's unclear how it is generated. I imagine it's a significant PR workflow change, perhaps with a bot, or giving extra work for the release manager. If you're interested in upgrading to such a Changelog, it seems like you should investigate how that is generated. If it is not significantly more work we may want to do that, but it's separate from your original bug report.
I will remove `@`.

I talked about the point 2 to be transparent about the discussion we had. I will investigate to see if there is an easy path.

Resolving patch:
diff --git a/tools/authors.py b/tools/authors.py
--- a/tools/authors.py
+++ b/tools/authors.py
@@ -1,9 +1,11 @@
 #!/usr/bin/env python
 # -*- encoding:utf-8 -*-
 """
-git-authors [OPTIONS] REV1..REV2
+List the authors who contributed within a given revision interval::
 
-List the authors who contributed within a given revision interval.
+    python tools/authors.py REV1..REV2
+
+`REVx` being a commit hash.
 
 To change the name mapping, edit .mailmap on the top-level of the
 repository.
@@ -17,6 +19,7 @@
 import os
 import io
 import subprocess
+import collections
 
 stdout_b = sys.stdout.buffer
 MAILMAP_FILE = os.path.join(os.path.dirname(__file__), "..", ".mailmap")
@@ -42,7 +45,7 @@ def main():
 
     # Analyze log data
     all_authors = set()
-    authors = set()
+    authors = collections.Counter()
 
     def analyze_line(line, names, disp=False):
         line = line.strip().decode('utf-8')
@@ -56,17 +59,17 @@ def analyze_line(line, names, disp=False):
             if disp:
                 if name not in names:
                     stdout_b.write(("    - Author: %s\n" % name).encode('utf-8'))
-            names.add(name)
+            names.update((name,))
 
         # Look for "thanks to" messages in the commit log
-        m = re.search(r'([Tt]hanks to|[Cc]ourtesy of) ([A-Z][A-Za-z]*? [A-Z][A-Za-z]*? [A-Z][A-Za-z]*|[A-Z][A-Za-z]*? [A-Z]\. [A-Z][A-Za-z]*|[A-Z][A-Za-z ]*? [A-Z][A-Za-z]*|[a-z0-9]+)($|\.| )', line)
+        m = re.search(r'([Tt]hanks to|[Cc]ourtesy of|Co-authored-by:) ([A-Z][A-Za-z]*? [A-Z][A-Za-z]*? [A-Z][A-Za-z]*|[A-Z][A-Za-z]*? [A-Z]\. [A-Z][A-Za-z]*|[A-Z][A-Za-z ]*? [A-Z][A-Za-z]*|[a-z0-9]+)($|\.| )', line)
         if m:
             name = m.group(2)
             if name not in (u'this',):
                 if disp:
                     stdout_b.write("    - Log   : %s\n" % line.strip().encode('utf-8'))
                 name = NAME_MAP.get(name, name)
-                names.add(name)
+                names.update((name,))
 
             line = line[m.end():].strip()
             line = re.sub(r'^(and|, and|, ) ', u'Thanks to ', line)
@@ -74,12 +77,12 @@ def analyze_line(line, names, disp=False):
 
     # Find all authors before the named range
     for line in git.pipe('log', '--pretty=@@@%an@@@%n@@@%cn@@@%n%b',
-                         '%s' % (rev1,)):
+                         f'{rev1}'):
         analyze_line(line, all_authors)
 
     # Find authors in the named range
     for line in git.pipe('log', '--pretty=@@@%an@@@%n@@@%cn@@@%n%b',
-                         '%s..%s' % (rev1, rev2)):
+                         f'{rev1}..{rev2}'):
         analyze_line(line, authors, disp=options.debug)
 
     # Sort
@@ -101,7 +104,7 @@ def name_key(fullname):
 
     # generate set of all new authors
     if vars(options)['new']:
-        new_authors = authors.difference(all_authors)
+        new_authors = set(authors.keys()).difference(all_authors)
         n_authors = list(new_authors)
         n_authors.sort(key=name_key)
         # Print some empty lines to separate
@@ -111,8 +114,12 @@ def name_key(fullname):
         # return for early exit so we only print new authors
         return
 
-    authors = list(authors)
-    authors.sort(key=name_key)
+    try:
+        authors.pop('GitHub')
+    except KeyError:
+        pass
+    # Order by name. Could order by count with authors.most_common()
+    authors = sorted(authors.items(), key=lambda i: name_key(i[0]))
 
     # Print
     stdout_b.write(b"""
@@ -121,11 +128,14 @@ def name_key(fullname):
 
 """)
 
-    for author in authors:
+    for author, count in authors:
+        # remove @ if only GH handle is available
+        author_clean = author.strip('@')
+
         if author in all_authors:
-            stdout_b.write(("* %s\n" % author).encode('utf-8'))
+            stdout_b.write((f"* {author_clean} ({count})\n").encode('utf-8'))
         else:
-            stdout_b.write(("* %s +\n" % author).encode('utf-8'))
+            stdout_b.write((f"* {author_clean} ({count}) +\n").encode('utf-8'))
 
     stdout_b.write(("""
 A total of %(count)d people contributed to this release.


Test patch: