Skip to content

Commit 3c71088

Browse files
humitosstsewd
andauthored
Command contact_owners: add support to filter by usernames (#9882)
* Command `contact_owners`: add support to filter by usernames Add `--usernames` argument to be able to filter by usernames when sending contacting users. This can be used as: ``` django-admin contact_owners --notification notification.md --sticky --usernames usernames.txt ``` ``` humitos santos anthony ``` ``` Read the Docs is going to force email verification to be able to login accounts in the following weeks. We've noticed **your account doesn't have any email verified**. Please, go to [your email settings](/accounts/email/) and verify your email now. ``` In this case, it will send a sticky notification with that content to these 3 users. Required by #9865 * Lint * Add note about how to expand use cases for this command * Update readthedocs/core/management/commands/contact_owners.py Co-authored-by: Santos Gallegos <[email protected]> * Lint Co-authored-by: Santos Gallegos <[email protected]>
1 parent e02ed7b commit 3c71088

File tree

1 file changed

+57
-16
lines changed

1 file changed

+57
-16
lines changed

readthedocs/core/management/commands/contact_owners.py

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import structlog
21
import sys
32
from pathlib import Path
43
from pprint import pprint
54

5+
import structlog
66
from django.conf import settings
77
from django.contrib.auth import get_user_model
88
from django.core.management.base import BaseCommand
@@ -29,12 +29,27 @@ class Command(BaseCommand):
2929
3030
django-admin contact_owners --email email.md
3131
32-
Email and send an ephemeral (disappears after shown once) notification to all owners of the "readthedocs" organization::
32+
Email and send an ephemeral (disappears after shown once) notification
33+
to all owners of the "readthedocs" organization::
34+
35+
django-admin contact_owners
36+
--email email.md
37+
--notification notification.md
38+
--organization readthedocs
3339
34-
django-admin contact_owners --email email.md --notification notification.md --organization readthedocs # noqa
40+
Send a sticky notifications to multiple users::
3541
36-
Where ``email.md`` is a markdown file with the first line as the subject, and the rest is the content.
37-
``user`` and ``domain`` are available in the context.
42+
django-admin contact_owners
43+
--notification notification.md
44+
--sticky
45+
--usernames usernames.txt
46+
47+
* ``usernames.txt`` is a text file containing one username per line.
48+
* ``notifications.md`` is a Markdown file containing the message
49+
to be included in the notification.
50+
* ``email.md`` is a Markdown file with the first line as the subject,
51+
and the rest is the content.
52+
Note that ``user`` and ``domain`` are available in the context.
3853
3954
.. code:: markdown
4055
@@ -48,9 +63,17 @@ class Command(BaseCommand):
4863
4964
By default the command won't send the email/notification (dry-run mode),
5065
add the ``--production`` flag to actually send the email/notification.
66+
67+
.. note::
68+
69+
If you need to extend the behavior or add a new use case,
70+
we recommend creating a simple script file that re-use the methods
71+
and functions from this command.
72+
This is an example to contact Domain owners:
73+
https://gist.github.com/humitos/3e08ed4763a9312f5c0a9a997ea95a42
5174
"""
5275

53-
help = 'Send an email or sticky notification from a file (markdown) to all owners.'
76+
help = "Send an email or sticky notification from a file (Markdown) to users."
5477

5578
def add_arguments(self, parser):
5679
parser.add_argument(
@@ -92,16 +115,24 @@ def add_arguments(self, parser):
92115
'--project',
93116
help='Project slug to filter by.',
94117
)
118+
parser.add_argument(
119+
"--usernames",
120+
help="Path to a file with one username per line to filter by.",
121+
)
95122

96123
def handle(self, *args, **options):
97124
if not options['email'] and not options['notification']:
98125
print("--email or --notification is required.")
99126
sys.exit(1)
100127

101-
project = options['project']
102-
organization = options['organization']
103-
if project and organization:
104-
print("--project and --organization can\'t be used together.")
128+
project = options["project"]
129+
organization = options["organization"]
130+
usernames = options["usernames"]
131+
if (
132+
len([item for item in [project, organization, usernames] if bool(item)])
133+
>= 2
134+
):
135+
print("--project, --organization and --usernames can't be used together.")
105136
sys.exit(1)
106137

107138
if project:
@@ -116,6 +147,15 @@ def handle(self, *args, **options):
116147
.filter(organizationowner__organization__disabled=False)
117148
.distinct()
118149
)
150+
elif usernames:
151+
file = Path(usernames)
152+
with file.open() as f:
153+
usernames = f.readlines()
154+
155+
# remove "\n" from lines
156+
usernames = [line.strip() for line in usernames]
157+
158+
users = User.objects.filter(username__in=usernames)
119159
else:
120160
users = (
121161
User.objects
@@ -124,16 +164,17 @@ def handle(self, *args, **options):
124164
)
125165

126166
print(
127-
'len(owners)={} production={} email={} notification={}'.format(
167+
"len(owners)={} production={} email={} notification={} sticky={}".format(
128168
users.count(),
129-
bool(options['production']),
130-
options['email'],
131-
options['notification'],
169+
bool(options["production"]),
170+
options["email"],
171+
options["notification"],
172+
options["sticky"],
132173
)
133174
)
134175

135-
if input('Continue? y/n: ') != 'y':
136-
print('Aborting run.')
176+
if input("Continue? y/N: ") != "y":
177+
print("Aborting run.")
137178
return
138179

139180
notification_content = ''

0 commit comments

Comments
 (0)