From 768c5615a3dc0999220d82e769b410e541296a8f Mon Sep 17 00:00:00 2001 From: Oscar Esteban Date: Wed, 18 Aug 2021 22:25:31 +0200 Subject: [PATCH] maint: initial commit adopting tentative governance model --- .maint/CONTRIBUTORS.md | 22 +++ .maint/FORMER.md | 8 + .maint/HOW_WE_WORK.md | 57 +++++++ .maint/MAINTAINERS.md | 15 ++ .maint/PIs.md | 16 ++ .maint/ROADMAP.md | 7 + .maint/contributors.json | 26 --- .maint/developers.json | 50 ------ .maint/former.json | 5 - .maint/paper_author_list.py | 70 -------- .maint/requirements.txt | 2 + .maint/update_authors.py | 310 ++++++++++++++++++++++++++++++++++++ .maint/update_zenodo.py | 137 ---------------- CODE_OF_CONDUCT.md | 3 + CONTRIBUTING.md | 3 +- GOVERNANCE.md | 131 +++++++++++++++ 16 files changed, 573 insertions(+), 289 deletions(-) create mode 100644 .maint/CONTRIBUTORS.md create mode 100644 .maint/FORMER.md create mode 100644 .maint/HOW_WE_WORK.md create mode 100644 .maint/MAINTAINERS.md create mode 100644 .maint/PIs.md create mode 100644 .maint/ROADMAP.md delete mode 100644 .maint/contributors.json delete mode 100644 .maint/developers.json delete mode 100644 .maint/former.json delete mode 100644 .maint/paper_author_list.py create mode 100644 .maint/requirements.txt create mode 100644 .maint/update_authors.py delete mode 100755 .maint/update_zenodo.py create mode 100644 CODE_OF_CONDUCT.md create mode 100644 GOVERNANCE.md diff --git a/.maint/CONTRIBUTORS.md b/.maint/CONTRIBUTORS.md new file mode 100644 index 000000000..3ffe4b2b6 --- /dev/null +++ b/.maint/CONTRIBUTORS.md @@ -0,0 +1,22 @@ +# CONTRIBUTORS + +This document lists those who have made contributions to the Project. +As per the contributor guidelines, they should be included by default in any publications derived from the Project. + +If you are new to the project, don't forget to add your name and affiliation to the list of contributors here! Our Welcome Bot will send an automated message reminding this to first-time contributors. +Before every release, unlisted contributors will be invited again to add their names to this file (just in case they missed the automated message from our Welcome Bot). + +| **Lastname** | **Name** | **Handle** | **ORCID** | **Affiliation** | +| --- | --- | --- | --- | --- | +| Dickie | Erin W. | @edickie | 0000-0003-3028-9864 | Krembil Centre for Neuroinformatics, The Centre for Addiction and Mental Health, Dept. of Psychiatry, University of Toronto | +| Esteban | Oscar | @oesteban | 0000-0001-8435-6191 | Dept. of Radiology, Lausanne University Hospital and University of Lausanne | +| Joseph | Michael | @josephmje | 0000-0002-0068-230X | The Centre for Addiction and Mental Health | +| Keshavan | Anisha | @akeshavan | 0000-0003-3554-043X | The University of Washington, eScience Institute | +| Lerma-Usabiaga | Garikoitz | @garikoitz | 0000-0001-9800-4816 | Basque Center on Cognition, Brain and Language (BCBL)
Department of Psychology, Stanford University | +| Mansour | Salim | @slimnsour | 0000-0002-1092-1650 | The Centre for Addiction and Mental Health | +| Pisner | Derek | @dPys | 0000-0002-1228-0201 | Department of Psychology, University of Texas at Austin, TX, USA | +| Richie-Halford | Adam | @richford | 0000-0001-9276-9084 | The University of Washington, eScience Institute | +| Rokem | Ariel | @arokem | 0000-0003-0679-1985 | The University of Washington eScience Institute | +| Cieslak | Matthew | @mattcieslak | 0000-0002-1931-4734 | Perelman School of Medicine, University of Pennsylvania, PA, USA | +| Kent | James D. | @jdkent | 0000-0002-4892-2659 | Neuroscience Program, University of Iowa | +| Veraart | Jelle | @jelleveraart | 0000-0003-0781-0420 | School of Medicine, New York University | diff --git a/.maint/FORMER.md b/.maint/FORMER.md new file mode 100644 index 000000000..0e29197a1 --- /dev/null +++ b/.maint/FORMER.md @@ -0,0 +1,8 @@ +# FORMER MEMBERS + +This document lists former contributors or maintainers who want to disengage from the Project, and seek to be dismissed in communications or future papers. +By adding your name to this list you are giving up on all your responsibilities to the project. +Should you desire to be considered back as a contributor or maintainer, please remove your name from this list and proceed as prescribed in the governance documents. + +| **Lastname** | **Name** | **Handle** | +| --- | --- | --- | \ No newline at end of file diff --git a/.maint/HOW_WE_WORK.md b/.maint/HOW_WE_WORK.md new file mode 100644 index 000000000..e211a8b5f --- /dev/null +++ b/.maint/HOW_WE_WORK.md @@ -0,0 +1,57 @@ +# How we work + +This document can be used by the Project to define the Maintainers' operation workflows. +The document is completely optional, and it is a responsibility of the Maintainers to decide whether it is used, and if so, to keep it up-to-date. + + + + +## Maintainers roles + +The Project has designated three specific roles that Maintainers will cover: + +* **(i)** *Lead-developer (LD)*. + The LD is responsible for ensuring the fluent communication between the Maintainers, keeps track of acquired responsibilities (e.g., review a pull-request), and anticipates and preempts roadblocks and conflicts that may make the work of other Maintainers harder; +* **(ii)** *SCRUM Guider (SG)*. + The SG is responsible for the triage of new issues, for communicating with the Maintainers and prioritize issues and features, and coordinate the concerted effort of Maintainers. + The SG may also lead meetings and keeps up-to-date the GitHub Project panel and the Milestones of this Project. +* **(iii)** *Outbound Coordinator (OC)*. + The OC is responsible for communicating with the maintainers of other projects (the project's OC, if the role is assigned) in the organization, ensuring that potential conflicts or incompatibility between releases across projects are resolved. +* **(iv)** *Advisory Member (AM)*. + AMs are invited maintainers to assist in fulfilling the maintenance duties and responsibilities by sharing their knowledge and resources with the board. + However, AMs are not directly responsible for the Project's direction and may or may not be involved in the decision making (which should be agreed upon by the MAINTAINERS board before starting their activities as AMs). + The Project may have as many AMs as the regular members see necessary to properly develop the Project. + +## Project management + +The Project uses the following management tools: + +* **GitHub Milestones** +* **GitHub Project panel** -- which is located at https://github.com/orgs/nipreps/projects/3 + +**Maintainers' Meetings**. +The maintainers will meet at least once every fours weeks (monthly), although the LD may call for extra meetings when they might be necessary. +The agenda of the regular monthly meeting will be organized by the SG, who will also lead regular meetings. + +**Open Meetings**. +Other meetings open to broader public can be proposed by any Maintainer, and the LD will be responsible of its organization in collaboration with the proponent(s). +As for any other decision, all Maintainers must agree on calling for a meeting. +These meetings can be organized to: + +* brainstorm and engage the community in developing/updating the roadmap; +* gather feedback (e.g., after a recent new release); +* inform about changes in the Maintainers team, as well as recruiting new members; +* disseminate new important features; +* make announcements; etc. + +## Setting priorities and triaging issues + +This Project follows [the general recommendations of *NiPreps*](https://www.nipreps.org/community/CONTRIBUTING/#issue-labels). + + diff --git a/.maint/MAINTAINERS.md b/.maint/MAINTAINERS.md new file mode 100644 index 000000000..ac699bd06 --- /dev/null +++ b/.maint/MAINTAINERS.md @@ -0,0 +1,15 @@ +# Maintainers + +This document lists the Maintainers of the project. +Maintainers may be added once approved by the existing maintainers as described in the `../GOVERNANCE.md` document. +By adding your name to this list you are agreeing to abide by the governance documents and to abide by all of the Organization's polices, including the code of conduct, trademark policy, and antitrust policy. +If you are participating because of your affiliation with another organization (designated below), you represent that you have the authority to bind that organization to these policies. + + + +| **Lastname** | **Name** | **Handle** | **ORCID** | **Affiliation** | **Role** | +| --- | --- | --- | --- | --- | --- | +| Dickie | Erin W. | @edickie | 0000-0003-3028-9864 | Krembil Centre for Neuroinformatics, The Centre for Addiction and Mental Health, Dept. of Psychiatry, University of Toronto | +| Esteban | Oscar | @oesteban | 0000-0001-8435-6191 | Dept. of Radiology, Lausanne University Hospital and University of Lausanne | +| Joseph | Michael | @josephmje | 0000-0002-0068-230X | The Centre for Addiction and Mental Health | +| Rokem | Ariel | @arokem | 0000-0003-0679-1985 | The University of Washington eScience Institute | diff --git a/.maint/PIs.md b/.maint/PIs.md new file mode 100644 index 000000000..df801194a --- /dev/null +++ b/.maint/PIs.md @@ -0,0 +1,16 @@ +# PRINCIPAL INVESTIGATORS + +This documents the key personnel who oversees the development of the Project and secures funding. +The names in this file are designated by the Organization's TSC at the time of accepting the Project under the Organization. +Changes to this file must be approved by the TSC. +When a PI ceases serving as such, by default, their name will be placed in the `CONTRIBUTORS.md` file should it not be there already. + +By having your name in this list you are agreeing to abide by the governance documents and to abide by all of the Organization's polices, including the code of conduct, trademark policy, and antitrust policy. +If you are participating because of your affiliation with another organization (designated below), you represent that you have the authority to bind that organization to these policies. + +| **Lastname** | **Name** | **Handle** | **ORCID** | **Affiliation** | **Position** | +| --- | --- | --- | --- | --- | --- | +| Esteban | Oscar | @oesteban | 0000-0001-8435-6191 | Department of Radiology, Lausanne University Hospital and University of Lausanne | | +| Rokem | Ariel | @arokem | 0000-0003-0679-1985 | The University of Washington eScience Institute, WA, USA | | +| Poldrack | Russell A. | @poldrack | 0000-0001-6755-0259 | Department of Psychology, Stanford University | | +| Satterthwaite | Theodore D. | | 0000-0001-7072-9399 | Perelman School of Medicine, University of Pennsylvania, PA, USA | | \ No newline at end of file diff --git a/.maint/ROADMAP.md b/.maint/ROADMAP.md new file mode 100644 index 000000000..7e53895be --- /dev/null +++ b/.maint/ROADMAP.md @@ -0,0 +1,7 @@ +# ROADMAP + +This document states how the roadmap is built, discussed and monitored by the Maintainers, with the engagement of Contributors and PIs. + +For example, if the GitHub Projects feature is used, this document will contain a link to the corresponding Project and document who is responsible of managing the project, contacting Contributors and Maintainers to monitor the progress of activities, organizing meetings and discussions around the activities, etc. + +This document may also indicate how the *milestones* feature of the project is used and, as for Projects, who is responsible of what coordination actions, their frequence, etc. diff --git a/.maint/contributors.json b/.maint/contributors.json deleted file mode 100644 index dff7c341d..000000000 --- a/.maint/contributors.json +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "affiliation": "Perelman School of Medicine, University of Pennsylvania, PA, USA", - "name": "Cieslak, Matthew", - "orcid": "0000-0002-1931-4734", - "type": "Researcher" - }, - { - "affiliation": "Neuroscience Program, University of Iowa", - "name": "Kent, James D.", - "orcid": "0000-0002-4892-2659", - "type": "Researcher" - }, - { - "affiliation": "Department of Psychology, Stanford University", - "name": "Poldrack, Russell A.", - "orcid": "0000-0001-6755-0259", - "type": "Researcher" - }, - { - "affiliation": "School of Medicine, New York University", - "name": "Veraart, Jelle", - "orcid": "0000-0003-0781-0420", - "type": "Researcher" - } -] diff --git a/.maint/developers.json b/.maint/developers.json deleted file mode 100644 index 697b330cb..000000000 --- a/.maint/developers.json +++ /dev/null @@ -1,50 +0,0 @@ -[ - { - "affiliation": "Krembil Centre for Neuroinformatics, The Centre for Addiction and Mental Health, Dept. of Psychiatry, University of Toronto", - "name": "Dickie, Erin W.", - "orcid": "0000-0003-3028-9864" - }, - { - "affiliation": "Dept. of Radiology, Lausanne University Hospital and University of Lausanne", - "name": "Esteban, Oscar", - "orcid": "0000-0001-8435-6191" - }, - { - "affiliation": "The Centre for Addiction and Mental Health", - "name": "Joseph, Michael", - "orcid": "0000-0002-0068-230X" - }, - { - "affiliation": "The University of Washington, eScience Institute", - "name": "Keshavan, Anisha", - "orcid": "0000-0003-3554-043X" - }, - { - "affiliation": [ - "Basque Center on Cognition, Brain and Language (BCBL)", - "Department of Psychology, Stanford University" - ], - "name": "Lerma-Usabiaga, Garikoitz", - "orcid": "0000-0001-9800-4816" - }, - { - "affiliation": "The Centre for Addiction and Mental Health", - "name": "Mansour, Salim", - "orcid": "0000-0002-1092-1650" - }, - { - "affiliation": "Department of Psychology, University of Texas at Austin, TX, USA", - "name": "Pisner, Derek", - "orcid": "0000-0002-1228-0201" - }, - { - "affiliation": "The University of Washington, eScience Institute", - "name": "Richie-Halford, Adam", - "orcid": "0000-0001-9276-9084" - }, - { - "affiliation": "The University of Washington eScience Institute", - "name": "Rokem, Ariel", - "orcid": "0000-0003-0679-1985" - } -] diff --git a/.maint/former.json b/.maint/former.json deleted file mode 100644 index 1b0d1a22d..000000000 --- a/.maint/former.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - { - "name": "nrajamani3" - } -] \ No newline at end of file diff --git a/.maint/paper_author_list.py b/.maint/paper_author_list.py deleted file mode 100644 index c4f86a4ef..000000000 --- a/.maint/paper_author_list.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python3 -"""Generate an author list for a new paper or abstract.""" -import sys -from pathlib import Path -import json -from update_zenodo import get_git_lines, sort_contributors - - -# These authors should go last -AUTHORS_LAST = ["Rokem, Ariel", "Esteban, Oscar"] - - -def _aslist(inlist): - if not isinstance(inlist, list): - return [inlist] - return inlist - - -if __name__ == "__main__": - devs = json.loads(Path(".maint/developers.json").read_text()) - contribs = json.loads(Path(".maint/contributors.json").read_text()) - - hits, misses = sort_contributors( - devs + contribs, - get_git_lines(), - exclude=json.loads(Path(".maint/former.json").read_text()), - last=AUTHORS_LAST, - ) - # Remove position - affiliations = [] - for item in hits: - del item["position"] - for a in _aslist(item.get("affiliation", "Unaffiliated")): - if a not in affiliations: - affiliations.append(a) - - aff_indexes = [ - ", ".join( - [ - "%d" % (affiliations.index(a) + 1) - for a in _aslist(author.get("affiliation", "Unaffiliated")) - ] - ) - for author in hits - ] - - if misses: - print( - "Some people made commits, but are missing in .maint/ " - f"files: {', '.join(misses)}", - file=sys.stderr, - ) - - print("Authors (%d):" % len(hits)) - print( - "%s." - % "; ".join( - [ - "%s \\ :sup:`%s`\\ " % (i["name"], idx) - for i, idx in zip(hits, aff_indexes) - ] - ) - ) - - print( - "\n\nAffiliations:\n%s" - % "\n".join( - ["{0: >2}. {1}".format(i + 1, a) for i, a in enumerate(affiliations)] - ) - ) diff --git a/.maint/requirements.txt b/.maint/requirements.txt new file mode 100644 index 000000000..6ffe47f10 --- /dev/null +++ b/.maint/requirements.txt @@ -0,0 +1,2 @@ +click +fuzzywuzzy[speedup] diff --git a/.maint/update_authors.py b/.maint/update_authors.py new file mode 100644 index 000000000..66448a29d --- /dev/null +++ b/.maint/update_authors.py @@ -0,0 +1,310 @@ +#!/usr/bin/env python3 +"""Update and sort the creators list of the zenodo record.""" +import sys +from pathlib import Path +import json +import click +from fuzzywuzzy import fuzz, process + + +def read_md_table(md_text): + """ + Extract the first table found in a markdown document as a Python dict. + + Examples + -------- + >>> read_md_table(''' + ... # Some text + ... + ... More text + ... + ... | **Header1** | **Header2** | + ... | --- | --- | + ... | val1 | val2 | + ... | | val4 | + ... + ... | **Header3** | **Header4** | + ... | --- | --- | + ... | val1 | val2 | + ... | | val4 | + ... ''') + [{'header1': 'val1', 'header2': 'val2'}, {'header2': 'val4'}] + + """ + prev = None + keys = None + retval = [] + for line in md_text.splitlines(): + if line.strip().startswith("| --- |"): + keys = ( + k.replace("*", "").strip() + for k in prev.split("|") + ) + keys = [k.lower() for k in keys if k] + continue + elif not keys: + prev = line + continue + + if not line or not line.strip().startswith("|"): + break + + values = [v.strip() or None for v in line.split("|")][1:-1] + retval.append({k: v for k, v in zip(keys, values) if v}) + + return retval + + +def sort_contributors(entries, git_lines, exclude=None, last=None): + """Return a list of author dictionaries, ordered by contribution.""" + last = last or [] + sorted_authors = sorted(entries, key=lambda i: i["name"]) + + first_last = [ + " ".join(val["name"].split(",")[::-1]).strip() for val in sorted_authors + ] + first_last_excl = [ + " ".join(val["name"].split(",")[::-1]).strip() for val in exclude or [] + ] + + unmatched = [] + author_matches = [] + for ele in git_lines: + matches = process.extract( + ele, first_last, scorer=fuzz.token_sort_ratio, limit=2 + ) + # matches is a list [('First match', % Match), ('Second match', % Match)] + if matches[0][1] > 80: + val = sorted_authors[first_last.index(matches[0][0])] + else: + # skip unmatched names + if ele not in first_last_excl: + unmatched.append(ele) + continue + + if val not in author_matches: + author_matches.append(val) + + names = {" ".join(val["name"].split(",")[::-1]).strip() for val in author_matches} + for missing_name in first_last: + if missing_name not in names: + missing = sorted_authors[first_last.index(missing_name)] + author_matches.append(missing) + + position_matches = [] + for i, item in enumerate(author_matches): + pos = item.pop("position", None) + if pos is not None: + position_matches.append((i, int(pos))) + + for i, pos in position_matches: + if pos < 0: + pos += len(author_matches) + 1 + author_matches.insert(pos, author_matches.pop(i)) + + return author_matches, unmatched + + +def get_git_lines(fname="line-contributors.txt"): + """Run git-line-summary.""" + import shutil + import subprocess as sp + + contrib_file = Path(fname) + + lines = [] + if contrib_file.exists(): + print("WARNING: Reusing existing line-contributors.txt file.", file=sys.stderr) + lines = contrib_file.read_text().splitlines() + + git_line_summary_path = shutil.which("git-line-summary") + if not lines and git_line_summary_path: + print("Running git-line-summary on repo") + lines = sp.check_output([git_line_summary_path]).decode().splitlines() + lines = [l for l in lines if "Not Committed Yet" not in l] + contrib_file.write_text("\n".join(lines)) + + if not lines: + raise RuntimeError( + """\ +Could not find line-contributors from git repository.%s""" + % """ \ +git-line-summary not found, please install git-extras. """ + * (git_line_summary_path is None) + ) + return [" ".join(line.strip().split()[1:-1]) for line in lines if "%" in line] + + +def _namelast(inlist): + retval = [] + for i in inlist: + i["name"] = (f"{i.pop('name', '')} {i.pop('lastname', '')}").strip() + retval.append(i) + return retval + + +@click.group() +def cli(): + """Generate authorship boilerplates.""" + pass + + +@cli.command() +@click.option("-z", "--zenodo-file", type=click.Path(exists=True), default=".zenodo.json") +@click.option("-m", "--maintainers", type=click.Path(exists=True), default=".maint/MAINTAINERS.md") +@click.option("-c", "--contributors", type=click.Path(exists=True), + default=".maint/CONTRIBUTORS.md") +@click.option("--pi", type=click.Path(exists=True), default=".maint/PIs.md") +@click.option("-f", "--former-file", type=click.Path(exists=True), default=".maint/FORMER.md") +def zenodo( + zenodo_file, + maintainers, + contributors, + pi, + former_file, +): + """Generate a new Zenodo payload file.""" + data = get_git_lines() + + zenodo = json.loads(Path(zenodo_file).read_text()) + + former = _namelast(read_md_table(Path(former_file).read_text())) + zen_creators, miss_creators = sort_contributors( + _namelast(read_md_table(Path(maintainers).read_text())), + data, + exclude=former, + ) + + zen_contributors, miss_contributors = sort_contributors( + _namelast(read_md_table(Path(contributors).read_text())), + data, + exclude=former + ) + + zen_pi = _namelast( + sorted( + read_md_table(Path(pi).read_text()), + key=lambda v: (int(v.get("position", -1)), v.get("lastname")) + ) + ) + + zenodo["creators"] = zen_creators + zenodo["contributors"] = zen_contributors + zen_pi + + misses = set(miss_creators).intersection(miss_contributors) + if misses: + print( + "Some people made commits, but are missing in .maint/ " + f"files: {', '.join(misses)}", + file=sys.stderr, + ) + + # Remove position + for creator in zenodo["creators"]: + creator.pop("position", None) + creator.pop("handle", None) + if isinstance(creator["affiliation"], list): + creator["affiliation"] = creator["affiliation"][0] + + for creator in zenodo["contributors"]: + creator.pop("handle", None) + creator["type"] = "Researcher" + creator.pop("position", None) + + if isinstance(creator["affiliation"], list): + creator["affiliation"] = creator["affiliation"][0] + + Path(zenodo_file).write_text( + "%s\n" % json.dumps(zenodo, indent=2) + ) + + +@cli.command() +@click.option("-m", "--maintainers", type=click.Path(exists=True), default=".maint/MAINTAINERS.md") +@click.option("-c", "--contributors", type=click.Path(exists=True), + default=".maint/CONTRIBUTORS.md") +@click.option("--pi", type=click.Path(exists=True), default=".maint/PIs.md") +@click.option("-f", "--former-file", type=click.Path(exists=True), default=".maint/FORMER.md") +def publication( + maintainers, + contributors, + pi, + former_file, +): + """Generate the list of authors and affiliations for papers.""" + members = ( + _namelast(read_md_table(Path(maintainers).read_text())) + + _namelast(read_md_table(Path(contributors).read_text())) + ) + + hits, misses = sort_contributors( + members, + get_git_lines(), + exclude=_namelast(read_md_table(Path(former_file).read_text())), + ) + + pi_hits = _namelast( + sorted( + read_md_table(Path(pi).read_text()), + key=lambda v: (int(v.get("position", -1)), v.get("lastname")) + ) + ) + + pi_names = [pi["name"] for pi in pi_hits] + hits = [ + hit for hit in hits + if hit["name"] not in pi_names + ] + pi_hits + + def _aslist(value): + if isinstance(value, (list, tuple)): + return value + return [value] + + # Remove position + affiliations = [] + for item in hits: + item.pop("position", None) + for a in _aslist(item.get("affiliation", "Unaffiliated")): + if a not in affiliations: + affiliations.append(a) + + aff_indexes = [ + ", ".join( + [ + "%d" % (affiliations.index(a) + 1) + for a in _aslist(author.get("affiliation", "Unaffiliated")) + ] + ) + for author in hits + ] + + if misses: + print( + "Some people made commits, but are missing in .maint/ " + f"files: {', '.join(misses)}", + file=sys.stderr, + ) + + print("Authors (%d):" % len(hits)) + print( + "%s." + % "; ".join( + [ + "%s \\ :sup:`%s`\\ " % (i["name"], idx) + for i, idx in zip(hits, aff_indexes) + ] + ) + ) + + print( + "\n\nAffiliations:\n%s" + % "\n".join( + ["{0: >2}. {1}".format(i + 1, a) for i, a in enumerate(affiliations)] + ) + ) + + +if __name__ == "__main__": + """ Install entry-point """ + cli() diff --git a/.maint/update_zenodo.py b/.maint/update_zenodo.py deleted file mode 100755 index af9a97268..000000000 --- a/.maint/update_zenodo.py +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env python3 -"""Update and sort the creators list of the zenodo record.""" -import sys -from pathlib import Path -import json -from fuzzywuzzy import fuzz, process - -# These ORCIDs should go last -CREATORS_LAST = ["Rokem, Ariel", "Esteban, Oscar"] -CONTRIBUTORS_LAST = ["Poldrack, Russell A."] - - -def sort_contributors(entries, git_lines, exclude=None, last=None): - """Return a list of author dictionaries, ordered by contribution.""" - last = last or [] - sorted_authors = sorted(entries, key=lambda i: i["name"]) - - first_last = [ - " ".join(val["name"].split(",")[::-1]).strip() for val in sorted_authors - ] - first_last_excl = [ - " ".join(val["name"].split(",")[::-1]).strip() for val in exclude or [] - ] - - unmatched = [] - author_matches = [] - position = 1 - for ele in git_lines: - matches = process.extract( - ele, first_last, scorer=fuzz.token_sort_ratio, limit=2 - ) - # matches is a list [('First match', % Match), ('Second match', % Match)] - if matches[0][1] > 80: - val = sorted_authors[first_last.index(matches[0][0])] - else: - # skip unmatched names - if ele not in first_last_excl: - unmatched.append(ele) - continue - - if val not in author_matches: - val["position"] = position - author_matches.append(val) - position += 1 - - names = {" ".join(val["name"].split(",")[::-1]).strip() for val in author_matches} - for missing_name in first_last: - if missing_name not in names: - missing = sorted_authors[first_last.index(missing_name)] - missing["position"] = position - author_matches.append(missing) - position += 1 - - all_names = [val["name"] for val in author_matches] - for last_author in last: - author_matches[all_names.index(last_author)]["position"] = position - position += 1 - - author_matches = sorted(author_matches, key=lambda k: k["position"]) - - return author_matches, unmatched - - -def get_git_lines(fname="line-contributors.txt"): - """Run git-line-summary.""" - import shutil - import subprocess as sp - - contrib_file = Path(fname) - - lines = [] - if contrib_file.exists(): - print("WARNING: Reusing existing line-contributors.txt file.", file=sys.stderr) - lines = contrib_file.read_text().splitlines() - - git_line_summary_path = shutil.which("git-line-summary") - if not lines and git_line_summary_path: - print("Running git-line-summary on repo") - lines = sp.check_output([git_line_summary_path]).decode().splitlines() - lines = [l for l in lines if "Not Committed Yet" not in l] - contrib_file.write_text("\n".join(lines)) - - if not lines: - raise RuntimeError( - """\ -Could not find line-contributors from git repository.%s""" - % """ \ -git-line-summary not found, please install git-extras. """ - * (git_line_summary_path is None) - ) - return [" ".join(line.strip().split()[1:-1]) for line in lines if "%" in line] - - -if __name__ == "__main__": - data = get_git_lines() - - zenodo_file = Path(".zenodo.json") - zenodo = json.loads(zenodo_file.read_text()) - - creators = json.loads(Path(".maint/developers.json").read_text()) - zen_creators, miss_creators = sort_contributors( - creators, - data, - exclude=json.loads(Path(".maint/former.json").read_text()), - last=CREATORS_LAST, - ) - contributors = json.loads(Path(".maint/contributors.json").read_text()) - zen_contributors, miss_contributors = sort_contributors( - contributors, - data, - exclude=json.loads(Path(".maint/former.json").read_text()), - last=CONTRIBUTORS_LAST, - ) - zenodo["creators"] = zen_creators - zenodo["contributors"] = zen_contributors - - misses = set(miss_creators).intersection(miss_contributors) - if misses: - print( - "Some people made commits, but are missing in .maint/ " - f"files: {', '.join(misses)}", - file=sys.stderr, - ) - - # Remove position - for creator in zenodo["creators"]: - del creator["position"] - if isinstance(creator["affiliation"], list): - creator["affiliation"] = creator["affiliation"][0] - - for creator in zenodo["contributors"]: - creator["type"] = "Researcher" - del creator["position"] - if isinstance(creator["affiliation"], list): - creator["affiliation"] = creator["affiliation"][0] - - zenodo_file.write_text("%s\n" % json.dumps(zenodo, indent=2)) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..f1802415c --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Code of Conduct + +This Project belongs to the [*NiPreps* Community, and is under its code of conduct](https://www.nipreps.org/community/CODE_OF_CONDUCT/). \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1b2c7e8d2..680dc1016 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,2 +1,3 @@ -Please check the [contributing guidelines of the NiPreps organization](https://www.nipreps.org/community/CONTRIBUTING) +# Contributing +This Project belongs to the [*NiPreps* Community, which specifies the contributing guidelines](https://www.nipreps.org/community/CONTRIBUTING). \ No newline at end of file diff --git a/GOVERNANCE.md b/GOVERNANCE.md new file mode 100644 index 000000000..fa4b61c2d --- /dev/null +++ b/GOVERNANCE.md @@ -0,0 +1,131 @@ +# Abstract + +This Project is consensus-based and belongs in the *NiPreps (NeuroImaging PREProcessing toolS)* Community. +Anyone with an interest in the Project can join the Community, contribute to the Project design, and participate in the decision making process. This document describes how that participation takes place, how to find consensus, and how deadlocks are resolved. + +# Governance Policy + +This document provides the governance policy for the Project. +In general, all *NiPreps Projects* embrace a [liberal contribution model](https://opensource.guide/leadership-and-governance/#what-are-some-of-the-common-governance-structures-for-open-source-projects) of governance structure. +However, because of the scientific domain of NiPreps , the community features some structure from *meritocracy models* to prescribe [the order in the authors list of new papers](https://www.nipreps.org/community/CONTRIBUTING/#publications) about these tools. + +## 1. Roles. + +This project may include the following roles. Additional roles may be adopted and documented by the Project. + +**1.1. Maintainers**. +Maintainers are members of a wonderful team *driving the Project* and thereby are responsible for organizing activities around developing, maintaining, and updating the Project. +Other examples of activities that drive the Project are: actively participating in the follow-up meetings, leading documentation sprints, helping in the design of the tool and definition of the roadmap, providing resources (in the broad sense, including funding), code-review, etc. +Maintainers are also responsible for determining consensus. +This Project may add or remove Maintainers with the approval of the current Maintainers. +Maintainers agree to this policy and to abide by all Project polices, including the code of conduct, trademark policy, and antitrust policy by adding their name to the `.maint/MAINTAINERS.md` file. + +**1.2. Contributors**. Contributors are those who actively help or have previously helped the Project in a broad sense. +Any community member can become a contributor by interacting directly with the project in concrete ways, such as: + +* proposing new features, changes to the code or documentation improvements via a GitHub pull request; +* contributing with benchmarking modules of the tool; +* reporting issues on our GitHub issues page; +* helping improve the scientific rigor of implementations; +* giving out support on the different communication channels (mattermost, NeuroStars , GitHub, etc.); +* discussing the design of the library, website, or tutorials on the mailing list, or in existing issues and pull requests; or +* reviewing open pull requests; + +among other possibilities. +By contributing to the project, community members can directly help to shape its future. + +Contributors should read the [*NiPreps Community's guidelines*](https://www.nipreps.org/community/), which contain a [code of conduct](https://www.nipreps.org/community/CODE_OF_CONDUCT/), [contributing guidelines](https://www.nipreps.org/community/CONTRIBUTING/), and [criteria for accepting new features](https://www.nipreps.org/community/features/), amongst other relevant documents. +Contributors agree to this policy and to abide by all Project polices by adding their name to the `.maint/CONTRIBUTORS.md` file. + +**1.3. Principal investigators**. +PIs are those who provide or have provided institutional resources (personnel, funding, etc.) to the Project, and play a supervision role over the development of the Project. +PIs have reserved the last position as senior authors of papers and other dissemination activities. +This Project may add or remove PIs with the approval of the Organization Steering Committee. +PIs enlisted in the `.maint/PIs.md` file agree to this policy and to abide by all Project polices. + +**1.4. Former members**. +Those who have contributed at some point to the Project but were required or they wished to disconnect from the Project's updates and to drop-out from publications and other dissemination activities, are listed in the `.maint/FORMER.md` file. + +## 2. Decisions. + +**2.1. Consensus-Based Decision Making**. +Projects make decisions through consensus of the Maintainers. +While explicit agreement of all Maintainers is preferred, it is not required for consensus. +Rather, the Maintainers will determine consensus based on their good faith consideration of a number of factors, including the dominant view of the Contributors and nature of support and objections. +The Maintainers will document evidence of consensus in accordance with these requirements. + +**2.2. Appeal Process**. Decisions may be appealed by opening an issue and that appeal will be considered by the Maintainers in good faith, who will respond in writing within a reasonable time. If the Maintainers deny the appeal, the appeal my be brought before the Organization Steering Committee, who will also respond in writing in a reasonable time. + +## 3. How We Work. + +**3.1. Openness**. Participation is open to anyone who is directly and materially affected by the activity in question. There shall be no undue financial barriers to participation. + +**3.2. Balance**. The development process should balance the interests of Contributors and other stakeholders. Contributors from diverse interest categories shall be sought with the objective of achieving balance. + +**3.3. Coordination and Harmonization**. Good faith efforts shall be made to resolve potential conflicts or incompatibility between releases in this Project. + +**3.4. Consideration of Views and Objections**. Prompt consideration shall be given to the written views and objections of all Contributors. + +**3.5. Written procedures**. This governance document and other materials documenting this project's development process shall be available to any interested person. + +**3.6. Community roadmap and release planning**. +Maintainers shall keep open planification documents and roadmap definition and maintenance, and are responsible to seek feedback and engage contributors in the process of defining the future lines of the Project. +Principal investigators will inform Maintainers about the existing commitments with funding agencies and other relevant institutions in terms of deliverables and milestones that the Project might need to observe to respond to such commitments. +In case of conflict between the Maintainers' criteria and the Principal investigators' proposal, an appeal must be addressed to the Organization Steering Committee. +The Project may maintain a `.maint/ROADMAP.md` stating how roadmaps are built, how issues are prioritized and organized, and the how the release process and achievement of deliverables and milestones are monitored. + +## 4. Release process and Scientific publication. + +### 4.1. Releases + +This Project follows the *NiPreps Community* guidelines for [releases](https://www.nipreps.org/devs/releases/) and [version synchrony](https://www.nipreps.org/devs/versions/). +The release process may be initiated by any Maintainer, who will follow the prescribed documentation to the effect. +This project has an automated deployment pipeline triggered with a git tagging operation by a Maintainer, which ensures the minting of the correct version number, python and docker packaging, publication of packages in open repositories and finally, the posting of a new release entry at Zenodo. + +**Long-term support (LTS) release series**. +End-user applications may commit to extended windows of support for particular version series (see [related documentation here](https://www.nipreps.org/devs/releases/#long-term-support-series)). +Past release series are maintained with *maintenance* branches named `maint/..x` (for instance fMRIPrep's `maint/20.2.x`). +For those maintenance branches tagged as LTS series, the `.maint/MAINTAINERS.md` and the `.maint/PIs.md` may differ from those of the development (called `master` or `main`) branch. +New contributors to a maintenance branch will be added to the `.maint/CONTRIBUTORS.md` file of that branch, and then upstreamed to posterior `maint/` branches and the `master`/`main` branch of the repository. + +### 4.2. Posting releases on Zenodo + +In the absence of higher-priority scientific publications, the appropriate Zenodo entry should be cited when referencing the Project. +Metadata submitted to the Zenodo repository is contained in the `/.zenodo.json` file at the root of the Project repository. +Before every new release, the metadata containing the authors and contributors of the Project must be updated running `python .maint/update_authors.py zenodo` from the root of the repository, on the appropriate release branch. + +### 4.3. Scientific publication + +Anyone listed as a Maintainer or a Contributor is invited to prepare and submit manuscript to journals as first author. +To compose the author list, all the Maintainers of the Project MUST be included and notified; and all the Contributors MUST be invited to participate. +First authorship(s) is (are) reserved for the authors that originated and kept the initiative of submission and wrote the manuscript. +Finally Principal Investigators are appended to the end of the author's list. + +To generate the ordering of your paper, please run `python .maint/update_authors.py publication` from the root of the repository, on the up-to-date upstream/master branch (or the appropriate tag this publication is based off). +Then, please modify this list and place your name first. + +**Public announcement**. +Publishing initiatives must be properly publicized through appropriate channels (GitHub discussions, NiPy Discourse, email lists, etc.) and made with sufficient anticipation for the Community to take corrective measures. + +**Open science pledge**. +*NiPreps* and its community adheres to open science principles, such that a pre-print should be posted on an adequate archive service (e.g., ArXiv or BioRxiv) prior publication. + +**Disagreement**. +In the case that any member of the community objects to the initiative of submitting a paper derived from the Project or its activities, or objects to the tentative individuals and/or their ordering in the author's list, the Principal Investigators of the project will determine a solution in coordination with the Maintainers. +If such a solution would not be acceptable by all the affected parties, an appeal may be addressed to the Organization's Steering Committee, which will reach out to the Principal Investigators to fully understand the conflict and establish the ultimate solution. + +## 5. No Confidentiality. + +Information disclosed in connection with any Project activity, including but not limited to meetings, contributions, and submissions, is not confidential, regardless of any markings or statements to the contrary. + +## 6. Trademarks. + +Any names, trademarks, logos, or goodwill developed by and associated with the Project (the "Marks") are controlled by the Organization. Maintainers may only use these Marks in accordance with the Organization's trademark policy. If a Maintainer resigns or is removed, any rights the Maintainer may have in the Marks revert to the Organization. + +## 7. Amendments. + +Amendments to this governance policy may be made by affirmative vote of 2/3 of all Maintainers, with approval by the Organization's Steering Committee. + +--- +Part of MVG-0.1-beta. +Made with love by GitHub. Licensed under the [CC-BY 4.0 License](https://creativecommons.org/licenses/by-sa/4.0/).