Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .circleci/version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Get sdcflows version."""

import sdcflows

print(sdcflows.__version__, end="", file=open("/tmp/.docker-version.txt", "w"))
print(sdcflows.__version__, end='', file=open('/tmp/.docker-version.txt', 'w'))
6 changes: 6 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Thu May 15 09:29:29 2025 -0400 - [email protected] - run: ruff format [ignore-rev]
d97ae316c0bdf71084a0732760ceed5221033fc2
# Thu May 15 09:26:57 2025 -0400 - [email protected] - run: ruff check --fix [ignore-rev]
cacd409cce71f2e530ad1bd1e8b79f397cab81d4
# Thu May 15 09:22:00 2025 -0400 - [email protected] - run: pre-commit run --all [ignore-rev]
2b40bee0d6b2c88628760a8b1513851a439411e8
2 changes: 1 addition & 1 deletion .git_archival.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
node: $Format:%H$
node-date: $Format:%cI$
describe-name: $Format:%(describe:tags=true,match=*[0-9]*)$
ref-names: $Format:%D$
ref-names: $Format:%D$
5 changes: 3 additions & 2 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ permissions:
contents: read

jobs:
flake8:
style:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pipx run flake8-pyproject sdcflows/
- run: pipx run ruff check sdcflows/
- run: pipx run ruff format --diff sdcflows/

# codespell:
# runs-on: ubuntu-latest
Expand Down
188 changes: 79 additions & 109 deletions .maint/update_authors.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
# ]
# ///
"""Update and sort the creators list of the zenodo record."""

import json
import sys
from pathlib import Path
import json

import click
from fuzzywuzzy import fuzz, process

Expand Down Expand Up @@ -43,21 +45,18 @@ def read_md_table(md_text):
keys = None
retval = []
for line in md_text.splitlines():
if line.strip().startswith("| --- |"):
keys = (
k.replace("*", "").strip()
for k in prev.split("|")
)
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("|"):
if not line or not line.strip().startswith('|'):
break

values = [v.strip() or None for v in line.split("|")][1:-1]
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
Expand All @@ -66,21 +65,15 @@ def read_md_table(md_text):
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"])
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 []
]
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 = 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])]
Expand All @@ -93,15 +86,15 @@ def sort_contributors(entries, git_lines, exclude=None, last=None):
if val not in author_matches:
author_matches.append(val)

names = {" ".join(val["name"].split(",")[::-1]).strip() for val in author_matches}
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)
pos = item.pop('position', None)
if pos is not None:
position_matches.append((i, int(pos)))

Expand All @@ -113,7 +106,7 @@ def sort_contributors(entries, git_lines, exclude=None, last=None):
return author_matches, unmatched


def get_git_lines(fname="line-contributors.txt"):
def get_git_lines(fname='line-contributors.txt'):
"""Run git-line-summary."""
import shutil
import subprocess as sp
Expand All @@ -122,17 +115,17 @@ def get_git_lines(fname="line-contributors.txt"):

lines = []
if contrib_file.exists():
print("WARNING: Reusing existing line-contributors.txt file.", file=sys.stderr)
print('WARNING: Reusing existing line-contributors.txt file.', file=sys.stderr)
lines = contrib_file.read_text().splitlines()

cmd = [shutil.which("git-line-summary")]
cmd = [shutil.which('git-line-summary')]
if cmd == [None]:
cmd = [shutil.which("git-summary"), "--line"]
cmd = [shutil.which('git-summary'), '--line']
if not lines and cmd[0]:
print(f"Running {' '.join(cmd)!r} on repo")
print(f'Running {" ".join(cmd)!r} on repo')
lines = sp.check_output(cmd).decode().splitlines()
lines = [l for l in lines if "Not Committed Yet" not in l]
contrib_file.write_text("\n".join(lines))
lines = [line for line in lines if 'Not Committed Yet' not in line]
contrib_file.write_text('\n'.join(lines))

if not lines:
raise RuntimeError(
Expand All @@ -142,15 +135,15 @@ def get_git_lines(fname="line-contributors.txt"):
git-line-summary not found, please install git-extras. """
* (cmd[0] is None)
)
return [" ".join(line.strip().split()[1:-1]) for line in lines if "%" in line]
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()
if not i["name"]:
i["name"] = i.get("handle", "<Unknown Name>")
i['name'] = (f'{i.pop("name", "")} {i.pop("lastname", "")}').strip()
if not i['name']:
i['name'] = i.get('handle', '<Unknown Name>')
retval.append(i)
return retval

