Пример #1
0
def get_full_student_profiles(sids):
    benchmark = get_benchmarker('get_full_student_profiles')
    benchmark('begin')
    if not sids:
        return []
    benchmark('begin SIS profile query')
    profile_results = data_loch.get_student_profiles(sids)
    benchmark('end SIS profile query')
    if not profile_results:
        return []
    profiles_by_sid = _get_profiles_by_sid(profile_results)
    profiles = []
    for sid in sids:
        profile = profiles_by_sid.get(sid)
        if profile:
            profiles.append(profile)

    benchmark('begin photo merge')
    _merge_photo_urls(profiles)
    benchmark('end photo merge')

    scope = get_student_query_scope()

    benchmark('begin ASC profile merge')
    _merge_asc_student_profile_data(profiles_by_sid, scope)
    benchmark('end ASC profile merge')

    if 'COENG' in scope or 'ADMIN' in scope:
        benchmark('begin COE profile merge')
        _merge_coe_student_profile_data(profiles_by_sid)
        benchmark('end COE profile merge')
    return profiles
Пример #2
0
def response_with_students_csv_download(sids, benchmark):
    rows = []
    for student in get_student_profiles(sids=sids):
        profile = student.get('profile')
        profile = profile and json.loads(profile)
        rows.append({
            'first_name': profile.get('firstName'),
            'last_name': profile.get('lastName'),
            'sid': profile.get('sid'),
            'email': profile.get('sisProfile', {}).get('emailAddress'),
            'phone': profile.get('sisProfile', {}).get('phoneNumber'),
        })
    benchmark('end')

    def _norm(row, key):
        value = row.get(key)
        return value and value.upper()

    return response_with_csv_download(
        rows=sorted(
            rows,
            key=lambda r:
            (_norm(r, 'last_name'), _norm(r, 'first_name'), _norm(r, 'sid'))),
        filename_prefix='cohort',
        fieldnames=['first_name', 'last_name', 'sid', 'email', 'phone'],
    )
Пример #3
0
def get_full_student_profiles(sids):
    if not sids:
        return []
    profile_results = data_loch.get_student_profiles(sids)
    if not profile_results:
        return []
    profiles_by_sid = {
        row['sid']: json.loads(row['profile'])
        for row in profile_results
    }
    profiles = []
    for sid in sids:
        profile = profiles_by_sid.get(sid)
        if profile:
            profiles.append(profile)

    scope = get_student_query_scope()
    if 'UWASC' in scope or 'ADMIN' in scope:
        athletics_profiles = data_loch.get_athletics_profiles(sids)
        for row in athletics_profiles:
            profile = profiles_by_sid.get(row['sid'])
            if profile:
                profile['athleticsProfile'] = json.loads(row['profile'])
    if 'COENG' in scope or 'ADMIN' in scope:
        coe_profiles = data_loch.get_coe_profiles(sids)
        if coe_profiles:
            for row in coe_profiles:
                profile = profiles_by_sid.get(row['sid'])
                if profile:
                    profile['coeProfile'] = json.loads(row['profile'])

    return profiles
Пример #4
0
 def update_all_for_term(cls, term_id):
     app.logger.info('Starting alert update')
     enrollments_for_term = data_loch.get_enrollments_for_term(str(term_id))
     no_activity_alerts_enabled = cls.no_activity_alerts_enabled()
     infrequent_activity_alerts_enabled = cls.infrequent_activity_alerts_enabled(
     )
     for row in enrollments_for_term:
         enrollments = json.loads(row['enrollment_term']).get(
             'enrollments', [])
         for enrollment in enrollments:
             cls.update_alerts_for_enrollment(
                 row['sid'], term_id, enrollment,
                 no_activity_alerts_enabled,
                 infrequent_activity_alerts_enabled)
     if app.config['ALERT_HOLDS_ENABLED'] and str(
             term_id) == current_term_id():
         holds = data_loch.get_sis_holds()
         for row in holds:
             hold_feed = json.loads(row['feed'])
             cls.update_hold_alerts(row['sid'], term_id,
                                    hold_feed.get('type'),
                                    hold_feed.get('reason'))
     if app.config['ALERT_WITHDRAWAL_ENABLED'] and str(
             term_id) == current_term_id():
         profiles = data_loch.get_student_profiles()
         for row in profiles:
             profile_feed = json.loads(row['profile'])
             if 'withdrawalCancel' in (profile_feed.get('sisProfile')
                                       or {}):
                 cls.update_withdrawal_cancel_alerts(row['sid'], term_id)
     app.logger.info('Alert update complete')
