Example #1
0
 def _send_invitation_email(self, section_id):
     SentEmail.create(
         section_id=section_id,
         recipient_uid=get_instructor_uids(section_id=section_id, term_id=self.term_id)[0],
         template_type='invitation',
         term_id=self.term_id,
     )
Example #2
0
def _create_emails_sent():
    term_id = app.config['CURRENT_TERM_ID']
    SentEmail.create(
        recipient_uids=['8765432'],
        section_id='28165',
        template_type='invitation',
        term_id=term_id,
    )
    std_commit(allow_test_environment=True)
    def test_do_not_email_filter(self, client, db, admin_session):
        """Do Not Email filter: Courses in eligible room; "opt out" is true; all stages of approval; not scheduled."""
        with test_approvals_workflow(app):
            # Send invites them opt_out.
            for section_id in (section_1_id, section_in_ineligible_room, section_3_id, section_4_id):
                CoursePreference.update_opt_out(section_id=section_id, term_id=self.term_id, opt_out=True)

                in_enabled_room = _is_course_in_enabled_room(section_id=section_id, term_id=self.term_id)
                if section_id == section_in_ineligible_room:
                    # Courses in ineligible rooms will be excluded from the feed.
                    assert not in_enabled_room
                else:
                    assert in_enabled_room
                    SentEmail.create(
                        section_id=section_id,
                        recipient_uids=_get_instructor_uids(section_id=section_id, term_id=self.term_id),
                        template_type='invitation',
                        term_id=self.term_id,
                    )

            # If course has approvals but not scheduled then it will show up in the feed.
            Approval.create(
                approved_by_uid=_get_instructor_uids(section_id=section_1_id, term_id=self.term_id)[0],
                approver_type_='instructor',
                cross_listed_section_ids=[],
                publish_type_='canvas',
                recording_type_='presentation_audio',
                room_id=Room.get_room_id(section_id=section_1_id, term_id=self.term_id),
                section_id=section_1_id,
                term_id=self.term_id,
            )
            # Feed will exclude scheduled.
            _schedule_recordings(
                section_id=section_3_id,
                term_id=self.term_id,
            )
            std_commit(allow_test_environment=True)

            api_json = self._api_courses(client, term_id=self.term_id, filter_='Do Not Email')
            for section_id in (section_1_id, section_4_id):
                # The 'Do Not Email' course is in the feed
                assert _find_course(api_json=api_json, section_id=section_id)

            for section_id in (section_3_id, section_in_ineligible_room):
                # Excluded courses
                assert not _find_course(api_json=api_json, section_id=section_id)
 def test_partially_approved_filter(self, client, db, admin_session):
     """Partially approved: Eligible, invited course with 1+ approvals, but not ALL instructors have approved."""
     with test_approvals_workflow(app):
         # Assert multiple instructors
         section_1_instructor_uids = _get_instructor_uids(section_id=section_1_id, term_id=self.term_id)
         section_6_instructor_uids = _get_instructor_uids(section_id=section_6_id, term_id=self.term_id)
         assert len(section_1_instructor_uids) > 1
         assert len(section_6_instructor_uids) > 1
         # Send invites
         courses = [
             {'section_id': section_1_id, 'instructor_uids': section_1_instructor_uids},
             {'section_id': section_6_id, 'instructor_uids': section_6_instructor_uids},
         ]
         for course in courses:
             SentEmail.create(
                 section_id=course['section_id'],
                 recipient_uids=course['instructor_uids'],
                 template_type='invitation',
                 term_id=self.term_id,
             )
             Approval.create(
                 approved_by_uid=course['instructor_uids'][0],
                 approver_type_='instructor',
                 cross_listed_section_ids=[],
                 publish_type_='canvas',
                 recording_type_='presentation_audio',
                 room_id=Room.get_room_id(section_id=course['section_id'], term_id=self.term_id),
                 section_id=course['section_id'],
                 term_id=self.term_id,
             )
         # Feed will include both scheduled and not scheduled.
         _schedule_recordings(
             section_id=section_1_id,
             term_id=self.term_id,
         )
         std_commit(allow_test_environment=True)
         api_json = self._api_courses(client, term_id=self.term_id, filter_='Partially Approved')
         assert len(api_json) == 2
         assert _find_course(api_json=api_json, section_id=section_1_id)
         course = _find_course(api_json=api_json, section_id=section_6_id)
         assert course
         assert course['label'] == 'LAW 23, LEC 002'
 def test_scheduled_filter(self, client, db, admin_session):
     """Scheduled filter: Courses with recordings scheduled."""
     with test_approvals_workflow(app):
         section_1_instructor_uids = _get_instructor_uids(section_id=section_1_id, term_id=self.term_id)
         section_6_instructor_uids = _get_instructor_uids(section_id=section_6_id, term_id=self.term_id)
         # Send invites
         courses = [
             {'section_id': section_1_id, 'instructor_uids': section_1_instructor_uids},
             {'section_id': section_6_id, 'instructor_uids': section_6_instructor_uids},
         ]
         for course in courses:
             SentEmail.create(
                 section_id=course['section_id'],
                 recipient_uids=course['instructor_uids'],
                 template_type='invitation',
                 term_id=self.term_id,
             )
             Approval.create(
                 approved_by_uid=course['instructor_uids'][0],
                 approver_type_='instructor',
                 cross_listed_section_ids=[],
                 publish_type_='canvas',
                 recording_type_='presentation_audio',
                 room_id=Room.get_room_id(section_id=course['section_id'], term_id=self.term_id),
                 section_id=course['section_id'],
                 term_id=self.term_id,
             )
         # Feed will only include courses that were scheduled.
         _schedule_recordings(
             section_id=section_1_id,
             term_id=self.term_id,
         )
         std_commit(allow_test_environment=True)
         api_json = self._api_courses(client, term_id=self.term_id, filter_='Scheduled')
         assert len(api_json) == 1
         assert _find_course(api_json=api_json, section_id=section_1_id)
         assert not _find_course(api_json=api_json, section_id=section_6_id)
 def test_not_invited_filter(self, client, db, admin_session):
     """Not-invited filter: Courses in eligible rooms, never sent an invitation. No approval. Not scheduled."""
     with test_approvals_workflow(app):
         # The first course gets an invitation
         section_1_instructor_uids = _get_instructor_uids(section_id=section_1_id, term_id=self.term_id)
         SentEmail.create(
             section_id=section_1_id,
             recipient_uids=section_1_instructor_uids,
             template_type='invitation',
             term_id=self.term_id,
         )
         # The second course did not receive an invitation BUT it does have approval.
         invite = SentEmail.get_emails_of_type(
             section_id=section_4_id,
             template_type='invitation',
             term_id=self.term_id,
         )
         assert not invite
         Approval.create(
             approved_by_uid=_get_instructor_uids(section_id=section_4_id, term_id=self.term_id)[0],
             approver_type_='instructor',
             cross_listed_section_ids=[],
             publish_type_='canvas',
             recording_type_='presentation_audio',
             room_id=Room.get_room_id(section_id=section_4_id, term_id=self.term_id),
             section_id=section_4_id,
             term_id=self.term_id,
         )
         std_commit(allow_test_environment=True)
         api_json = self._api_courses(client, term_id=self.term_id, filter_='Not Invited')
         assert not _find_course(api_json=api_json, section_id=section_1_id)
         assert not _find_course(api_json=api_json, section_id=section_4_id)
         # Third course is in enabled room and has not received an invite. Therefore, it is in the feed.
         assert _is_course_in_enabled_room(section_id=section_3_id, term_id=self.term_id)
         course = _find_course(api_json=api_json, section_id=section_3_id)
         assert course
         assert course['label'] == 'BIO 1B, LEC 001'
 def test_invited_filter(self, client, db, admin_session):
     """Invited filter: Course in an eligible room, have received invitation. No approvals. Not scheduled."""
     with test_approvals_workflow(app):
         # First, send invitations
         SentEmail.create(
             section_id=section_4_id,
             recipient_uids=_get_instructor_uids(section_id=section_4_id, term_id=self.term_id),
             template_type='invitation',
             term_id=self.term_id,
         )
         section_5_instructor_uids = _get_instructor_uids(section_id=section_5_id, term_id=self.term_id)
         SentEmail.create(
             section_id=section_5_id,
             recipient_uids=section_5_instructor_uids,
             template_type='invitation',
             term_id=self.term_id,
         )
         # The section with approval will NOT show up in search results
         Approval.create(
             approved_by_uid=section_5_instructor_uids[0],
             approver_type_='instructor',
             cross_listed_section_ids=[],
             publish_type_='canvas',
             recording_type_='presentation_audio',
             room_id=SisSection.get_course(term_id=self.term_id, section_id=section_5_id)['room']['id'],
             section_id=section_5_id,
             term_id=self.term_id,
         )
         std_commit(allow_test_environment=True)
         api_json = self._api_courses(client, term_id=self.term_id, filter_='Invited')
         # Section with ZERO approvals will show up in search results
         course = _find_course(api_json=api_json, section_id=section_4_id)
         assert course
         assert course['label'] == 'CHEM C110L, LAB 001'
         # The section with approval will NOT show up in search results
         assert not _find_course(api_json=api_json, section_id=section_5_id)
