Repository: carpentries/amy

Issue:
Documentation update: sending mails
Sending mails was a documentation written for some specific feature of AMY that is no longer needed.

1. Remove that documentation
2. Probably remove the feature (management command) altogether 


Discussion:

Resolving patch:
diff --git a/amy/workshops/management/commands/instructors_activity.py b/amy/workshops/management/commands/instructors_activity.py
deleted file mode 100644
--- a/amy/workshops/management/commands/instructors_activity.py
+++ /dev/null
@@ -1,157 +0,0 @@
-import logging
-import os
-
-from django.core.mail import send_mail
-from django.core.management.base import BaseCommand
-from django.template.loader import get_template
-
-from workshops.models import Badge, Person, Role
-
-logger = logging.getLogger()
-
-
-class Command(BaseCommand):
-    help = "Report instructors activity."
-
-    def add_arguments(self, parser):
-        parser.add_argument(
-            "--send-out-for-real",
-            action="store_true",
-            default=False,
-            help="Send information to the instructors.",
-        )
-        parser.add_argument(
-            "--no-may-contact-only",
-            action="store_true",
-            default=False,
-            help="Include instructors not willing to be contacted.",
-        )
-        parser.add_argument(
-            "--django-mailing",
-            action="store_true",
-            default=False,
-            help="Use Django mailing system. This requires some environmental "
-            "variables to be set, see `settings.py`.",
-        )
-        parser.add_argument(
-            "-s",
-            "--sender",
-            action="store",
-            default="workshops@carpentries.org",
-            help='E-mail used in "from:" field.',
-        )
-
-    def foreign_tasks(self, tasks, person, roles):
-        """List of other instructors' tasks, per event."""
-        return [
-            task.event.task_set.filter(role__in=roles)
-            .exclude(person=person)
-            .select_related("person")
-            for task in tasks
-        ]
-
-    def fetch_activity(self, may_contact_only=True):
-        roles = Role.objects.filter(name__in=["instructor", "helper"])
-        instructor_badges = Badge.objects.instructor_badges()
-
-        instructors = Person.objects.filter(badges__in=instructor_badges)
-        instructors = instructors.exclude(email__isnull=True)
-        if may_contact_only:
-            instructors = instructors.exclude(may_contact=False)
-
-        # let's get some things faster
-        instructors = instructors.select_related("airport").prefetch_related(
-            "task_set", "lessons", "award_set", "badges"
-        )
-
-        # don't repeat the records
-        instructors = instructors.distinct()
-
-        result = []
-        for person in instructors:
-            tasks = person.task_set.filter(role__in=roles).select_related(
-                "event", "role"
-            )
-            record = {
-                "person": person,
-                "lessons": person.lessons.all(),
-                "instructor_awards": person.award_set.filter(
-                    badge__in=person.badges.instructor_badges()
-                ),
-                "tasks": zip(tasks, self.foreign_tasks(tasks, person, roles)),
-            }
-            result.append(record)
-
-        return result
-
-    def make_message(self, record):
-        tmplt = get_template("mailing/instructor_activity.txt")
-        return tmplt.render(context=record)
-
-    def subject(self, record):
-        # in future we can vary the subject depending on the record details
-        return "Updating your Software Carpentry information"
-
-    def recipient(self, record):
-        return record["person"].email
-
-    def send_message(
-        self, subject, message, sender, recipient, for_real=False, django_mailing=False
-    ):
-        if for_real:
-            if django_mailing:
-                send_mail(subject, message, sender, [recipient])
-
-            else:
-                command = 'mail -s "{subject}" -r {sender} {recipient}'.format(
-                    subject=subject,
-                    sender=sender,
-                    recipient=recipient,
-                )
-
-                writer = os.popen(command, "w")
-                writer.write(message)
-                writer.close()
-
-        if self.verbosity >= 2:
-            # write only a header
-            self.stdout.write("-" * 40 + "\n")
-            self.stdout.write("To: {}\n".format(recipient))
-            self.stdout.write("Subject: {}\n".format(subject))
-            self.stdout.write("From: {}\n".format(sender))
-        if self.verbosity >= 3:
-            # write whole message out
-            self.stdout.write(message + "\n")
-
-    def handle(self, *args, **options):
-        # default is dummy run - only actually send mail if told to
-        send_for_real = options["send_out_for_real"]
-
-        # by default include only instructors who have `may_contact==True`
-        no_may_contact_only = options["no_may_contact_only"]
-
-        # use mailing options from settings.py or the `mail` system command?
-        django_mailing = options["django_mailing"]
-
-        # verbosity option is added by Django
-        self.verbosity = int(options["verbosity"])
-
-        sender = options["sender"]
-
-        results = self.fetch_activity(not no_may_contact_only)
-
-        for result in results:
-            message = self.make_message(result)
-            subject = self.subject(result)
-            recipient = self.recipient(result)
-            self.send_message(
-                subject,
-                message,
-                sender,
-                recipient,
-                for_real=send_for_real,
-                django_mailing=django_mailing,
-            )
-
-        if self.verbosity >= 1:
-            self.stdout.write("Sent {} emails.\n".format(len(results)))


