Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Standup rota: Use people-related classes and notify them in Slack #698

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
4 changes: 2 additions & 2 deletions tests/workspace/test_dependabot_rota.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import json

from workspace.dependabot.jobs import report_rota
from workspace.utils.people import People
from workspace.utils.people import PEOPLE


TEAM_REX = [People.JON, People.LUCY, People.KATIE]
TEAM_REX = [PEOPLE["Jon"], PEOPLE["Lucy"], PEOPLE["Katie"]]


def test_rota_report_on_monday(freezer, monkeypatch):
Expand Down
39 changes: 24 additions & 15 deletions tests/workspace/test_people.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
from workspace.utils.people import (
People,
get_formatted_slack_username,
get_person_from_github_username,
)
from workspace.utils.people import Person, PersonDict


ALICE = Person("Alice", "alice1974", "U07GX6L3KLA")
BOB = Person("Sir bob", "the_real_bob", "U05BP32LSAD")

TEST_PERSON_DICT = PersonDict(
{
"alice": ALICE,
"bob": BOB,
}
)

def test_get_person_from_github_username():
result = get_person_from_github_username("lucyb")

assert result == People.LUCY.value
class TestPersonDict:
def test_get_person_from_github_username(self):
result = TEST_PERSON_DICT.by_github_username("the_real_bob")

assert result == BOB

def test_get_person_from_github_username_returns_default():
result = get_person_from_github_username("TestUser")
def test_get_person_from_github_username_returns_default(self):
result = TEST_PERSON_DICT.by_github_username("TestUser")

assert result.github_username == "TestUser"
assert result.slack_username == "TestUser"
assert result.human_readable == "TestUser"
assert result.github_username == "TestUser"
assert result.slack_username == "TestUser"


def test_get_formatted_slack_username_returns_slack_user_id():
result = get_formatted_slack_username(People.LUCY.value)
class TestPerson:
def test_get_formatted_slack_username_returns_slack_user_id(self):
result = ALICE.get_formatted_slack_username()