Example #8
0
    def send(
        self,
        message,
        recipient,
        subject_line,
        term_id=None,
        section_id=None,
        template_type=None,
    ):
        if not message or not subject_line or not recipient:
            app.logger.error(
                'Attempted to send a message without required fields: '
                f'(recipient={recipient}, subject_line={subject_line}, message={message}'
            )
            return False

        eb_env = get_eb_environment()
        prefix = '' if 'prod' in (eb_env
                                  or '') else f"[{eb_env or 'diablo-local'}] "
        subject_line = f'{prefix}{scrub_email_content(subject_line)}'
        message = scrub_email_content(message)

        @skip_when_pytest()
        def _send():
            smtp = SMTP(self.bcop_smtp_server, port=self.bcop_smtp_port)
            # TLS encryption
            smtp.starttls()
            smtp.set_debuglevel(app.logger.level == logging.DEBUG)
            smtp.login(self.bcop_smtp_username, self.bcop_smtp_password)

            emails_sent_to = set()

            if app.config['DIABLO_ENV'] == 'test':
                write_email_to_log(message=message,
                                   recipient=recipient,
                                   subject_line=subject_line)
            else:
                from_address = f"{app.config['EMAIL_COURSE_CAPTURE_SUPPORT_LABEL']} <{app.config['EMAIL_COURSE_CAPTURE_SUPPORT']}>"

                for email_address in self.get_email_addresses(user=recipient):
                    msg = MIMEMultipart('alternative')
                    msg['From'] = from_address
                    msg['To'] = email_address

                    if app.config['EMAIL_TEST_MODE']:
                        # Append intended recipient email address to verify when testing.
                        intended_email = recipient['email']
                        msg['Subject'] = f'{subject_line} (To: {intended_email})'
                    else:
                        msg['Subject'] = subject_line

                    # TODO: 'plain' text version of email?
                    msg.attach(MIMEText(message, 'plain'))
                    msg.attach(MIMEText(message, 'html'))
                    # Send
                    smtp.sendmail(from_addr=from_address,
                                  to_addrs=email_address,
                                  msg=msg.as_string())

                    emails_sent_to.add(email_address)

            app.logger.info(
                f'\'{template_type}\' email sent to {", ".join(list(emails_sent_to))}'
            )
            # Disconnect
            smtp.quit()

        # Send emails
        _send()

        SentEmail.create(
            recipient_uid=recipient['uid'],
            section_id=section_id,
            template_type=template_type,
            term_id=term_id or app.config['CURRENT_TERM_ID'],
        )
        return True