Expand All @@ -162,12 +155,13 @@ def cli():


@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")
@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,
Expand All @@ -188,75 +182,62 @@ def zenodo(
)

zen_contributors, miss_contributors = sort_contributors(
_namelast(read_md_table(Path(contributors).read_text())),
data,
exclude=former
_namelast(read_md_table(Path(contributors).read_text())), data, exclude=former
)

zen_pi = _namelast(reversed(read_md_table(Path(pi).read_text())))

zenodo["creators"] = zen_creators
zenodo["contributors"] = zen_contributors + [
pi for pi in zen_pi if pi not in zen_contributors
]
creator_names = {
c["name"] for c in zenodo["creators"]
if c["name"] != "<Unknown Name>"
}

zenodo["contributors"] = [
c for c in zenodo["contributors"]
if c["name"] not in creator_names
]
zenodo['creators'] = zen_creators
zenodo['contributors'] = zen_contributors + [pi for pi in zen_pi if pi not in zen_contributors]
creator_names = {c['name'] for c in zenodo['creators'] if c['name'] != '<Unknown Name>'}

zenodo['contributors'] = [c for c in zenodo['contributors'] if c['name'] not in creator_names]

misses = set(miss_creators).intersection(miss_contributors)
if misses:
print(
"Some people made commits, but are missing in .maint/ "
f"files: {', '.join(misses)}",
f'Some people made commits, but are missing in .maint/ files: {", ".join(misses)}',
file=sys.stderr,
)

# Remove position
for creator in zenodo["creators"]:
creator.pop("position", None)
creator.pop("handle", None)
if "affiliation" not in creator:
creator["affiliation"] = "Unknown affiliation"
elif 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 "affiliation" not in creator:
creator["affiliation"] = "Unknown affiliation"
elif isinstance(creator["affiliation"], list):
creator["affiliation"] = creator["affiliation"][0]

Path(zenodo_file).write_text(
"%s\n" % json.dumps(zenodo, indent=2, ensure_ascii=False)
)
for creator in zenodo['creators']:
creator.pop('position', None)
creator.pop('handle', None)
if 'affiliation' not in creator:
creator['affiliation'] = 'Unknown affiliation'
elif 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 'affiliation' not in creator:
creator['affiliation'] = 'Unknown affiliation'
elif isinstance(creator['affiliation'], list):
creator['affiliation'] = creator['affiliation'][0]

Path(zenodo_file).write_text('%s\n' % json.dumps(zenodo, indent=2, ensure_ascii=False))


@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")
@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()))
members = _namelast(read_md_table(Path(maintainers).read_text())) + _namelast(
read_md_table(Path(contributors).read_text())
)
former_names = _namelast(read_md_table(Path(former_file).read_text()))

Expand All @@ -267,11 +248,8 @@ def publication(
)

pi_hits = _namelast(reversed(read_md_table(Path(pi).read_text())))
pi_names = [pi["name"] for pi in pi_hits]
hits = [
hit for hit in hits
if hit["name"] not in pi_names
] + pi_hits
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)):
Expand All @@ -281,47 +259,39 @@ def _aslist(value):
# Remove position
affiliations = []
for item in hits:
item.pop("position", None)
for a in _aslist(item.get("affiliation", "Unaffiliated")):
item.pop('position', None)
for a in _aslist(item.get('affiliation', 'Unaffiliated')):
if a not in affiliations:
affiliations.append(a)

aff_indexes = [
", ".join(
', '.join(
[
"%d" % (affiliations.index(a) + 1)
for a in _aslist(author.get("affiliation", "Unaffiliated"))
'%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)}",
f'Some people made commits, but are missing in .maint/ files: {", ".join(misses)}',
file=sys.stderr,
)

print("Authors (%d):" % len(hits))
print('Authors (%d):' % len(hits))
print(
"%s."
% "; ".join(
[
"%s \\ :sup:`%s`\\ " % (i["name"], idx)
for i, idx in zip(hits, aff_indexes)
]
)
'%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)]
)
'\n\nAffiliations:\n%s'
% '\n'.join(['{0: >2}. {1}'.format(i + 1, a) for i, a in enumerate(affiliations)])
)


if __name__ == "__main__":
if __name__ == '__main__':
""" Install entry-point """
cli()
3 changes: 1 addition & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
exclude: ".*/data/.*"
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v5.0.0
hooks:
- id: check-added-large-files
- id: check-docstring-first
- id: check-merge-conflict
- id: check-json
- id: check-toml
Expand Down
Loading
Loading