Test patch:
diff --git a/amy/workshops/tests/test_commands.py b/amy/workshops/tests/test_commands.py
--- a/amy/workshops/tests/test_commands.py
+++ b/amy/workshops/tests/test_commands.py
@@ -28,131 +28,16 @@
     datetime_match,
 )
 from workshops.management.commands.fake_database import Command as FakeDatabaseCommand
-from workshops.management.commands.instructors_activity import (
-    Command as InstructorsActivityCommand,
-)
 from workshops.management.commands.migrate_inactive_trainers_to_trainer_badges import (
     Command as MigrateInactiveTrainersToTrainerBadges,
 )
 from workshops.management.commands.migrate_to_single_instructor_badge import (
     Command as MigrateToSingleInstructorBadge,
 )
-from workshops.models import Award, Badge, Event, Organization, Person, Role, Task
+from workshops.models import Award, Badge, Event, Organization, Person
 from workshops.tests.base import TestBase
 
 
-class TestInstructorsActivityCommand(TestBase):
-    def setUp(self):
-        self.cmd = InstructorsActivityCommand()
-
-        # add instructors
-        self._setUpLessons()
-        self._setUpBadges()
-        self._setUpAirports()
-        self._setUpInstructors()
-
-        # and some non-instructors
-        self._setUpNonInstructors()
-
-        # add one event that some instructors took part in
-        self._setUpOrganizations()
-        self.event = Event.objects.create(
-            slug="event-with-tasks",
-            host=self.org_alpha,
-            start=date(2015, 8, 30),
-        )
-        self._setUpRoles()
-        self.instructor = Role.objects.get(name="instructor")
-        self.helper = Role.objects.get(name="helper")
-        self.learner = Role.objects.get(name="learner")
-
-        Task.objects.bulk_create(
-            [
-                Task(event=self.event, person=self.hermione, role=self.instructor),
-                Task(event=self.event, person=self.ron, role=self.instructor),
-                Task(event=self.event, person=self.ron, role=self.helper),
-                Task(event=self.event, person=self.harry, role=self.helper),
-                Task(event=self.event, person=self.spiderman, role=self.learner),
-                Task(event=self.event, person=self.blackwidow, role=self.learner),
-            ]
-        )
-
-    def test_getting_foreign_tasks(self):
-        """Make sure we get tasks for other people (per event)."""
-        person = self.hermione
-        roles = [self.instructor, self.helper]
-        tasks = person.task_set.filter(role__in=roles)
-
-        # index 0, because Hermione has only one task and we're checking it
-        fg_tasks = self.cmd.foreign_tasks(tasks, person, roles)[0]
-
-        # we should receive other instructors and helpers for self.event
-        expecting = set(
-            [
-                Task.objects.get(
-                    event=self.event, person=self.ron, role=self.instructor
-                ),
-                Task.objects.get(event=self.event, person=self.ron, role=self.helper),
-                Task.objects.get(event=self.event, person=self.harry, role=self.helper),
-            ]
-        )
-
-        self.assertEqual(expecting, set(fg_tasks))
-
-    def test_fetching_activity(self):
-        """Make sure we get correct results for all instructors."""
-        # include people who don't want to be contacted (other option is tested
-        # in `self.test_fetching_activity_may_contact_only`)
-        results = self.cmd.fetch_activity(may_contact_only=False)
-        instructor_badges = Badge.objects.instructor_badges()  # type: ignore
-
-        persons = [d["person"] for d in results]
-        lessons = [list(d["lessons"]) for d in results]
-        instructor_awards = [list(d["instructor_awards"]) for d in results]
-        tasks = [d["tasks"] for d in results]
-
-        expecting_persons = [self.hermione, self.harry, self.ron]
-        expecting_lessons = [
-            list(self.hermione.lessons.all()),
-            list(self.harry.lessons.all()),
-            list(self.ron.lessons.all()),
-        ]
-        expecting_awards = [
-            list(person.award_set.filter(badge__in=instructor_badges))
-            for person in expecting_persons
-        ]
-
-        self.assertEqual(set(persons), set(expecting_persons))
-        self.assertEqual(lessons, expecting_lessons)
-        self.assertEqual(instructor_awards, expecting_awards)
-
-        for task in tasks:
-            for own_task, foreign_tasks in task:
-                # we don't test foreign tasks, since they should be tested in
-                # `self.test_getting_foreign_tasks`
-                self.assertIn(
-                    own_task,
-                    own_task.person.task_set.filter(
-                        role__name__in=["instructor", "helper"]
-                    ),
-                )
-
-    def test_fetching_activity_may_contact_only(self):
-        """Make sure we get results only for people we can send emails to."""
-        # let's make Harry willing to receive emails
-        self.hermione.may_contact = False
-        self.harry.may_contact = True
-        self.ron.may_contact = False
-        self.hermione.save()
-        self.harry.save()
-        self.ron.save()
-
-        results = self.cmd.fetch_activity(may_contact_only=True)
-        persons = [d["person"] for d in results]
-        expecting_persons = [self.harry]
-        self.assertEqual(set(persons), set(expecting_persons))
-
-
 class TestWebsiteUpdatesCommand(TestBase):
     maxDiff = None