Пример #5
0
def update_curated_group_lists():
    """Remove no-longer-accessible students from curated group lists."""
    from boac.models.curated_group import CuratedGroup
    for curated_group in CuratedGroup.query.all():
        all_sids = CuratedGroupStudent.get_sids(curated_group.id)
        available_students = [s['sid'] for s in data_loch.get_student_profiles(all_sids)]
        if len(all_sids) > len(available_students):
            unavailable_sids = set(all_sids) - set(available_students)
            app.logger.info(f'Deleting inaccessible SIDs from curated group {curated_group.id}: {unavailable_sids}')
            for sid in unavailable_sids:
                CuratedGroup.remove_student(curated_group.id, sid)
Пример #6
0
    def test_get_student_profiles(self, app):
        import json
        sid = '11667051'
        student_profiles = data_loch.get_student_profiles([sid])
        assert len(student_profiles) == 1

        student = student_profiles[0]
        assert student['sid'] == sid
        assert student['gender'] == 'Different Identity'
        assert student['minority'] is False
        sis_profile = json.loads(student_profiles[0]['profile'])['sisProfile']
        assert sis_profile['academicCareer'] == 'UGRD'
Пример #7
0
def update_curated_cohort_lists():
    """Remove no-longer-accessible students from curated cohort lists."""
    from boac.models.curated_cohort import CuratedCohort
    for cohort in CuratedCohort.query.all():
        member_sids = [s.sid for s in cohort.students]
        available_students = [s['sid'] for s in data_loch.get_student_profiles(member_sids)]
        if len(member_sids) > len(available_students):
            cohort_id = cohort.id
            unavailable_sids = set(member_sids) - set(available_students)
            app.logger.info(f'Deleting inaccessible SIDs from cohort ID {cohort_id} : {unavailable_sids}')
            for sid in unavailable_sids:
                CuratedCohort.remove_student(cohort_id, sid)
Пример #8
0
    def test_get_student_profiles(self):
        import json
        sid = '11667051'
        student_profiles = data_loch.get_student_profiles([sid])
        assert len(student_profiles) == 1

        student = student_profiles[0]
        assert student['sid'] == sid
        profile = json.loads(student['profile'])
        assert profile['demographics']['gender'] == 'Different Identity'
        assert profile['demographics']['underrepresented'] is False
        assert profile['sisProfile']['academicCareer'] == 'UGRD'
Пример #9
0
    def update_all_for_term(cls, term_id):
        app.logger.info('Starting alert update')
        enrollments_for_term = data_loch.get_enrollments_for_term(str(term_id))
        no_activity_alerts_enabled = cls.no_activity_alerts_enabled()
        infrequent_activity_alerts_enabled = cls.infrequent_activity_alerts_enabled(
        )
        for row in enrollments_for_term:
            enrollments = json.loads(row['enrollment_term']).get(
                'enrollments', [])
            for enrollment in enrollments:
                cls.update_alerts_for_enrollment(
                    sid=row['sid'],
                    term_id=term_id,
                    enrollment=enrollment,
                    no_activity_alerts_enabled=no_activity_alerts_enabled,
                    infrequent_activity_alerts_enabled=
                    infrequent_activity_alerts_enabled,
                )
        profiles = data_loch.get_student_profiles()
        if app.config['ALERT_WITHDRAWAL_ENABLED'] and str(
                term_id) == current_term_id():
            for row in profiles:
                sis_profile_feed = json.loads(
                    row['profile']).get('sisProfile') or {}
                if sis_profile_feed.get('withdrawalCancel',
                                        {}).get('termId') == str(term_id):
                    cls.update_withdrawal_cancel_alerts(row['sid'], term_id)

        sids = [p['sid'] for p in profiles]
        for sid, academic_standing_list in get_academic_standing_by_sid(
                sids).items():
            standing = next((s for s in academic_standing_list
                             if s['termId'] == str(term_id)), None)
            if standing and standing['status'] in ('DIS', 'PRO', 'SUB'):
                cls.update_academic_standing_alerts(
                    action_date=standing['actionDate'],
                    sid=standing['sid'],
                    status=standing['status'],
                    term_id=term_id,
                )
        app.logger.info('Alert update complete')
