feat(feedback): add command to notify helpers who opted-in last year
This commit is contained in:
parent
b685ad800f
commit
10dbb06957
|
@ -161,3 +161,6 @@ cython_debug/
|
|||
|
||||
# E.g. copies of old dev database
|
||||
*.bkp
|
||||
|
||||
# Database dumps
|
||||
/*.json
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.template import Context, Template
|
||||
from phonenumber_field.phonenumber import PhoneNumber
|
||||
|
||||
from shiftregister.app.models import Helper
|
||||
from shiftregister.messaging import Message, MessageType
|
||||
from shiftregister.messaging.outbound import send
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = (
|
||||
"Notify a list of phone numbers excluding those already found in the database."
|
||||
)
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
help="print messages that would be sent, but do not actually send them",
|
||||
)
|
||||
parser.add_argument(
|
||||
"numbers", help="path to numbers list as JSON", type=pathlib.Path
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
with options["numbers"].open() as f:
|
||||
numbers = json.load(f)
|
||||
|
||||
if not isinstance(numbers, list) or not all(
|
||||
map(lambda o: isinstance(o, dict) and "phone" in o, numbers)
|
||||
):
|
||||
raise CommandError(
|
||||
"JSON document must be an array of objects containing (at least) the key 'phone'"
|
||||
)
|
||||
|
||||
template = Template(sys.stdin.read().strip())
|
||||
|
||||
numbers_count = len(numbers)
|
||||
existing_numbers = set(Helper.objects.values_list("phone", flat=True))
|
||||
numbers = list(
|
||||
filter(
|
||||
lambda o: PhoneNumber.from_string(o["phone"]) not in existing_numbers,
|
||||
numbers,
|
||||
)
|
||||
)
|
||||
|
||||
self.stderr.write(
|
||||
self.style.WARNING(
|
||||
f"{numbers_count-len(numbers)} already found in database"
|
||||
)
|
||||
)
|
||||
|
||||
messages = (
|
||||
Message(
|
||||
i,
|
||||
recipient=o["phone"],
|
||||
text=template.render(Context(o)),
|
||||
type=MessageType.OUTBOUND,
|
||||
)
|
||||
for i, o in enumerate(numbers)
|
||||
)
|
||||
|
||||
if options["dry_run"]:
|
||||
messages = list(messages)
|
||||
|
||||
self.stderr.write(
|
||||
self.style.WARNING(f"would send {len(messages)} message(s)")
|
||||
)
|
||||
self.stderr.write()
|
||||
|
||||
for message in messages:
|
||||
if int(message.key) != 0:
|
||||
self.stderr.write()
|
||||
self.stderr.write(self.style.WARNING("---"))
|
||||
self.stderr.write()
|
||||
|
||||
self.stderr.write(self.style.WARNING(f"to: {message.recipient}"))
|
||||
self.stderr.write(self.style.WARNING(f"length: {len(message.text)}"))
|
||||
self.stderr.write()
|
||||
self.stderr.write(self.style.WARNING(message.text))
|
||||
|
||||
if len(messages) > 0:
|
||||
self.stderr.write()
|
||||
|
||||
return
|
||||
|
||||
all_messages = set(messages)
|
||||
sent_messages = set()
|
||||
|
||||
self.stderr.write(
|
||||
self.style.SUCCESS(
|
||||
f"{'0'.rjust(len(str(len(all_messages))))} / {len(all_messages)}"
|
||||
),
|
||||
ending="",
|
||||
)
|
||||
|
||||
error = None
|
||||
try:
|
||||
for message in send(all_messages):
|
||||
sent_messages.add(message)
|
||||
|
||||
self.stderr.write(
|
||||
self.style.SUCCESS(
|
||||
f"\r{str(len(sent_messages)).rjust(len(str(len(all_messages))))} / {len(all_messages)}"
|
||||
),
|
||||
ending="",
|
||||
)
|
||||
except Exception as e:
|
||||
error = e
|
||||
|
||||
self.stderr.write()
|
||||
self.stderr.write(
|
||||
self.style.WARNING(f"sent {len(sent_messages)} out of {len(all_messages)}")
|
||||
)
|
||||
self.stderr.write(
|
||||
self.style.WARNING(f"numbers to which messages could not be sent:")
|
||||
)
|
||||
self.stdout.write(
|
||||
self.style.ERROR(
|
||||
json.dumps(
|
||||
[
|
||||
{"phone": message.recipient}
|
||||
for message in all_messages - sent_messages
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
if error:
|
||||
raise CommandError(error)
|
|
@ -45,11 +45,10 @@ class Message:
|
|||
self.created_at = created_at
|
||||
|
||||
def __eq__(self, other):
|
||||
if other is None:
|
||||
return False
|
||||
elif isinstance(other, Message):
|
||||
other = other.key
|
||||
else:
|
||||
other = str(other)
|
||||
if isinstance(other, Message):
|
||||
return self.key == other.key
|
||||
|
||||
return self.key == other
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.key)
|
||||
|
|
Loading…
Reference in New Issue