Skip to content

Commit baa026d

Browse files
authored
List users email (#446)
* Add management command to list users email. * Accept email values in command line, send email. * Workaround docker incompatibility issue with postgres:latest See docker-library/postgres#1100
1 parent efa5a14 commit baa026d

File tree

4 files changed

+125
-2
lines changed

4 files changed

+125
-2
lines changed

Diff for: .circleci/docker-compose.test-prod-db.yaml

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ version: '3'
22

33
services:
44
db:
5-
image: postgres:latest
5+
# Latest postgres docker image uses the new Debian bookworm and it is incompatible with some
6+
# existing docker implementations. CircleCI runs one of those older implementations.
7+
# Pinning the image to N-bullseye (prior Debian distribution) as a workaround.
8+
# https://github.com/docker-library/postgres/issues/1100
9+
# image: postgres:latest
10+
image: postgres:12-bullseye
611
expose:
712
- "5432"
813
environment:

Diff for: .circleci/docker-compose.yml

+6-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,12 @@ services:
3232
POSTGRES_PASSWORD: scionlab_rw_passw0rd
3333

3434
coord-db:
35-
image: postgres:latest
35+
# Latest postgres docker image uses the new Debian bookworm and it is incompatible with some
36+
# existing docker implementations. CircleCI runs one of those older implementations.
37+
# Pinning the image to N-bullseye (prior Debian distribution) as a workaround.
38+
# https://github.com/docker-library/postgres/issues/1100
39+
# image: postgres:latest
40+
image: postgres:12-bullseye
3641
networks:
3742
- coord_net
3843
expose:

Diff for: scionlab/management/commands/mass_email.py

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Copyright 2023 ETH Zurich
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import argparse
16+
import sys
17+
18+
from typing import List
19+
from django.conf import settings
20+
from django.core.management.base import BaseCommand, CommandParser
21+
from django.core.mail import EmailMessage
22+
23+
from scionlab.models.user import User
24+
25+
26+
class Command(BaseCommand):
27+
help = 'List or send a message to the unique emails of the users in alphabetical order'
28+
29+
def add_arguments(self, parser: CommandParser) -> None:
30+
parser.add_argument('-a', '--action', type=str, required=True,
31+
choices=['list', 'send'],
32+
help='Action of the command: list or send')
33+
parser.add_argument('--subject', type=str, required=False,
34+
help='Subject of the email to send')
35+
parser.add_argument('--body', type=argparse.FileType('r'), default='-',
36+
help='Input file to read the body of the email from')
37+
parser.add_argument('--skip_blocks', type=int, default=0,
38+
help='skip N initial blocks of recipients. Useful to continue ' +
39+
'sending after a crash')
40+
41+
def handle(self, *args, **kwargs):
42+
action = kwargs['action']
43+
if action == 'list':
44+
self.list()
45+
elif action == 'send':
46+
self.send(**kwargs)
47+
48+
def list(self):
49+
# print active users emails:
50+
for email in self._obtain_active_emails():
51+
print(email)
52+
53+
# report user stats:
54+
inactive = []
55+
active = []
56+
for u in User.objects.all():
57+
if not u.is_active:
58+
inactive.append(u)
59+
else:
60+
active.append(u)
61+
print(f'--------------------------- inactive: {len(inactive)}')
62+
print(f'--------------------------- active: {len(active)}')
63+
64+
def send(self, **kwargs):
65+
# retrieve the email parameters, or complain
66+
if 'subject' not in kwargs or kwargs['subject'] is None:
67+
exit('Need a subject')
68+
subject = kwargs['subject']
69+
if kwargs['body'] == sys.stdin:
70+
print('Type the body of the email, end with ^D')
71+
with kwargs['body'] as f:
72+
body = f.read()
73+
skip_blocks = kwargs['skip_blocks']
74+
75+
self._send(
76+
subject=subject,
77+
body=body,
78+
skip_blocks=skip_blocks,
79+
)
80+
81+
def _send(self, subject: str, body: str, skip_blocks: int):
82+
recipients = self._obtain_active_emails()
83+
block_count = (len(recipients) - 1) // settings.MAX_EMAIL_RECIPIENTS + 1
84+
for b in range(skip_blocks, block_count):
85+
# the recipients are a subset of the total
86+
dest = recipients[
87+
b*settings.MAX_EMAIL_RECIPIENTS:
88+
(b + 1) * settings.MAX_EMAIL_RECIPIENTS]
89+
# prepare the email and send it
90+
msg = EmailMessage(
91+
subject,
92+
body,
93+
settings.DEFAULT_FROM_EMAIL, # From
94+
[], # To
95+
bcc=dest,
96+
)
97+
msg.send()
98+
print(f'sent {b+1}/{block_count} -> {dest}')
99+
print('done')
100+
101+
def _obtain_active_emails(self) -> List[str]:
102+
emails = User.objects.filter(
103+
is_active=True,
104+
).values_list('email', flat=True).order_by('email').distinct()
105+
return emails
106+
107+
108+
def exit(msg: str):
109+
print(msg)
110+
sys.exit(1)

Diff for: scionlab/settings/common.py

+3
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ class VPNKeygenConf:
151151
# Location of the scion-pki command to generate TRCs.
152152
SCION_PKI_COMMAND = os.path.join(BASE_DIR, 'static_bin', 'scion-pki')
153153

154+
# Maximum number of recipients that the SMTP server we have can handle at once.
155+
MAX_EMAIL_RECIPIENTS = 100
156+
154157
# ##### DEBUG CONFIGURATION ###############################
155158
ALLOWED_HOSTS = []
156159
DEBUG = False

0 commit comments

Comments
 (0)