Пример #10
0
def get_full_student_profiles(sids):
    benchmark = get_benchmarker('get_full_student_profiles')
    benchmark('begin')
    if not sids:
        return []
    benchmark('begin SIS profile query')
    profile_results = data_loch.get_student_profiles(sids)
    benchmark('end SIS profile query')
    if not profile_results:
        return []
    profiles_by_sid = _get_profiles_by_sid(profile_results)
    profiles = []
    for sid in sids:
        profile = profiles_by_sid.get(sid)
        if profile:
            profiles.append(profile)

    benchmark('begin photo merge')
    _merge_photo_urls(profiles)
    benchmark('end photo merge')

    scope = get_student_query_scope()
    if 'UWASC' in scope or 'ADMIN' in scope:
        benchmark('begin ASC profile merge')
        athletics_profiles = data_loch.get_athletics_profiles(sids)
        for row in athletics_profiles:
            profile = profiles_by_sid.get(row['sid'])
            if profile:
                profile['athleticsProfile'] = json.loads(row['profile'])
        benchmark('end ASC profile merge')
    if 'COENG' in scope or 'ADMIN' in scope:
        benchmark('begin COE profile merge')
        coe_profiles = data_loch.get_coe_profiles(sids)
        if coe_profiles:
            for coe_profile in coe_profiles:
                sid = coe_profile['sid']
                _merge_coe_student_profile_data(profiles_by_sid.get(sid),
                                                coe_profile)
        benchmark('end COE profile merge')
    return profiles
Пример #11
0
def response_with_students_csv_download(sids, fieldnames, benchmark):
    rows = []
    getters = {
        'first_name':
        lambda profile: profile.get('firstName'),
        'last_name':
        lambda profile: profile.get('lastName'),
        'sid':
        lambda profile: profile.get('sid'),
        'email':
        lambda profile: profile.get('sisProfile', {}).get('emailAddress'),
        'phone':
        lambda profile: profile.get('sisProfile', {}).get('phoneNumber'),
        'majors':
        lambda profile: ';'.join([
            plan.get('description')
            for plan in profile.get('sisProfile', {}).get('plans', [])
            if plan.get('status') == 'Active'
        ], ),
        'level':
        lambda profile: profile.get('sisProfile', {}).get('level', {}).get(
            'description'),
        'terms_in_attendance':
        lambda profile: profile.get('sisProfile', {}).get('termsInAttendance'),
        'expected_graduation_date':
        lambda profile: profile.get('sisProfile', {}).get(
            'expectedGraduationTerm', {}).get('name'),
        'units_completed':
        lambda profile: profile.get('sisProfile', {}).get('cumulativeUnits'),
        'term_gpa':
        lambda profile: profile.get('termGpa'),
        'cumulative_gpa':
        lambda profile: profile.get('sisProfile', {}).get('cumulativeGPA'),
        'program_status':
        lambda profile: profile.get('sisProfile', {}).get(
            'academicCareerStatus'),
    }
    term_gpas = get_term_gpas_by_sid(sids, as_dicts=True)
    for student in get_student_profiles(sids=sids):
        profile = student.get('profile')
        profile = profile and json.loads(profile)
        student_term_gpas = term_gpas.get(profile['sid'])
        profile['termGpa'] = student_term_gpas[sorted(
            student_term_gpas)[-1]] if student_term_gpas else None
        row = {}
        for fieldname in fieldnames:
            row[fieldname] = getters[fieldname](profile)
        rows.append(row)
    benchmark('end')

    def _norm(row, key):
        value = row.get(key)
        return value and value.upper()

    return response_with_csv_download(
        rows=sorted(
            rows,
            key=lambda r:
            (_norm(r, 'last_name'), _norm(r, 'first_name'), _norm(r, 'sid'))),
        filename_prefix='cohort',
        fieldnames=fieldnames,
    )
