From c07646d94f19894b9ba3d9593c86222f977c4458 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Tue, 10 Jan 2023 12:34:41 +0100 Subject: [PATCH 1/5] 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 --- .../management/commands/contact_owners.py | 54 +++++++++++++------ 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/readthedocs/core/management/commands/contact_owners.py b/readthedocs/core/management/commands/contact_owners.py index c84d6fc4c32..9300ca8a1ab 100644 --- a/readthedocs/core/management/commands/contact_owners.py +++ b/readthedocs/core/management/commands/contact_owners.py @@ -1,8 +1,8 @@ -import structlog import sys from pathlib import Path from pprint import pprint +import structlog from django.conf import settings from django.contrib.auth import get_user_model from django.core.management.base import BaseCommand @@ -31,10 +31,16 @@ class Command(BaseCommand): Email and send an ephemeral (disappears after shown once) notification to all owners of the "readthedocs" organization:: - django-admin contact_owners --email email.md --notification notification.md --organization readthedocs # noqa + django-admin contact_owners --email email.md --notification notification.md --organization readthedocs + + Send a sticky notifications to multiple users:: - Where ``email.md`` is a markdown file with the first line as the subject, and the rest is the content. - ``user`` and ``domain`` are available in the context. + django-admin contact_owners --notification notification.md --sticky --usernames usernames.txt + + * ``usernames.txt`` is a text file containing one username per line. + * ``notifications.md`` is a Markdown file containing the message to be included in the notification. + * ``email.md`` is a Markdown file with the first line as the subject, and the rest is the content. + Note that ``user`` and ``domain`` are available in the context. .. code:: markdown @@ -50,7 +56,7 @@ class Command(BaseCommand): add the ``--production`` flag to actually send the email/notification. """ - help = 'Send an email or sticky notification from a file (markdown) to all owners.' + help = "Send an email or sticky notification from a file (Markdown) to users." def add_arguments(self, parser): parser.add_argument( @@ -92,16 +98,24 @@ def add_arguments(self, parser): '--project', help='Project slug to filter by.', ) + parser.add_argument( + "--usernames", + help="Path to a file with the one username per line to filter by.", + ) def handle(self, *args, **options): if not options['email'] and not options['notification']: print("--email or --notification is required.") sys.exit(1) - project = options['project'] - organization = options['organization'] - if project and organization: - print("--project and --organization can\'t be used together.") + project = options["project"] + organization = options["organization"] + usernames = options["usernames"] + if ( + len([item for item in [project, organization, usernames] if bool(item)]) + >= 2 + ): + print("--project, --organization and --usernames can't be used together.") sys.exit(1) if project: @@ -116,6 +130,15 @@ def handle(self, *args, **options): .filter(organizationowner__organization__disabled=False) .distinct() ) + elif usernames: + file = Path(usernames) + with file.open() as f: + usernames = f.readlines() + + # remove "\n" from lines + usernames = [line.strip() for line in usernames] + + users = User.objects.filter(username__in=usernames) else: users = ( User.objects @@ -124,16 +147,17 @@ def handle(self, *args, **options): ) print( - 'len(owners)={} production={} email={} notification={}'.format( + "len(owners)={} production={} email={} notification={} sticky={}".format( users.count(), - bool(options['production']), - options['email'], - options['notification'], + bool(options["production"]), + options["email"], + options["notification"], + options["sticky"], ) ) - if input('Continue? y/n: ') != 'y': - print('Aborting run.') + if input("Continue? y/N: ") != "y": + print("Aborting run.") return notification_content = '' From 88ec590333bff8d94df7c98fe1ac86e6bb9b4c84 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Thu, 19 Jan 2023 19:25:02 +0100 Subject: [PATCH 2/5] Lint --- .../management/commands/contact_owners.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/readthedocs/core/management/commands/contact_owners.py b/readthedocs/core/management/commands/contact_owners.py index 9300ca8a1ab..9cc584170c2 100644 --- a/readthedocs/core/management/commands/contact_owners.py +++ b/readthedocs/core/management/commands/contact_owners.py @@ -29,17 +29,26 @@ class Command(BaseCommand): django-admin contact_owners --email email.md - Email and send an ephemeral (disappears after shown once) notification to all owners of the "readthedocs" organization:: + Email and send an ephemeral (disappears after shown once) notification + to all owners of the "readthedocs" organization:: - django-admin contact_owners --email email.md --notification notification.md --organization readthedocs + django-admin contact_owners + --email email.md + --notification notification.md + --organization readthedocs Send a sticky notifications to multiple users:: - django-admin contact_owners --notification notification.md --sticky --usernames usernames.txt + django-admin contact_owners + --notification notification.md + --sticky + --usernames usernames.txt * ``usernames.txt`` is a text file containing one username per line. - * ``notifications.md`` is a Markdown file containing the message to be included in the notification. - * ``email.md`` is a Markdown file with the first line as the subject, and the rest is the content. + * ``notifications.md`` is a Markdown file containing the message + to be included in the notification. + * ``email.md`` is a Markdown file with the first line as the subject, + and the rest is the content. Note that ``user`` and ``domain`` are available in the context. .. code:: markdown From b50f93fc7ff3822f90d73b59c5fb696b085cd0ab Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 23 Jan 2023 11:09:31 +0100 Subject: [PATCH 3/5] Add note about how to expand use cases for this command --- readthedocs/core/management/commands/contact_owners.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/readthedocs/core/management/commands/contact_owners.py b/readthedocs/core/management/commands/contact_owners.py index 9cc584170c2..261f001d971 100644 --- a/readthedocs/core/management/commands/contact_owners.py +++ b/readthedocs/core/management/commands/contact_owners.py @@ -63,6 +63,13 @@ class Command(BaseCommand): By default the command won't send the email/notification (dry-run mode), add the ``--production`` flag to actually send the email/notification. + + .. note:: + + If you need to extend the behavior or add a new use case, + we recommend creating a simple script file that re-use the methods and functions from this command. + This is an example to contact Domain owners: + https://gist.github.com/humitos/3e08ed4763a9312f5c0a9a997ea95a42 """ help = "Send an email or sticky notification from a file (Markdown) to users." From 6ef289e43682bdc88459a249d1a3cf9987f44e7f Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 23 Jan 2023 16:23:43 +0100 Subject: [PATCH 4/5] Update readthedocs/core/management/commands/contact_owners.py Co-authored-by: Santos Gallegos --- readthedocs/core/management/commands/contact_owners.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readthedocs/core/management/commands/contact_owners.py b/readthedocs/core/management/commands/contact_owners.py index 261f001d971..1655d2168b2 100644 --- a/readthedocs/core/management/commands/contact_owners.py +++ b/readthedocs/core/management/commands/contact_owners.py @@ -116,7 +116,7 @@ def add_arguments(self, parser): ) parser.add_argument( "--usernames", - help="Path to a file with the one username per line to filter by.", + help="Path to a file with one username per line to filter by.", ) def handle(self, *args, **options): From 775cd254501250babd6c027984f7f4f8171902c6 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 23 Jan 2023 16:24:38 +0100 Subject: [PATCH 5/5] Lint --- readthedocs/core/management/commands/contact_owners.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/readthedocs/core/management/commands/contact_owners.py b/readthedocs/core/management/commands/contact_owners.py index 1655d2168b2..136d9ad7ac3 100644 --- a/readthedocs/core/management/commands/contact_owners.py +++ b/readthedocs/core/management/commands/contact_owners.py @@ -67,7 +67,8 @@ class Command(BaseCommand): .. note:: If you need to extend the behavior or add a new use case, - we recommend creating a simple script file that re-use the methods and functions from this command. + we recommend creating a simple script file that re-use the methods + and functions from this command. This is an example to contact Domain owners: https://gist.github.com/humitos/3e08ed4763a9312f5c0a9a997ea95a42 """