def test_invite_new_instructors(self, app, db_session):
        """Invite all assigned instructors who haven't yet received an invitation."""
        term_id = app.config['CURRENT_TERM_ID']
        with test_approvals_workflow(app):
            # The job creates many new invitations.
            timestamp = utc_now()
            # Emails are queued but not sent.
            InvitationJob(simply_yield).run()
            assert len(_get_invitations_since(term_id, timestamp)) == 0
            # Emails are sent. We have more emails than courses since some courses have multiple instructors.
            QueuedEmailsJob(simply_yield).run()
            invitations = _get_invitations_since(term_id, timestamp)
            assert len(invitations) == 14

            # Each eligible course has an invitation.
            eligible_courses = [
                c for c in SisSection.get_courses(term_id=term_id)
                if len(c['meetings']['eligible']) == 1
            ]
            assert len(eligible_courses) == 11
            for course in eligible_courses:
                for i in course['instructors']:
                    sent_email = next((e for e in invitations
                                       if e.section_id == course['sectionId']
                                       and i['uid'] == e.recipient_uid), None)
                    assert sent_email
                    email_json = sent_email.to_api_json()
                    assert email_json['recipientUid'] == i['uid']
                    assert email_json['sectionId'] == course['sectionId']
                    assert email_json['templateType'] == 'invitation'
                    assert email_json['termId'] == term_id
                    assert email_json['sentAt']

            # Add an instructor.
            section = SisSection.query.filter_by(term_id=term_id,
                                                 section_id=50002).first()
            db_session.expunge(section)
            make_transient(section)
            section.id = None
            section.instructor_uid = '10008'
            db_session.add(section)
            std_commit(allow_test_environment=True)

            # Re-run the job. An email is sent to the new instructor only.
            timestamp = utc_now()
            InvitationJob(simply_yield).run()
            QueuedEmailsJob(simply_yield).run()
            invitations = _get_invitations_since(term_id, timestamp)
            assert len(invitations) == 1
            invitation = invitations[0].to_api_json()
            assert invitation['sectionId'] == 50002
            assert invitation['recipientUid'] == '10008'
Beispiel #2
0
 def upsert(cls, rows):
     now = utc_now().strftime('%Y-%m-%dT%H:%M:%S+00')
     count_per_chunk = 10000
     for chunk in range(0, len(rows), count_per_chunk):
         rows_subset = rows[chunk:chunk + count_per_chunk]
         query = """
             INSERT INTO instructors (
                 created_at, dept_code, email, first_name, last_name, uid, updated_at
             )
             SELECT
                 created_at, dept_code, email, first_name, last_name, uid, updated_at
             FROM json_populate_recordset(null::instructors, :json_dumps)
             ON CONFLICT(uid) DO
             UPDATE SET
                 dept_code = EXCLUDED.dept_code,
                 email = EXCLUDED.email,
                 first_name = EXCLUDED.first_name,
                 last_name = EXCLUDED.last_name;
         """
         data = [{
             'created_at': now,
             'dept_code': row['dept_code'],
             'email': row['email'],
             'first_name': row['first_name'],
             'last_name': row['last_name'],
             'uid': row['uid'],
             'updated_at': now,
         } for row in rows_subset]
         db.session.execute(query, {'json_dumps': json.dumps(data)})
    def test_queued_email_for_admin(self):
        """Certain email template types are for admin recipients only."""
        def _emails_sent():
            return _get_emails_sent(email_template_type=email_template_type,
                                    section_id=section_id,
                                    term_id=term_id)

        term_id = app.config['CURRENT_TERM_ID']
        section_id = 22287
        email_template_type = 'admin_alert_room_change'

        QueuedEmail.create(section_id, email_template_type, term_id)
        std_commit(allow_test_environment=True)

        before = utc_now()
        emails_sent_before = _emails_sent()
        # Run the job
        QueuedEmailsJob(app.app_context).run()
        std_commit(allow_test_environment=True)

        # Expect email to admin email address
        emails_sent_after = _emails_sent()
        assert len(emails_sent_after) == len(emails_sent_before) + 1

        sent_email = next(
            (e for e in emails_sent_after
             if e.section_id == section_id and e.sent_at > before), None)
        assert sent_email
        json_ = sent_email.to_api_json()
        assert json_['recipientUids'] == [app.config['EMAIL_DIABLO_ADMIN_UID']]
        assert json_['sectionId'] == section_id
        assert json_['templateType'] == email_template_type
        assert json_['termId'] == term_id
        assert json_['sentAt']
    def test_course_has_opted_out(self):
        """Do not send email to courses that have opted out."""
        def _emails_sent():
            return _get_emails_sent(email_template_type=email_template_type,
                                    section_id=section_id,
                                    term_id=term_id)

        term_id = app.config['CURRENT_TERM_ID']
        section_id = 28602
        CoursePreference.update_opt_out(term_id=term_id,
                                        section_id=section_id,
                                        opt_out=True)
        email_template_type = 'invitation'

        QueuedEmail.create(section_id, email_template_type, term_id)
        std_commit(allow_test_environment=True)

        before = utc_now()
        emails_sent_before = _emails_sent()
        # Run the job
        QueuedEmailsJob(app.app_context).run()
        std_commit(allow_test_environment=True)

        # Expect no emails sent
        emails_sent_after = _emails_sent()
        assert len(emails_sent_after) == len(emails_sent_before)
        assert not next(
            (e for e in emails_sent_after
             if e.section_id == section_id and e.sent_at > before), None)