Example #9
0
    def send(
        self,
        message,
        recipients,
        subject_line,
        term_id=None,
        section_id=None,
        template_type=None,
    ):
        @skip_when_pytest()
        def _send():
            # Connect to SMTP server
            smtp = SMTP(self.bcop_smtp_server, port=self.bcop_smtp_port)
            # TLS encryption
            smtp.starttls()
            smtp.set_debuglevel(app.logger.level == logging.DEBUG)
            smtp.login(self.bcop_smtp_username, self.bcop_smtp_password)

            emails_sent_to = set()

            for recipient in recipients:
                mock_message = _get_mock_message(
                    recipient['name'],
                    recipient['email'],
                    subject_line,
                    message,
                )
                if app.config['DIABLO_ENV'] == 'test':
                    app.logger.info(mock_message)
                else:
                    from_address = app.config['EMAIL_DIABLO_SUPPORT']
                    to_address = self.get_email_address(
                        user=recipient, subject_line=subject_line)

                    msg = MIMEMultipart('alternative')
                    msg['From'] = from_address
                    msg['To'] = to_address
                    msg['Bcc'] = app.config['EMAIL_DIABLO_ADMIN']

                    if app.config['EMAIL_TEST_MODE']:
                        # Append intended recipient email address to verify when testing.
                        intended_email = recipient['email']
                        msg['Subject'] = f'{subject_line} (To: {intended_email})'
                    else:
                        msg['Subject'] = subject_line

                    # TODO: 'plain' text version of email?
                    msg.attach(MIMEText(message, 'plain'))
                    msg.attach(MIMEText(message, 'html'))
                    # Send
                    smtp.sendmail(from_addr=from_address,
                                  to_addrs=to_address,
                                  msg=msg.as_string())

                    emails_sent_to.add(to_address)

            app.logger.info(
                f'{len(recipients)} \'{template_type}\' emails sent to {", ".join(list(emails_sent_to))}'
            )
            # Disconnect
            smtp.quit()

        # Send emails
        _send()

        SentEmail.create(
            recipient_uids=[recipient['uid'] for recipient in recipients],
            section_id=section_id,
            template_type=template_type,
            term_id=term_id or app.config['CURRENT_TERM_ID'],
        )