assert result == "<@U035FT48KEK>"
assert result == "<@U07GX6L3KLA>"
4 changes: 2 additions & 2 deletions tests/workspace/test_standup_rota.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def test_daily_rota_odd_week(freezer):
},
{
"text": {
"text": "Monday: Jon (backup: Mike)",
"text": "Monday: <@U023ZG5H24R> (backup: Mike)",
"type": "mrkdwn",
},
"type": "section",
Expand All @@ -77,7 +77,7 @@ def test_daily_rota_even_week(freezer):
},
{
"text": {
"text": "Wednesday: Mary (backup: Steve)",
"text": "Wednesday: <@U07LKQ06Q8L> (backup: Steve)",
"type": "mrkdwn",
},
"type": "section",
Expand Down
9 changes: 5 additions & 4 deletions workspace/dependabot/jobs.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from datetime import date, timedelta
from itertools import cycle, islice

from workspace.utils.people import TEAM_REX, People, get_formatted_slack_username
from workspace.utils.people import PEOPLE, TEAM_REX
from workspace.utils.rota import RotaReporter


Expand All @@ -11,14 +11,15 @@ class DependabotRotaReporter(RotaReporter):
The candidates are currently Team Rex (except Katie).
If the candidate definition or Team changes this will affect
the rota offset and the rota will restart at an arbitrary point.
Consider redesigning class to include an offset if that happens.
"""

def get_rota(self) -> dict:
today = date.today()
this_monday = today - timedelta(days=today.weekday())
next_monday = this_monday + timedelta(weeks=1)

candidates = [r for r in TEAM_REX if r != People.KATIE]
candidates = [r for r in TEAM_REX if r != PEOPLE["Katie"]]
i = this_monday.isocalendar().week % len(candidates)

# allow looping around the end of the list in case i+1==len(candidates)
Expand All @@ -37,9 +38,9 @@ def get_rota_text_for_week(
) -> str:
checker = rota[monday]
if this_or_next == "this":
checker = get_formatted_slack_username(checker.value)
checker = checker.get_formatted_slack_username()
else:
checker = checker.name.title()
checker = checker.human_readable
return f"To review dependabot PRs {this_or_next} week ({self.format_week(monday)}): {checker}"


Expand Down
7 changes: 2 additions & 5 deletions workspace/report/generate_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@
import requests

from workspace.utils.blocks import get_basic_header_and_text_blocks
from workspace.utils.people import (
get_formatted_slack_username,
get_person_from_github_username,
)
from workspace.utils.people import PEOPLE


URL = "https://api.github.com/graphql"
Expand Down Expand Up @@ -174,7 +171,7 @@ def get_status_and_summary(card): # pragma: no cover
title = card["content"]["title"]
url = card["content"].get("bodyUrl")
assignees = " / ".join(
get_formatted_slack_username(get_person_from_github_username(node["login"]))
PEOPLE.by_github_username(node["login"]).get_formatted_slack_username()
for node in card["content"]["assignees"]["nodes"]
)

Expand Down
18 changes: 12 additions & 6 deletions workspace/standup/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
from datetime import date, datetime, timedelta

from workspace.utils import blocks
from workspace.utils.people import PEOPLE


PAIRS = {
"monday": ["Mike", "Jon"],
"wednesday": ["Mary", "Steve"],
"friday": ["Thomas", "Katie"],
"monday": [PEOPLE["Mike"], PEOPLE["Jon"]],
"wednesday": [PEOPLE["Mary"], PEOPLE["Steve"]],
"friday": [PEOPLE["Thomas"], PEOPLE["Katie"]],
}


Expand Down Expand Up @@ -38,11 +39,12 @@ def weekly_rota(args):
header = "Team Rex stand ups this week"
days = "\n".join(
[
f"{day_of_week.title()}: {PAIRS[day_of_week][primary]} (backup: {PAIRS[day_of_week][secondary]})"
f"{day_of_week.title()}: "
f"{PAIRS[day_of_week][primary].human_readable} "
f"(backup: {PAIRS[day_of_week][secondary].human_readable})"
for day_of_week in PAIRS.keys()
]
)

return json.dumps(blocks.get_basic_header_and_text_blocks(header, days))


Expand All @@ -53,7 +55,11 @@ def daily_rota(args):
primary = is_even_week(rota_date)
secondary = 0 if primary else 1
header = "Team Rex stand up"
body = f"{day_of_week.title()}: {PAIRS[day_of_week][primary]} (backup: {PAIRS[day_of_week][secondary]})"
body = (
f"{day_of_week.title()}: "
f"{PAIRS[day_of_week][primary].get_formatted_slack_username()} "
f"(backup: {PAIRS[day_of_week][secondary].human_readable})"
)

return json.dumps(blocks.get_basic_header_and_text_blocks(header, body))

Expand Down
105 changes: 60 additions & 45 deletions workspace/utils/people.py
Original file line number Diff line number Diff line change
@@ -1,56 +1,71 @@
from collections import namedtuple
from enum import Enum
from collections import UserDict, namedtuple


Person = namedtuple("Person", ["github_username", "slack_username"])
class Person(
namedtuple("Person", ["human_readable", "github_username", "slack_username"])
):
def get_formatted_slack_username(self) -> str:
return f"<@{self.slack_username}>"


class People(Enum):
"""Tech team members' GitHub and Slack usernames."""
class PersonDict(UserDict):
def __init__(self, initialdata=None):
super().__init__(initialdata)
self._by_github_username_dict = {
person.github_username: person for person in self.data.values()
}

# Find a Slack user's username by right-clicking on their name in the Slack app and clicking "Copy link".
def by_github_username(self, github_username):
default = Person(
human_readable=github_username,
github_username=github_username,
slack_username=github_username,
)
return self._by_github_username_dict.get(github_username, default)

ALICE = Person("alarthast", "U07KX6L3CMA")
BECKY = Person("rebkwok", "U01SP5JLBFD")
BEN_BC = Person("benbc", "U01SPCP06Q1")
CATHERINE = Person("CLStables", "U036A6LTR7D")
DAVE = Person("evansd", "UAXE5V4RG")
ELI = Person("eli-miriam", "U07LHEJ9TS4")
IAIN = Person("iaindillingham", "U01S6BLGK28")
JON = Person("Jongmassey", "U023ZG5H24R")
KATIE = Person("KatieB5", "U07KUKWBGKV")
LUCY = Person("lucyb", "U035FT48KEK")
MARY = Person("Mary-Anya", "U07LKQ06Q8L")
MILAN = Person("milanwiedemann", "U02GPV8NNU9")
MIKE = Person("mikerkelly", "U07KKL4PJJY")
PETER = Person("inglesp", "U4N1YPAP7")
PROVIDENCE = Person("Providence-o", "U07AGDM6ZJN")
RICHARD = Person("rw251", "U07QEMHUUMD")
SIMON = Person("bloodearnest", "U01AMBZUT47")
STEVE = Person("StevenMaude", "U01TJP3CG76")
THOMAS = Person("tomodwyer", "U01UQ0T2M7V")
TOM_P = Person("remlapmot", "U07L0L0SS6M")
TOM_W = Person("madwort", "U019R5FJ7G8")

# Tech team members' GitHub and Slack usernames.
# Find a Slack user's username by right-clicking on their name in the Slack app
# and clicking "Copy link".
PEOPLE = PersonDict(
{
person.human_readable: person
for person in [
Person("Alice", "alarthast", "U07KX6L3CMA"),
Person("Becky", "rebkwok", "U01SP5JLBFD"),
Person("Ben BC", "benbc", "U01SPCP06Q1"),
Person("Catherine", "CLStables", "U036A6LTR7D"),
Person("Dave", "evansd", "UAXE5V4RG"),
Person("Eli", "eli-miriam", "U07LHEJ9TS4"),
Person("Iain", "iaindillingham", "U01S6BLGK28"),
Person("Jon", "Jongmassey", "U023ZG5H24R"),
Person("Katie", "KatieB5", "U07KUKWBGKV"),
Person("Lucy", "lucyb", "U035FT48KEK"),
Person("Mary", "Mary-Anya", "U07LKQ06Q8L"),
Person("Milan", "milanwiedemann", "U02GPV8NNU9"),
Person("Mike", "mikerkelly", "U07KKL4PJJY"),
Person("Peter", "inglesp", "U4N1YPAP7"),
Person("Providence", "Providence-o", "U07AGDM6ZJN"),
Person("Richard", "rw251", "U07QEMHUUMD"),
Person("Simon", "bloodearnest", "U01AMBZUT47"),
Person("Steve", "StevenMaude", "U01TJP3CG76"),
Person("Thomas", "tomodwyer", "U01UQ0T2M7V"),
Person("Tom P", "remlapmot", "U07L0L0SS6M"),
Person("Tom W", "madwort", "U019R5FJ7G8"),
]
}
)

PEOPLE_BY_GITHUB_USERNAME = {
person.value.github_username: person.value for person in People
}

# Note that adding to or re-ordering this list will will affect the
# DependabotRotaReporter ordering algorithm, restarting it at an arbitrary
# point. If you need to change this list, consider redesigning that class to
# include an offset.
TEAM_REX = [
People.JON,
People.KATIE,
People.LUCY,
People.STEVE,
People.MIKE,
People.THOMAS,
PEOPLE["Jon"],
PEOPLE["Katie"],
PEOPLE["Lucy"],
PEOPLE["Steve"],
PEOPLE["Mike"],
PEOPLE["Thomas"],
]


def get_person_from_github_username(github_username) -> Person:
default = Person(github_username=github_username, slack_username=github_username)
return PEOPLE_BY_GITHUB_USERNAME.get(github_username, default)


def get_formatted_slack_username(person: Person) -> str:
return f"<@{person.slack_username}>"
Loading