Beispiel #5
0
    def test_course_has_opted_out(self):
        """Do not send email to courses that have opted out."""
        def _emails_sent():
            return _get_emails_sent(email_template_type=email_template_type,
                                    section_id=section_id,
                                    term_id=term_id)

        term_id = app.config['CURRENT_TERM_ID']
        section_id = 50000
        CoursePreference.update_opt_out(term_id=term_id,
                                        section_id=section_id,
                                        opt_out=True)
        email_template_type = 'invitation'
        recipient = {
            'name': 'William Peter Blatty',
            'uid': '10001',
        }
        QueuedEmail.create(section_id,
                           email_template_type,
                           term_id,
                           recipient=recipient)
        std_commit(allow_test_environment=True)

        before = utc_now()
        emails_sent_before = _emails_sent()
        # Run the job
        QueuedEmailsJob(simply_yield).run()
        std_commit(allow_test_environment=True)

        # Expect no emails sent
        emails_sent_after = _emails_sent()
        assert len(emails_sent_after) == len(emails_sent_before)
        assert not next(
            (e for e in emails_sent_after
             if e.section_id == section_id and e.sent_at > before), None)
    def test_send_invitation_emails(self):
        """Send all email in 'queued_emails' table."""
        def _emails_sent(section_id):
            return _get_emails_sent(email_template_type=email_template_type,
                                    section_id=section_id,
                                    term_id=term_id)

        term_id = app.config['CURRENT_TERM_ID']
        courses = [
            {
                'sectionId': 28602,
                'instructorUids': ['234567', '8765432'],
            },
            {
                'sectionId': 28165,
                'instructorUids': ['8765432'],
            },
        ]
        email_template_type = 'invitation'

        for course in courses:
            QueuedEmail.create(course['sectionId'], email_template_type,
                               term_id)
            std_commit(allow_test_environment=True)

        def _get_emails_to_courses():
            emails_sent_ = []
            for course_ in courses:
                emails_sent_.extend(_emails_sent(course_['sectionId']))
            return emails_sent_

        before = utc_now()
        emails_sent_before = _get_emails_to_courses()
        # Run the job
        QueuedEmailsJob(app.app_context).run()
        std_commit(allow_test_environment=True)

        # Expect one email per instructor
        emails_sent_after = _get_emails_to_courses()
        assert len(emails_sent_after) == len(emails_sent_before) + 2

        def _find_email(section_id):
            return next((e for e in emails_sent_after
                         if e.section_id == section_id and e.sent_at > before),
                        None)

        for course in courses:
            sent_email = _find_email(section_id=course['sectionId'])
            assert sent_email
            json_ = sent_email.to_api_json()
            assert set(json_['recipientUids']) == set(course['instructorUids'])
            assert json_['sectionId'] == course['sectionId']
            assert json_['templateType'] == email_template_type
            assert json_['termId'] == term_id
            assert json_['sentAt']
