diff --git a/_extensions/gh_repo.py b/_extensions/gh_repo.py index 164c69a..fd187b9 100644 --- a/_extensions/gh_repo.py +++ b/_extensions/gh_repo.py @@ -9,13 +9,12 @@ # https://docutils.readthedocs.io/en/sphinx-docs/howto/rst-roles.html -def gh_repo(name, rawtext, text, lineno, inliner, - options=None, content=None): +def gh_repo(name, rawtext, text, lineno, inliner, options=None, content=None): url = f"https://github.com/{text}" - node = nodes.reference(rawtext, f'{text}', refuri=url, **(options or {})) + node = nodes.reference(rawtext, f"{text}", refuri=url, **(options or {})) return [node], [] def setup(app): - app.add_role('gh-repo', gh_repo) - return {'version': '1.0', 'parallel_read_safe': True} + app.add_role("gh-repo", gh_repo) + return {"version": "1.0", "parallel_read_safe": True} diff --git a/_extensions/slack.py b/_extensions/slack.py index d60225c..1a8a74e 100644 --- a/_extensions/slack.py +++ b/_extensions/slack.py @@ -13,32 +13,46 @@ import strictyaml as yaml -BOARD_HISTORY_SCHEMA = yaml.Seq(yaml.Map({ - 'from': yaml.Datetime(), - 'members': yaml.MapPattern(yaml.Str(), yaml.Str()), -})) -BOARD_HISTORY_PATH = Path(__file__).parent.parent / 'board.yml' -BOARD_HISTORY = sorted(yaml.load(BOARD_HISTORY_PATH.read_text(), - BOARD_HISTORY_SCHEMA).data, - key=itemgetter('from'), reverse=True) +BOARD_HISTORY_SCHEMA = yaml.Seq( + yaml.Map( + { + "from": yaml.Datetime(), + "members": yaml.MapPattern(yaml.Str(), yaml.Str()), + } + ) +) +BOARD_HISTORY_PATH = Path(__file__).parent.parent / "board.yml" +BOARD_HISTORY = sorted( + yaml.load(BOARD_HISTORY_PATH.read_text(), BOARD_HISTORY_SCHEMA).data, + key=itemgetter("from"), + reverse=True, +) # https://docutils.readthedocs.io/en/sphinx-docs/howto/rst-roles.html -def slack(name, rawtext, text, lineno, inliner, - options=None, content=None, pyvec_board_years=None): - text = text.lstrip('#') - - if pyvec_board_years and text.startswith('pyvec-board'): - text = f'pyvec-board-{pyvec_board_years[0]}-{pyvec_board_years[1]}' +def slack( + name, + rawtext, + text, + lineno, + inliner, + options=None, + content=None, + pyvec_board_years=None, +): + text = text.lstrip("#") + + if pyvec_board_years and text.startswith("pyvec-board"): + text = f"pyvec-board-{pyvec_board_years[0]}-{pyvec_board_years[1]}" url = f"https://pyvec.slack.com/app_redirect?channel={text}" - node = nodes.reference(rawtext, f'#{text}', refuri=url, **(options or {})) + node = nodes.reference(rawtext, f"#{text}", refuri=url, **(options or {})) return [node], [] def setup(app): - board_year_from = BOARD_HISTORY[0]['from'].year + board_year_from = BOARD_HISTORY[0]["from"].year board_years = (board_year_from, board_year_from + 3) - app.add_role('slack', functools.partial(slack, pyvec_board_years=board_years)) - return {'version': '1.0', 'parallel_read_safe': True} + app.add_role("slack", functools.partial(slack, pyvec_board_years=board_years)) + return {"version": "1.0", "parallel_read_safe": True} diff --git a/_extensions/test_slack.py b/_extensions/test_slack.py index 86673bb..9d666fc 100644 --- a/_extensions/test_slack.py +++ b/_extensions/test_slack.py @@ -3,19 +3,30 @@ def test_slack(): - nodes, _ = slack('slack', ':slack:`#pyvo`', '#pyvo', None, None) + nodes, _ = slack("slack", ":slack:`#pyvo`", "#pyvo", None, None) - assert nodes[0]['refuri'] == 'https://pyvec.slack.com/app_redirect?channel=pyvo' + assert nodes[0]["refuri"] == "https://pyvec.slack.com/app_redirect?channel=pyvo" -@pytest.mark.parametrize('text, expected', [ - ("pyvo", "pyvo"), - ("pyvec-board", "pyvec-board-2000-3000"), - ("pyvec-board-", "pyvec-board-2000-3000"), - ("pyvec-board-1987-4587", "pyvec-board-2000-3000"), -]) +@pytest.mark.parametrize( + "text, expected", + [ + ("pyvo", "pyvo"), + ("pyvec-board", "pyvec-board-2000-3000"), + ("pyvec-board-", "pyvec-board-2000-3000"), + ("pyvec-board-1987-4587", "pyvec-board-2000-3000"), + ], +) def test_slack_board(text, expected): - nodes, _ = slack('slack', f':slack:`#{text}`', f'#{text}', None, None, - pyvec_board_years=(2000, 3000)) + nodes, _ = slack( + "slack", + f":slack:`#{text}`", + f"#{text}", + None, + None, + pyvec_board_years=(2000, 3000), + ) - assert nodes[0]['refuri'] == f'https://pyvec.slack.com/app_redirect?channel={expected}' + assert ( + nodes[0]["refuri"] == f"https://pyvec.slack.com/app_redirect?channel={expected}" + ) diff --git a/_extensions/twitter.py b/_extensions/twitter.py index 2ae7b66..428cca3 100644 --- a/_extensions/twitter.py +++ b/_extensions/twitter.py @@ -9,14 +9,13 @@ # https://docutils.readthedocs.io/en/sphinx-docs/howto/rst-roles.html -def twitter(name, rawtext, text, lineno, inliner, - options=None, content=None): - text = text.lstrip('@') +def twitter(name, rawtext, text, lineno, inliner, options=None, content=None): + text = text.lstrip("@") url = f"https://twitter.com/{text}" - node = nodes.reference(rawtext, f'@{text}', refuri=url, **(options or {})) + node = nodes.reference(rawtext, f"@{text}", refuri=url, **(options or {})) return [node], [] def setup(app): - app.add_role('twitter', twitter) - return {'version': '1.0', 'parallel_read_safe': True} + app.add_role("twitter", twitter) + return {"version": "1.0", "parallel_read_safe": True} diff --git a/_scripts/generate_grants.py b/_scripts/generate_grants.py index a3bcbdc..1d8a641 100644 --- a/_scripts/generate_grants.py +++ b/_scripts/generate_grants.py @@ -9,104 +9,119 @@ import strictyaml as yaml -GITHUB_TOKEN = os.getenv('GITHUB_TOKEN') -GITHUB_API_HEADERS = {'Accept': 'application/vnd.github.squirrel-girl-preview', - 'Authorization': f'token {GITHUB_TOKEN}'} -GITHUB_API_URL = 'https://api.github.com/repos/pyvec/money/issues' -REACTIONS_MAPPING = {'+1': 'ano', '-1': 'ne', 'eyes': 'zdržel(a) se'} +GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") +GITHUB_API_HEADERS = { + "Accept": "application/vnd.github.squirrel-girl-preview", + "Authorization": f"token {GITHUB_TOKEN}", +} +GITHUB_API_URL = "https://api.github.com/repos/pyvec/money/issues" +REACTIONS_MAPPING = {"+1": "ano", "-1": "ne", "eyes": "zdržel(a) se"} CONTENT_PATH = Path(__file__).parent.parent -BOARD_HISTORY_SCHEMA = yaml.Seq(yaml.Map({ - 'from': yaml.Datetime(), - 'members': yaml.MapPattern(yaml.Str(), yaml.Str()), -})) -BOARD_HISTORY_PATH = Path(__file__).parent.parent / 'board.yml' -BOARD_HISTORY = sorted(yaml.load(BOARD_HISTORY_PATH.read_text(), - BOARD_HISTORY_SCHEMA).data, - key=itemgetter('from'), reverse=True) +BOARD_HISTORY_SCHEMA = yaml.Seq( + yaml.Map( + { + "from": yaml.Datetime(), + "members": yaml.MapPattern(yaml.Str(), yaml.Str()), + } + ) +) +BOARD_HISTORY_PATH = Path(__file__).parent.parent / "board.yml" +BOARD_HISTORY = sorted( + yaml.load(BOARD_HISTORY_PATH.read_text(), BOARD_HISTORY_SCHEMA).data, + key=itemgetter("from"), + reverse=True, +) def to_date(iso_datetime_string): - iso_date_string, _ = iso_datetime_string.split('T') + iso_date_string, _ = iso_datetime_string.split("T") return date.fromisoformat(iso_date_string) def remove_comments(html): - return re.sub(r'', '', html).strip() + return re.sub(r"", "", html).strip() def get_board_member_name(username, voted_at, board_history=None): board_history = board_history or BOARD_HISTORY for board in board_history: # sorted from the most recent - if voted_at > board['from'].date(): - return board['members'].get(username) + if voted_at > board["from"].date(): + return board["members"].get(username) return None def get_votes(reactions, voted_at, board_history=None): for reaction in reactions: - username = reaction['user']['login'] + username = reaction["user"]["login"] name = get_board_member_name(username, voted_at, board_history) if name: # else not reaction from a board member - text = REACTIONS_MAPPING.get(reaction['content']) + text = REACTIONS_MAPPING.get(reaction["content"]) if text: - yield {'name': name, 'text': text} + yield {"name": name, "text": text} def get_lock_date(events): for event in reversed(events): - if event['event'] == 'locked': - return to_date(event['created_at']) + if event["event"] == "locked": + return to_date(event["created_at"]) -if __name__ == '__main__': - res = requests.get(GITHUB_API_URL, headers=GITHUB_API_HEADERS, - params={'per_page': 100, 'state': 'closed'}) +if __name__ == "__main__": + res = requests.get( + GITHUB_API_URL, + headers=GITHUB_API_HEADERS, + params={"per_page": 100, "state": "closed"}, + ) res.raise_for_status() grants = [] for issue in res.json(): - - labels = [label['name'] for label in issue['labels']] - if 'approved' not in labels and 'rejected' not in labels: + labels = [label["name"] for label in issue["labels"]] + if "approved" not in labels and "rejected" not in labels: # skip unlabeled, e.g. https://github.com/pyvec/money/issues/1 continue - if issue['locked']: - res = requests.get(issue['events_url'], - headers=GITHUB_API_HEADERS, - params={'per_page': 100}) + if issue["locked"]: + res = requests.get( + issue["events_url"], + headers=GITHUB_API_HEADERS, + params={"per_page": 100}, + ) res.raise_for_status() - if res.headers.get('link'): - raise NotImplementedError(f"The number of events of issue #{issue['number']} " - "is paginated and this code isn't yet designed " - "to handle this!") + if res.headers.get("link"): + raise NotImplementedError( + f"The number of events of issue #{issue['number']} " + "is paginated and this code isn't yet designed " + "to handle this!" + ) else: voted_at = get_lock_date(res.json()) else: - voted_at = to_date(issue['closed_at']) + voted_at = to_date(issue["closed_at"]) - res = requests.get(issue['reactions']['url'], - headers=GITHUB_API_HEADERS) + res = requests.get(issue["reactions"]["url"], headers=GITHUB_API_HEADERS) res.raise_for_status() votes = list(get_votes(res.json(), voted_at)) - grants.append({ - 'title': issue['title'], - 'description': remove_comments(issue['body']), - 'url': issue['html_url'], - 'user': { - 'username': issue['user']['login'], - 'url': issue['user']['html_url'], - }, - 'is_approved': 'approved' in labels, - 'filed_at': to_date(issue['created_at']), - 'voted_at': voted_at, - 'votes': votes, - }) - grants = sorted(grants, key=itemgetter('voted_at'), reverse=True) - - tpl_path = CONTENT_PATH / 'operations' / 'grants.rst.jinja' + grants.append( + { + "title": issue["title"], + "description": remove_comments(issue["body"]), + "url": issue["html_url"], + "user": { + "username": issue["user"]["login"], + "url": issue["user"]["html_url"], + }, + "is_approved": "approved" in labels, + "filed_at": to_date(issue["created_at"]), + "voted_at": voted_at, + "votes": votes, + } + ) + grants = sorted(grants, key=itemgetter("voted_at"), reverse=True) + + tpl_path = CONTENT_PATH / "operations" / "grants.rst.jinja" tpl = Template(tpl_path.read_text()) print(tpl.render(grants=grants)) diff --git a/_scripts/test_generate_grants.py b/_scripts/test_generate_grants.py index 0bf90db..94b5ba7 100644 --- a/_scripts/test_generate_grants.py +++ b/_scripts/test_generate_grants.py @@ -2,77 +2,88 @@ import pytest -from generate_grants import (to_date, remove_comments, get_board_member_name, - get_votes) +from generate_grants import to_date, remove_comments, get_board_member_name, get_votes @pytest.fixture def board_history(): return [ # sorted! - {'from': datetime(2020, 1, 1), 'members': {'alice': 'Alice', - 'doubravka': 'Doubravka'}}, - {'from': datetime(2019, 1, 1), 'members': {'bobby': 'Bob'}}, + { + "from": datetime(2020, 1, 1), + "members": {"alice": "Alice", "doubravka": "Doubravka"}, + }, + {"from": datetime(2019, 1, 1), "members": {"bobby": "Bob"}}, ] def test_to_date(): - assert to_date('2020-02-12T13:22:01Z') == date(2020, 2, 12) + assert to_date("2020-02-12T13:22:01Z") == date(2020, 2, 12) def test_remove_comments(): - assert remove_comments(''' - - something - - ''') == 'something' - - -@pytest.mark.parametrize('username,voted_at,expected', [ - pytest.param('bobby', date(2019, 8, 30), 'Bob', id='bob-board-y'), - pytest.param('alice', date(2019, 8, 30), None, id='alice-board-n'), - pytest.param('bobby', date(2020, 8, 30), None, id='bob-board-n'), - pytest.param('alice', date(2020, 8, 30), 'Alice', id='alice-board-y'), -]) + assert ( + remove_comments( + """ + + something + + """ + ) + == "something" + ) + + +@pytest.mark.parametrize( + "username,voted_at,expected", + [ + pytest.param("bobby", date(2019, 8, 30), "Bob", id="bob-board-y"), + pytest.param("alice", date(2019, 8, 30), None, id="alice-board-n"), + pytest.param("bobby", date(2020, 8, 30), None, id="bob-board-n"), + pytest.param("alice", date(2020, 8, 30), "Alice", id="alice-board-y"), + ], +) def test_get_board_member_name(username, voted_at, board_history, expected): assert get_board_member_name(username, voted_at, board_history) == expected def test_get_votes_returns_only_board_members_votes(board_history): reactions = [ - {'user': {'login': 'bobby'}, 'content': '+1'}, - {'user': {'login': 'alice'}, 'content': '+1'}, - {'user': {'login': 'doubravka'}, 'content': '+1'}, + {"user": {"login": "bobby"}, "content": "+1"}, + {"user": {"login": "alice"}, "content": "+1"}, + {"user": {"login": "doubravka"}, "content": "+1"}, ] assert list(get_votes(reactions, date(2020, 8, 30), board_history)) == [ - {'name': 'Alice', 'text': 'ano'}, - {'name': 'Doubravka', 'text': 'ano'}, + {"name": "Alice", "text": "ano"}, + {"name": "Doubravka", "text": "ano"}, ] def test_get_votes_returns_only_relevant_emojis(board_history): reactions = [ - {'user': {'login': 'alice'}, 'content': '+1'}, - {'user': {'login': 'doubravka'}, 'content': 'heart'}, + {"user": {"login": "alice"}, "content": "+1"}, + {"user": {"login": "doubravka"}, "content": "heart"}, ] assert list(get_votes(reactions, date(2020, 8, 30), board_history)) == [ - {'name': 'Alice', 'text': 'ano'}, + {"name": "Alice", "text": "ano"}, ] -@pytest.mark.parametrize('content,expected', [ - ('+1', 'ano'), - ('-1', 'ne'), - ('eyes', 'zdržel(a) se'), -]) -def test_get_votes_understands_all_relevant_emojis(content, board_history, - expected): +@pytest.mark.parametrize( + "content,expected", + [ + ("+1", "ano"), + ("-1", "ne"), + ("eyes", "zdržel(a) se"), + ], +) +def test_get_votes_understands_all_relevant_emojis(content, board_history, expected): reactions = [ - {'user': {'login': 'alice'}, 'content': content}, - {'user': {'login': 'doubravka'}, 'content': 'heart'}, + {"user": {"login": "alice"}, "content": content}, + {"user": {"login": "doubravka"}, "content": "heart"}, ] assert list(get_votes(reactions, date(2020, 8, 30), board_history)) == [ - {'name': 'Alice', 'text': expected}, + {"name": "Alice", "text": expected}, ]