Пример #1
0
def merge_sis_profile_plans(academic_status, sis_profile):
    sis_profile['plans'] = []
    for student_plan in academic_status.get('studentPlans', []):
        academic_plan = student_plan.get('academicPlan', {})
        # SIS majors come in five flavors.
        if academic_plan.get('type', {}).get('code') not in ['MAJ', 'SS', 'SP', 'HS', 'CRT']:
            continue
        plan = academic_plan.get('plan', {})
        major = plan.get('description')
        plan_feed = {
            'degreeProgramUrl': degree_program_url_for_major(major),
            'description': major,
        }
        # Find the latest expected graduation term from any plan.
        expected_graduation_term = student_plan.get('expectedGraduationTerm', {}).get('id')
        if expected_graduation_term and expected_graduation_term > sis_profile.get('expectedGraduationTerm', {}).get('id', '0'):
            sis_profile['expectedGraduationTerm'] = {
                'id': expected_graduation_term,
                'name': term_name_for_sis_id(expected_graduation_term),
            }
        # Add program unless plan code indicates undeclared.
        if plan.get('code') != '25000U':
            program = student_plan.get('academicPlan', {}).get('academicProgram', {}).get('program', {})
            plan_feed['program'] = program.get('description')
        # Add plan unless it's a duplicate.
        if not next((p for p in sis_profile['plans'] if p.get('description') == plan_feed.get('description')), None):
            sis_profile['plans'].append(plan_feed)
Пример #2
0
    def refresh_current_term_index(self):
        today = datetime.now(pytz.utc).astimezone(
            pytz.timezone(app.config['TIMEZONE'])).date()
        current_term = self.get_sis_current_term(today)

        if current_term:
            current_term_id = current_term['term_id']

            # If today is one month or less before the end of the current term, or if the current term is summer,
            # include the next term.
            if current_term_id[3] == '5' or (current_term['term_ends'] -
                                             timedelta(weeks=4)) < today:
                future_term_id = next_term_id(current_term['term_id'])
                # ... and if the upcoming term is Summer, include the next Fall term as well.
                if future_term_id[3] == '5':
                    future_term_id = next_term_id(future_term_id)
            else:
                future_term_id = current_term_id

            with rds.transaction() as transaction:
                transaction.execute(
                    f'TRUNCATE {rds_schema}.current_term_index')
                columns = ['current_term_name', 'future_term_name']
                values = tuple([
                    current_term['term_name'],
                    term_name_for_sis_id(future_term_id)
                ])
                if transaction.execute(
                        f'INSERT INTO {rds_schema}.current_term_index ({", ".join(columns)}) VALUES {values} '
                ):
                    transaction.commit()
                else:
                    transaction.rollback()
                    raise BackgroundJobError(
                        'Error refreshing RDS current term index.')
Пример #3
0
def map_sis_enrollments(sis_enrollments):
    student_enrollments_map = {}
    for key, all_sids_grp in groupby(sis_enrollments, operator.itemgetter('sis_term_id')):
        term_id = str(key)
        term_name = berkeley.term_name_for_sis_id(term_id)
        student_enrollments_map[term_id] = {}
        for sid, all_enrs_grp in groupby(all_sids_grp, operator.itemgetter('sid')):
            term_enrollments = merge_enrollment(all_enrs_grp, term_id, term_name)
            student_enrollments_map[term_id][sid] = term_enrollments
    return student_enrollments_map