Beispiel #7
0
    def test_send_invitation_emails(self):
        """Send all email in 'queued_emails' table."""
        term_id = app.config['CURRENT_TERM_ID']
        courses = SisSection.get_courses(section_ids=[50000, 50001],
                                         term_id=term_id)
        email_template_type = 'invitation'

        for course in courses:
            for instructor in course['instructors']:
                QueuedEmail.create(course['sectionId'],
                                   email_template_type,
                                   term_id,
                                   recipient=instructor)
            std_commit(allow_test_environment=True)

        def _get_emails_to_courses():
            emails_sent = []
            for c in courses:
                emails_sent.extend(
                    _get_emails_sent(
                        email_template_type=email_template_type,
                        section_id=c['sectionId'],
                        term_id=term_id,
                    ), )
            return emails_sent

        before = utc_now()
        emails_sent_before = _get_emails_to_courses()
        # Run the job
        QueuedEmailsJob(simply_yield).run()
        std_commit(allow_test_environment=True)

        # Expect one email per instructor
        emails_sent_after = _get_emails_to_courses()
        assert len(emails_sent_after) == len(emails_sent_before) + 3

        def _find_email(section_id, uid):
            return next(
                (e for e in emails_sent_after if e.section_id == section_id
                 and e.sent_at > before and uid == e.recipient_uid), None)

        for course in courses:
            for instructor in course['instructors']:
                sent_email = _find_email(section_id=course['sectionId'],
                                         uid=instructor['uid'])
                assert sent_email
                email_json = sent_email.to_api_json()
                assert email_json['recipientUid'] == instructor['uid']
                assert email_json['sectionId'] == course['sectionId']
                assert email_json['templateType'] == email_template_type
                assert email_json['termId'] == term_id
                assert email_json['sentAt']
 def _run(self):
     kaltura = Kaltura()
     for blackout in Blackout.all_blackouts():
         if blackout.end_date < utc_now():
             app.logger.info(f'Removing past blackout: {blackout}')
             Blackout.delete_blackout(blackout.id)
         else:
             events = kaltura.get_events_in_date_range(end_date=blackout.end_date, start_date=blackout.start_date)
             for event in events:
                 created_by_diablo = CREATED_BY_DIABLO_TAG in event['tags']
                 if created_by_diablo and not represents_recording_series(event):
                     kaltura.delete(event['id'])
                     app.logger.info(f"'Event {event['summary']} deleted per {blackout}.")
    def test_course_opted_out(self, app):
        """Do not send email to courses that have opted out."""
        term_id = app.config['CURRENT_TERM_ID']
        with test_approvals_workflow(app):
            section_id = 50006
            CoursePreference.update_opt_out(term_id=term_id,
                                            section_id=section_id,
                                            opt_out=True)
            std_commit(allow_test_environment=True)

            timestamp = utc_now()
            # Emails are queued but not sent.
            InvitationJob(simply_yield).run()
            assert len(_get_invitations_since(term_id, timestamp)) == 0
            # Emails are sent.
            QueuedEmailsJob(simply_yield).run()
            invitations = _get_invitations_since(term_id, timestamp)
            assert len(invitations) == 15
            assert not next(
                (e for e in invitations if e.section_id == section_id), None)
Beispiel #10
0
    def test_queued_email_for_admin(self):
        """Certain email template types are for admin recipients only."""
        def _emails_sent():
            return _get_emails_sent(email_template_type=email_template_type,
                                    section_id=section_id,
                                    term_id=term_id)

        term_id = app.config['CURRENT_TERM_ID']
        section_id = 50005
        email_template_type = 'admin_alert_room_change'
        recipient = {
            'name': 'Course Capture Admin',
            'uid': app.config['EMAIL_DIABLO_ADMIN_UID'],
        }
        QueuedEmail.create(section_id,
                           email_template_type,
                           term_id,
                           recipient=recipient)
        std_commit(allow_test_environment=True)

        before = utc_now()
        emails_sent_before = _emails_sent()
        # Run the job
        QueuedEmailsJob(simply_yield).run()
        std_commit(allow_test_environment=True)

        # Expect email to admin email address
        emails_sent_after = _emails_sent()
        assert len(emails_sent_after) == len(emails_sent_before) + 1

        sent_email = next(
            (e for e in emails_sent_after
             if e.section_id == section_id and e.sent_at > before), None)
        assert sent_email
        email_json = sent_email.to_api_json()
        assert email_json['recipientUid'] == app.config[
            'EMAIL_DIABLO_ADMIN_UID']
        assert email_json['sectionId'] == section_id
        assert email_json['templateType'] == email_template_type
        assert email_json['termId'] == term_id
        assert email_json['sentAt']