Пример #12
0
def response_with_students_csv_download(sids, fieldnames, benchmark):
    term_id_last = previous_term_id(current_term_id())
    term_id_previous = previous_term_id(term_id_last)
    rows = []
    getters = {
        'first_name':
        lambda profile: profile.get('firstName'),
        'last_name':
        lambda profile: profile.get('lastName'),
        'sid':
        lambda profile: profile.get('sid'),
        'email':
        lambda profile: profile.get('sisProfile', {}).get('emailAddress'),
        'phone':
        lambda profile: profile.get('sisProfile', {}).get('phoneNumber'),
        'majors':
        lambda profile: ';'.join([
            plan.get('description')
            for plan in profile.get('sisProfile', {}).get('plans', [])
            if plan.get('status') == 'Active'
        ], ),
        'intended_majors':
        lambda profile: ';'.join([
            major.get('description')
            for major in profile.get('sisProfile', {}).get('intendedMajors')
        ], ),
        'level_by_units':
        lambda profile: profile.get('sisProfile', {}).get('level', {}).get(
            'description'),
        'minors':
        lambda profile: ';'.join([
            plan.get('description')
            for plan in profile.get('sisProfile', {}).get('plansMinor', [])
            if plan.get('status') == 'Active'
        ], ),
        'subplans':
        lambda profile: ';'.join([
            subplan
            for subplan in profile.get('sisProfile', {}).get('subplans', [])
        ]),
        'terms_in_attendance':
        lambda profile: profile.get('sisProfile', {}).get('termsInAttendance'),
        'expected_graduation_term':
        lambda profile: profile.get('sisProfile', {}).get(
            'expectedGraduationTerm', {}).get('name'),
        'units_completed':
        lambda profile: profile.get('sisProfile', {}).get('cumulativeUnits'),
        f'term_gpa_{term_id_previous}':
        lambda profile: profile.get('termGpa', {}).get(term_id_previous),
        f'term_gpa_{term_id_last}':
        lambda profile: profile.get('termGpa', {}).get(term_id_last),
        'cumulative_gpa':
        lambda profile: profile.get('sisProfile', {}).get('cumulativeGPA'),
        'program_status':
        lambda profile: ';'.join(
            list(
                set([
                    plan.get('status')
                    for plan in profile.get('sisProfile', {}).get('plans', [])
                ], ), ), ),
        'academic_standing':
        lambda profile: profile.get('academicStanding'),
        'transfer':
        lambda profile: 'Yes'
        if profile.get('sisProfile', {}).get('transfer') else '',
        'intended_major':
        lambda profile: ', '.join([
            major.get('description') for major in
            (profile.get('sisProfile', {}).get('intendedMajors') or [])
        ]),
        'units_in_progress':
        lambda profile: profile.get('enrolledUnits', {}),
    }
    academic_standing = get_academic_standing_by_sid(sids, as_dicts=True)
    term_gpas = get_term_gpas_by_sid(sids, as_dicts=True)
    term_units = get_term_units_by_sid(current_term_id(), sids)

    def _get_last_element(results):
        return results[sorted(results)[-1]] if results else None

    def _add_row(student_profile):
        student_profile['academicStanding'] = _get_last_element(
            academic_standing.get(student_profile['sid']))
        student_profile['termGpa'] = term_gpas.get(student_profile['sid'], {})
        student_profile['enrolledUnits'] = term_units[student_profile['sid']]
        row = {}
        for fieldname in fieldnames:
            row[fieldname] = getters[fieldname](student_profile)
        rows.append(row)

    students = get_student_profiles(sids=sids)
    for student in students:
        profile = student.get('profile')
        if profile:
            _add_row(json.loads(profile))
    remaining_sids = list(set(sids) - set([s.get('sid') for s in students]))
    if remaining_sids:
        for profile in get_historical_student_profiles(remaining_sids):
            _add_row(profile)

    benchmark('end')

    return response_with_csv_download(
        rows=sorted(
            rows,
            key=lambda r:
            (_norm(r, 'last_name'), _norm(r, 'first_name'), _norm(r, 'sid'))),
        filename_prefix='cohort',
        fieldnames=fieldnames,
    )