Пример #4
0
def merge_sis_profile_plans(academic_status, sis_profile):
    plans = []
    plans_minor = []
    subplans = set()
    for student_plan in academic_status.get('studentPlans', []):
        academic_plan = student_plan.get('academicPlan', {})
        # SIS majors come in five flavors, plus a sixth for minors.
        if academic_plan.get('type', {}).get('code') not in ['MAJ', 'SS', 'SP', 'HS', 'CRT', 'MIN']:
            continue
        plan = academic_plan.get('plan', {})
        description = plan.get('description')
        plan_feed = {
            'degreeProgramUrl': degree_program_url_for_major(description),
            'description': description,
        }
        # Find the latest expected graduation term from any plan.
        expected_graduation_term = student_plan.get('expectedGraduationTerm', {}).get('id')
        if expected_graduation_term and expected_graduation_term > sis_profile.get('expectedGraduationTerm', {}).get('id', '0'):
            sis_profile['expectedGraduationTerm'] = {
                'id': expected_graduation_term,
                'name': term_name_for_sis_id(expected_graduation_term),
            }

        program = student_plan.get('academicPlan', {}).get('academicProgram', {}).get('program', {})
        plan_feed['program'] = program.get('formalDescription') or program.get('description')

        # Add plan status.
        plan_status = student_plan.get('statusInPlan', {}).get('status')
        if plan_status:
            plan_feed['status'] = plan_status.get('formalDescription') or plan_status.get('description')
            # We generally prefer the 'formalDescription', but our formality has limits.
            if plan_feed['status'] == 'Active in Program':
                plan_feed['status'] = 'Active'
        else:
            # A plan with no status is considered discontinued. (NS-689)
            plan_feed['status'] = 'Discontinued'

        # Add plan unless it's a duplicate.
        if academic_plan.get('type', {}).get('code') == 'MIN':
            plan_collection = plans_minor
        else:
            plan_collection = plans
        if not next((p for p in plan_collection if p.get('description') == plan_feed.get('description')), None):
            plan_collection.append(plan_feed)

        # Add any subplans.
        for academic_subplan in student_plan.get('academicSubPlans', []):
            subplan_description = academic_subplan.get('subPlan', {}).get('description')
            if subplan_description:
                subplans.add(subplan_description)

    sis_profile['plans'] = sorted(plans, key=itemgetter('description'))
    sis_profile['plansMinor'] = sorted(plans_minor, key=itemgetter('description'))
    sis_profile['subplans'] = sorted(list(subplans))
    def generate_term_feeds(self, sids, feed_file):
        enrollment_stream = queries.stream_sis_enrollments(sids=sids)
        term_gpa_stream = queries.stream_term_gpas(sids=sids)
        term_gpa_tracker = {'term_id': '9999', 'sid': '', 'term_gpas': []}

        row_count = 0

        try:
            term_gpa_results = groupby(term_gpa_stream, lambda r:
                                       (str(r['term_id']), r['sid']))

            for term_id, term_enrollments_grp in groupby(
                    enrollment_stream, operator.itemgetter('sis_term_id')):
                term_id = str(term_id)
                term_name = berkeley.term_name_for_sis_id(term_id)
                for sid, enrollments_grp in groupby(
                        term_enrollments_grp, operator.itemgetter('sid')):
                    term_feed = None
                    for is_dropped, enrollments_subgroup in groupby(
                            enrollments_grp, operator.itemgetter('dropped')):
                        if not is_dropped:
                            term_feed = merge_enrollment(
                                enrollments_subgroup, term_id, term_name)
                        else:
                            if not term_feed:
                                term_feed = empty_term_feed(term_id, term_name)
                            append_drops(term_feed, enrollments_subgroup)

                    while term_gpa_tracker['term_id'] > term_id or (
                            term_gpa_tracker['term_id'] == term_id
                            and term_gpa_tracker['sid'] < sid):
                        (term_gpa_tracker['term_id'], term_gpa_tracker['sid']
                         ), term_gpa_tracker['term_gpas'] = next(
                             term_gpa_results)
                    if term_gpa_tracker[
                            'term_id'] == term_id and term_gpa_tracker[
                                'sid'] == sid:
                        append_term_gpa(term_feed,
                                        term_gpa_tracker['term_gpas'])

                    feed_file.write(
                        encoded_tsv_row([sid, term_id,
                                         json.dumps(term_feed)]) + b'\n')
                    row_count += 1

        finally:
            enrollment_stream.close()
            term_gpa_stream.close()

        return row_count
Пример #6
0
def get_merged_enrollment_terms(uid, cs_id, term_ids, canvas_courses_feed, canvas_site_map):
    enrollment_results = queries.get_sis_enrollments(uid) or []
    enrollments_by_term = {}
    for key, group in groupby(enrollment_results, key=operator.itemgetter('sis_term_id')):
        enrollments_by_term[str(key)] = list(group)
    drops_and_midterms_result = queries.get_sis_api_drops_and_midterms(cs_id, term_ids) or []
    term_feeds = {}
    for term_id in term_ids:
        enrollments = enrollments_by_term.get(term_id, [])
        term_name = berkeley.term_name_for_sis_id(term_id)
        drops_and_midterms = next((json.loads(row['feed']) for row in drops_and_midterms_result if str(row['term_id']) == str(term_id)), {})
        term_feed = merge_enrollment(cs_id, enrollments, term_id, term_name, drops_and_midterms)
        for site in canvas_courses_feed:
            merge_canvas_course_site(term_feed, site, canvas_site_map)
        sort_canvas_course_sites(term_feed)
        term_feeds[term_id] = term_feed
    return term_feeds
Пример #7
0
    def refresh_current_term_index(self):
        today = datetime.now(pytz.utc).astimezone(
            pytz.timezone(app.config['TIMEZONE'])).date()
        current_term = self.get_sis_current_term(today)

        if current_term:
            term_id = current_term['term_id']

            # Check if the advance enrollment period has started for the next two upcoming terms.
            future_term_id = term_id
            for _ in range(2):
                term_id = next_term_id(term_id)
                term = self.get_sis_term_for_id(term_id)
                advance_enrollment_period = 0
                if term_id[3] == '2':
                    advance_enrollment_period = 95
                elif term_id[3] == '5':
                    advance_enrollment_period = 124
                elif term_id[3] == '8':
                    advance_enrollment_period = 140
                if term['term_begins'] - timedelta(
                        days=advance_enrollment_period) < today:
                    future_term_id = term_id

            with rds.transaction() as transaction:
                transaction.execute(
                    f'TRUNCATE {rds_schema}.current_term_index')
                columns = ['current_term_name', 'future_term_name']
                values = tuple([
                    current_term['term_name'],
                    term_name_for_sis_id(future_term_id)
                ])
                if transaction.execute(
                        f'INSERT INTO {rds_schema}.current_term_index ({", ".join(columns)}) VALUES {values} '
                ):
                    transaction.commit()
                else:
                    transaction.rollback()
                    raise BackgroundJobError(
                        'Error refreshing RDS current term index.')
Пример #8
0
 def test_term_name_for_sis_id(self):
     assert berkeley.term_name_for_sis_id('2178') == 'Fall 2017'
     assert berkeley.term_name_for_sis_id('1978') == 'Fall 1997'
     assert berkeley.term_name_for_sis_id('1760') == 'Winter 1976'