Beispiel #11
0
 def refresh(cls, sis_sections, term_id):
     db.session.execute(
         cls.__table__.delete().where(cls.term_id == term_id))
     now = utc_now().strftime('%Y-%m-%dT%H:%M:%S+00')
     count_per_chunk = 10000
     for chunk in range(0, len(sis_sections), count_per_chunk):
         rows_subset = sis_sections[chunk:chunk + count_per_chunk]
         query = """
             INSERT INTO sis_sections (
                 allowed_units, course_name, course_title, created_at, instruction_format, instructor_name,
                 instructor_role_code, instructor_uid, is_primary, meeting_days, meeting_end_date, meeting_end_time,
                 meeting_location, meeting_start_date, meeting_start_time, section_id, section_num, term_id
             )
             SELECT
                 allowed_units, course_name, course_title, created_at, instruction_format, instructor_name,
                 instructor_role_code, instructor_uid, is_primary, meeting_days, meeting_end_date, meeting_end_time,
                 meeting_location, meeting_start_date, meeting_start_time, section_id, section_num, term_id
             FROM json_populate_recordset(null::sis_sections, :json_dumps)
         """
         data = [{
             'allowed_units': row['allowed_units'],
             'course_name': row['course_name'],
             'course_title': row['course_title'],
             'created_at': now,
             'instruction_format': row['instruction_format'],
             'instructor_name': row['instructor_name'],
             'instructor_role_code': row['instructor_role_code'],
             'instructor_uid': row['instructor_uid'],
             'is_primary': row['is_primary'],
             'meeting_days': row['meeting_days'],
             'meeting_end_date': row['meeting_end_date'],
             'meeting_end_time': row['meeting_end_time'],
             'meeting_location': row['meeting_location'],
             'meeting_start_date': row['meeting_start_date'],
             'meeting_start_time': row['meeting_start_time'],
             'section_id': int(row['section_id']),
             'section_num': row['section_num'],
             'term_id': int(row['term_id']),
         } for row in rows_subset]
         db.session.execute(query, {'json_dumps': json.dumps(data)})
def _save_courses(sis_sections):
    now = utc_now().strftime('%Y-%m-%dT%H:%M:%S+00')
    query = """
        INSERT INTO sis_sections (
            allowed_units, course_name, course_title, created_at, deleted_at, instruction_format, instructor_name,
            instructor_role_code, instructor_uid, is_primary, meeting_days, meeting_end_date, meeting_end_time,
            meeting_location, meeting_start_date, meeting_start_time, section_id, section_num, term_id
        )
        SELECT
            allowed_units, course_name, course_title, created_at, deleted_at, instruction_format, instructor_name,
            instructor_role_code, instructor_uid, is_primary::BOOLEAN, meeting_days, meeting_end_date::TIMESTAMP,
            meeting_end_time, meeting_location, meeting_start_date::TIMESTAMP, meeting_start_time, section_id::INTEGER,
            section_num, term_id::INTEGER
        FROM json_populate_recordset(null::sis_sections, :json_dumps)
    """
    data = [{
        'allowed_units': row['allowed_units'],
        'course_name': row['course_name'],
        'course_title': row['course_title'],
        'created_at': now,
        'deleted_at': now if row.get('is_deleted') else None,
        'instruction_format': row['instruction_format'],
        'instructor_name': row['instructor_name'],
        'instructor_role_code': row['instructor_role_code'],
        'instructor_uid': row['instructor_uid'],
        'is_primary': row['is_primary'],
        'meeting_days': row['meeting_days'],
        'meeting_end_date': row['meeting_end_date'],
        'meeting_end_time': row['meeting_end_time'],
        'meeting_location': row['meeting_location'],
        'meeting_start_date': row['meeting_start_date'],
        'meeting_start_time': row['meeting_start_time'],
        'section_id': int(row['section_id']),
        'section_num': row['section_num'],
        'term_id': int(row['term_id']),
    } for row in sis_sections]
    db.session.execute(query, {'json_dumps': json.dumps(data)})
Beispiel #13
0
 def delete(cls, uid):
     now = utc_now()
     user = cls.query.filter_by(uid=uid).first()
     user.deleted_at = now
     std_commit()
     return user
Beispiel #14
0
from diablo.models.email_template import EmailTemplate
from diablo.models.job import Job
from diablo.models.room import Room
from diablo.models.sis_section import SisSection
from flask import current_app as app
from sqlalchemy.sql import text
from tests.util import simply_yield

_test_users = [
    {
        'uid': '90001',
        'deleted_at': None,
    },
    {
        'uid': '90002',
        'deleted_at': utc_now(),
    },
]


def clear():
    with open(app.config['BASE_DIR'] + '/scripts/db/drop_schema.sql',
              'r') as ddlfile:
        db.session().execute(text(ddlfile.read()))
        std_commit()


def load(create_test_data=True):
    cache.clear()
    _load_schemas()
    if create_test_data: