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

Merged

Conversation

mikerkelly
Copy link
Contributor

@mikerkelly mikerkelly commented Jan 15, 2025

Fixes #695.

This uses the new people-related data structures and allows users to be named by Slack username in the standup daily monday repo, @-ing them in Slack. I tested this in the test Slack workspace (temporarily changing my own slack username locally as the username string is different per workspace apparently).

I don't think the DependabotRotaReporter class is suitable for this report as that cycles through a list of candidates and reports one of them each week, whereas the standup rota reports 6 people in a certain order each week, with the ordering alternating between "even" and "odd" weeks.

Also some changes to the class and test design that are hopefully beneficial, see individual commit messages.

@mikerkelly mikerkelly changed the title Standup rota: Use people class and notify them in Slack Standup rota: Use people-related classes and notify them in Slack Jan 15, 2025
@Jongmassey
Copy link
Contributor

One of the advantages of using an enum over a dict is that you get editor autocompletion which is easier and less error prone than using strings for the dictionary keys

@mikerkelly
Copy link
Contributor Author

mikerkelly commented Jan 16, 2025

One of the advantages of using an enum over a dict is that you get editor autocompletion which is easier and less error prone than using strings for the dictionary keys

Thanks, Jon, I wasn't aware of that. Sorry for clobbering over without prior discussion. I had the idea as I was approaching a meeting and it seemed more expedient to just get the example out in a commit and discuss in review.

Speaking purely personally I find either way about as convenient. I would note that it's also a little inconvenient to have to remember to dereference the value of the enum to access the actual Person object, which I had to remember to do manually a few times while developing this ticket and spent maybe a minute or two the first time confused about why People.XXX didn't have attributes I was adding to Person and understanding that I needed to use People.XXX.value.human_readable, for example. It's also a bit inconvenient to have to type the Person's name twice in two different formats when adding someone new to the list.

Specific members of the Enum are only accessed a few times in the code. I can see it could be much more convenient to have autocomplete in a different use case for Enum where it was used much more widely in the code, and the name was significant but the value was not. Note that the last couple of lines below don't exist anymore if we take up the change to test_people.py from 4f004cd to make it use test data rather than the real data. So it would only be a couple of instances at present.

workspace/dependabot/jobs.py:        candidates = [r for r in TEAM_REX if r != People.KATIE]
tests/workspace/test_dependabot_rota.py:TEAM_REX = [People.JON, People.LUCY, People.KATIE]
tests/workspace/test_people.py:    assert result == People.LUCY.value
tests/workspace/test_people.py:    result = get_formatted_slack_username(People.LUCY.value)

I suppose we need to weigh up the convenience for devs of autocomplete when using particular text editors against avoiding the need to remember to add .value to People.XXX; convenience when adding users; being able to test the people.py classes more directly and with test data rather than the real People; and being able to make get_person_from_github_username a class method. Happy to discuss, or just revert if you feel strongly.

@Jongmassey
Copy link
Contributor

Sorry for taking so long on this. I've had a think about your comment and I agree that dereferencing the value is inconvenient and error prone.

I've knocked up this draft which I think incorporates perhaps the best of both worlds, I'd love to hear your thoughts.

This will make it easier to read and see the changes in subsequent commits.
This allows access to richer information about people.

This enables a later commit in the same PR to use
`get_formatted_slack_username`. They're split up to keep the change per-commit
small.
A function that has as its only parameter an instance of a certain class is
effectively a method of that class, and making that explicit is slightly
clearer.
I don't think Enum is the best structure for accessing a set of instances.
Enum are typically used where you don't care about the actual value, and just
want to restrict the names used. An example given in the Python docs:

```
class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3
```

We do care about the Person object referenced. Enums are not normal Python
classes and have special properties and behaviours we don't rely on.  Defining
a simple class with static attributes avoids the need to dereference `.value`
in various places and risks of complications from using Enum where it isn't
strictly needed.

We can also make `get_person_from_github_username` a method of People rather
than a module-level function that relates to People.
This is a slightly richer class for defining data in code and can help editors
like VSCode with type hints and with type checking tools.
@mikerkelly mikerkelly force-pushed the mikerkelly/workspace/standup/use-people-class-and-ping-them branch from 81be38c to 2364b31 Compare January 31, 2025 09:49
@mikerkelly
Copy link
Contributor Author

mikerkelly commented Jan 31, 2025

@Jongmassey, thanks for this. My dict-based attempt was quite messy. I like your use of @classmethod and @property but I'm still not sure about extending Enum unless we need it. And I find moving the attributes and methods about the Person instances into the People class a bit awkward. I think it's even further from the normal use of Enum to start adding methods operating on the values. It's definitely better to have the .value dereferencing centralized rather than in clients, but I'd prefer avoiding it completely if we can find a good way to do that while keeping the benefits, as it seems a bit complicated for what we're trying to do.

I think the main benefit you want to see from using Enum is autocomplete, is that right? I think it's the static definition of attributes that allows the language server to provide that, not subclassing Enum. So we should be able to get the benefits of autocomplete without using subclassing Enum, which is perhaps unnecessary and complicating things.

I've tried rebasing to another attempt taking several of your improvements but subclassing Object for People rather than Enum. This gives me autocomplete in VSCode. It has methods for the github-based lookup, with caching. I'm using dataclass rather than namedtuple for Person to move the richness into the instances rather than the container class, which I think will be easier to extend if needed, and also allows type hinting (in VSCode People.MIKE.human_readable has a hovering type-hint that its type is string, for example). See what you think.

@Jongmassey
Copy link
Contributor

I've made People iterable as we discussed this morning on that basis you're off on AL for a few days :)

@mikerkelly mikerkelly force-pushed the mikerkelly/workspace/standup/use-people-class-and-ping-them branch 3 times, most recently from aeb17ff to c8b7e7b Compare February 6, 2025 15:07
This is to allow `for person in People` or similar to work.

Now `People.all` is not required.

Add ordering to the dataclass to allow the iterator to be sorted for a
consistent ordering.

Note that changing `TEAM_REX` affects `DependabotRotaReporter`.  This might
help avoid change happening without this linkage being noticed.
@mikerkelly mikerkelly force-pushed the mikerkelly/workspace/standup/use-people-class-and-ping-them branch 2 times, most recently from b3614a3 to 6ad2681 Compare February 6, 2025 15:24
`PersonCollection.__init__()` populates `human_readable` based on attribute
value if not set explicitly. This occurs when the class definition is read.
This avoids the need to explicitly specify it for most people, when it's mostly
duplicative of the attribute value, for example MIKE => Mike.

Cases where the attempted conversion doesn't give the result we want can still
be manually specified in the definition of `People`. For example, with "BEN_BC"
it's not easy to determine programatically that all of the second word should
be capitalized, so we specify explicitly as "Ben BC".
Doing it in the metaclass at `People`-definition-time is slightly clearer and
more efficient than calculating and caching it the first time it's accessed.
@mikerkelly mikerkelly force-pushed the mikerkelly/workspace/standup/use-people-class-and-ping-them branch from 6ad2681 to 6b3aa19 Compare February 6, 2025 15:27
@mikerkelly
Copy link
Contributor Author

I've made commits to use a metaclass for the iterator and to populate human readable from the attribute name rather than needing to specify it manually (except for special cases like BEN_BC that can't easily be converted). Ready for another review.

@mikerkelly mikerkelly merged commit c637f6f into main Feb 7, 2025
5 checks passed
@mikerkelly mikerkelly deleted the mikerkelly/workspace/standup/use-people-class-and-ping-them branch February 7, 2025 08:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Refactor Team REX Standup Rota
2 participants