def validate_sids(): params = request.get_json() sids = [sid.strip() for sid in list(params.get('sids'))] domain = params.get('domain') or 'default' if is_unauthorized_domain(domain): raise ForbiddenRequestError( f'You are unauthorized to query the \'{domain}\' domain') if sids: if next((sid for sid in sids if not sid.isnumeric()), None): raise BadRequestError('Each SID must be numeric') else: summary = [] if domain == 'admitted_students': available_sids = [ row['sid'] for row in get_admitted_students_by_sids(offset=0, sids=sids) ] else: available_sids = query_students( sids=sids, sids_only=True, academic_career_status=('all'))['sids'] for sid in sids: summary.append({ 'sid': sid, 'status': 200 if sid in available_sids else 404, }) return tolerant_jsonify(summary) else: raise BadRequestError('Requires \'sids\' param')
def to_api_json(self, include_students, order_by='last_name', offset=0, limit=50): benchmark = get_benchmarker(f'CuratedGroup {self.id} to_api_json') benchmark('begin') sids = CuratedGroupStudent.get_sids(curated_group_id=self.id) feed = { 'domain': self.domain, 'id': self.id, 'name': self.name, 'ownerId': self.owner_id, 'sids': sids, 'totalStudentCount': len(sids), } if include_students: if sids: if self.domain == 'admitted_students': feed['students'] = get_admitted_students_by_sids( limit=limit, offset=offset, order_by=order_by, sids=sids, ) else: result = query_students( sids=sids, academic_career_status=('all'), include_profiles=False, order_by=order_by, offset=offset, limit=limit, ) feed['students'] = result['students'] else: feed['students'] = [] benchmark('end') return feed
def to_api_json(self, order_by='last_name', offset=0, limit=50, include_students=True): feed = { 'id': self.id, 'ownerId': self.owner_id, 'name': self.name, } if include_students: sids = CuratedGroupStudent.get_sids(curated_group_id=self.id) if sids: result = query_students(sids=sids, order_by=order_by, offset=offset, limit=limit, include_profiles=False) feed['students'] = result['students'] feed['studentCount'] = result['totalStudentCount'] else: feed['students'] = [] feed['studentCount'] = 0 return feed
def validate_sids(): params = request.get_json() sids = [sid.strip() for sid in list(params.get('sids'))] if sids: if next((sid for sid in sids if not sid.isnumeric()), None): raise BadRequestError(f'Each SID must be numeric') else: summary = [] available_sids = query_students(sids=sids, sids_only=True)['sids'] for sid in sids: summary.append({ 'sid': sid, 'status': 200 if sid in available_sids else 404, }) return tolerant_jsonify(summary) else: raise BadRequestError('Requires \'sids\' param')
def get_students(): params = request.get_json() if is_unauthorized_search(params): raise ForbiddenRequestError( 'You are unauthorized to access student data managed by other departments' ) results = query_students( advisor_ldap_uids=util.get(params, 'advisorLdapUids'), coe_prep_statuses=util.get(params, 'coePrepStatuses'), ethnicities=util.get(params, 'ethnicities'), genders=util.get(params, 'genders'), gpa_ranges=util.get(params, 'gpaRanges'), group_codes=util.get(params, 'groupCodes'), include_profiles=True, is_active_asc=_convert_asc_inactive_arg( util.get(params, 'isInactiveAsc')), in_intensive_cohort=util.to_bool_or_none( util.get(params, 'inIntensiveCohort')), last_name_range=_get_name_range_boundaries( util.get(params, 'lastNameRange')), levels=util.get(params, 'levels'), limit=util.get(params, 'limit', 50), majors=util.get(params, 'majors'), offset=util.get(params, 'offset', 0), order_by=util.get(params, 'orderBy', None), underrepresented=util.get(params, 'underrepresented'), unit_ranges=util.get(params, 'unitRanges'), ) if results is None: raise BadRequestError('Invalid search criteria') alert_counts = Alert.current_alert_counts_for_viewer(current_user.id) students = results['students'] if results else [] add_alert_counts(alert_counts, students) return tolerant_jsonify({ 'students': students, 'totalStudentCount': results['totalStudentCount'] if results else 0, })
def to_api_json(self, order_by='last_name', offset=0, limit=50, include_students=True): feed = { 'id': self.id, 'ownerId': self.owner_id, 'name': self.name, } if include_students: sids = CuratedGroupStudent.get_sids(curated_group_id=self.id) if sids: result = query_students(sids=sids, order_by=order_by, offset=offset, limit=limit, include_profiles=False) feed['students'] = result['students'] feed['totalStudentCount'] = result['totalStudentCount'] # Attempt to supplement with historical student rows if we seem to be missing something. if result['totalStudentCount'] < len(sids): remaining_sids = list(set(sids) - set(result['sids'])) historical_sid_rows = query_historical_sids(remaining_sids) if len(historical_sid_rows): for row in historical_sid_rows: ManuallyAddedAdvisee.find_or_create(row['sid']) feed['totalStudentCount'] += len(historical_sid_rows) page_shortfall = max(0, limit - len(result['students'])) feed[ 'students'] += historical_sid_rows[:page_shortfall] else: feed['students'] = [] feed['totalStudentCount'] = 0 return feed
def to_api_json( self, order_by=None, offset=0, limit=50, alert_offset=None, alert_limit=None, include_sids=False, include_students=True, include_profiles=False, include_alerts_for_user_id=None, ): benchmark = get_benchmarker(f'CohortFilter {self.id} to_api_json') benchmark('begin') c = self.filter_criteria c = c if isinstance(c, dict) else json.loads(c) coe_advisor_ldap_uids = util.get(c, 'coeAdvisorLdapUids') if not isinstance(coe_advisor_ldap_uids, list): coe_advisor_ldap_uids = [coe_advisor_ldap_uids ] if coe_advisor_ldap_uids else None cohort_name = self.name cohort_json = { 'id': self.id, 'code': self.id, 'name': cohort_name, 'owners': [], 'alertCount': self.alert_count, } for owner in self.owners: cohort_json['owners'].append({ 'uid': owner.uid, 'deptCodes': [ m.university_dept.dept_code for m in owner.department_memberships ], }) coe_ethnicities = c.get('coeEthnicities') coe_genders = c.get('coeGenders') coe_prep_statuses = c.get('coePrepStatuses') coe_probation = util.to_bool_or_none(c.get('coeProbation')) coe_underrepresented = util.to_bool_or_none( c.get('coeUnderrepresented')) cohort_owner_academic_plans = util.get(c, 'cohortOwnerAcademicPlans') entering_terms = c.get('enteringTerms') ethnicities = c.get('ethnicities') expected_grad_terms = c.get('expectedGradTerms') genders = c.get('genders') gpa_ranges = c.get('gpaRanges') group_codes = c.get('groupCodes') in_intensive_cohort = util.to_bool_or_none(c.get('inIntensiveCohort')) is_inactive_asc = util.to_bool_or_none(c.get('isInactiveAsc')) is_inactive_coe = util.to_bool_or_none(c.get('isInactiveCoe')) last_name_ranges = c.get('lastNameRanges') last_term_gpa_ranges = c.get('lastTermGpaRanges') levels = c.get('levels') majors = c.get('majors') midpoint_deficient_grade = util.to_bool_or_none( c.get('midpointDeficient')) team_groups = athletics.get_team_groups( group_codes) if group_codes else [] transfer = util.to_bool_or_none(c.get('transfer')) underrepresented = util.to_bool_or_none(c.get('underrepresented')) unit_ranges = c.get('unitRanges') cohort_json.update({ 'criteria': { 'coeAdvisorLdapUids': coe_advisor_ldap_uids, 'coeEthnicities': coe_ethnicities, 'coeGenders': coe_genders, 'coePrepStatuses': coe_prep_statuses, 'coeProbation': coe_probation, 'coeUnderrepresented': coe_underrepresented, 'cohortOwnerAcademicPlans': cohort_owner_academic_plans, 'enteringTerms': entering_terms, 'ethnicities': ethnicities, 'expectedGradTerms': expected_grad_terms, 'genders': genders, 'gpaRanges': gpa_ranges, 'groupCodes': group_codes, 'inIntensiveCohort': in_intensive_cohort, 'isInactiveAsc': is_inactive_asc, 'isInactiveCoe': is_inactive_coe, 'lastNameRanges': last_name_ranges, 'lastTermGpaRanges': last_term_gpa_ranges, 'levels': levels, 'majors': majors, 'midpointDeficient': midpoint_deficient_grade, 'transfer': transfer, 'unitRanges': unit_ranges, 'underrepresented': underrepresented, }, 'teamGroups': team_groups, }) if not include_students and not include_alerts_for_user_id and self.student_count is not None: # No need for a students query; return the database-stashed student count. cohort_json.update({ 'totalStudentCount': self.student_count, }) benchmark('end') return cohort_json benchmark('begin students query') sids_only = not include_students # Translate the "My Students" filter, if present, into queryable criteria. Although our database relationships allow # for multiple cohort owners, we assume a single owner here since the "My Students" filter makes no sense # in any other scenario. if cohort_owner_academic_plans: if self.owners: owner_sid = get_csid_for_uid(app, self.owners[0].uid) else: owner_sid = current_user.get_csid() advisor_plan_mappings = [{ 'advisor_sid': owner_sid, 'academic_plan_code': plan } for plan in cohort_owner_academic_plans] else: advisor_plan_mappings = None results = query_students( advisor_plan_mappings=advisor_plan_mappings, coe_advisor_ldap_uids=coe_advisor_ldap_uids, coe_ethnicities=coe_ethnicities, coe_genders=coe_genders, coe_prep_statuses=coe_prep_statuses, coe_probation=coe_probation, coe_underrepresented=coe_underrepresented, entering_terms=entering_terms, ethnicities=ethnicities, expected_grad_terms=expected_grad_terms, genders=genders, gpa_ranges=gpa_ranges, group_codes=group_codes, in_intensive_cohort=in_intensive_cohort, include_profiles=(include_students and include_profiles), is_active_asc=None if is_inactive_asc is None else not is_inactive_asc, is_active_coe=None if is_inactive_coe is None else not is_inactive_coe, last_name_ranges=last_name_ranges, last_term_gpa_ranges=last_term_gpa_ranges, levels=levels, limit=limit, majors=majors, midpoint_deficient_grade=midpoint_deficient_grade, offset=offset, order_by=order_by, sids_only=sids_only, transfer=transfer, underrepresented=underrepresented, unit_ranges=unit_ranges, ) benchmark('end students query') if results: # Cohort might have tens of thousands of SIDs. if include_sids: cohort_json['sids'] = results['sids'] cohort_json.update({ 'totalStudentCount': results['totalStudentCount'], }) # If the cohort is new or cache refresh is underway then store student_count and sids in the db. if self.student_count is None: self.update_sids_and_student_count( results['sids'], results['totalStudentCount']) if include_students: cohort_json.update({ 'students': results['students'], }) if include_alerts_for_user_id: benchmark('begin alerts query') alert_count_per_sid = Alert.include_alert_counts_for_students( viewer_user_id=include_alerts_for_user_id, group=results, offset=alert_offset, limit=alert_limit, ) benchmark('end alerts query') cohort_json.update({ 'alerts': alert_count_per_sid, }) if self.alert_count is None: alert_count = sum(student['alertCount'] for student in alert_count_per_sid) self.update_alert_count(alert_count) cohort_json.update({ 'alertCount': alert_count, }) benchmark('end') return cohort_json
def _query_students( benchmark, criteria, include_profiles, limit, offset, order_by, owner, sids_only, term_id, ): benchmark('begin students query') # Translate the "My Students" filter, if present, into queryable criteria. plans = criteria.get('cohortOwnerAcademicPlans') if plans: if owner: owner_sid = get_csid_for_uid(app, owner.uid) else: owner_sid = current_user.get_csid() advisor_plan_mappings = [{ 'advisor_sid': owner_sid, 'academic_plan_code': plan } for plan in plans] else: advisor_plan_mappings = None coe_advisor_ldap_uids = util.get(criteria, 'coeAdvisorLdapUids') if not isinstance(coe_advisor_ldap_uids, list): coe_advisor_ldap_uids = [coe_advisor_ldap_uids ] if coe_advisor_ldap_uids else None results = query_students( academic_standings=criteria.get('academicStandings'), advisor_plan_mappings=advisor_plan_mappings, coe_advisor_ldap_uids=coe_advisor_ldap_uids, coe_ethnicities=criteria.get('coeEthnicities'), coe_genders=criteria.get('coeGenders'), coe_prep_statuses=criteria.get('coePrepStatuses'), coe_probation=criteria.get('coeProbation'), coe_underrepresented=criteria.get('coeUnderrepresented'), colleges=criteria.get('colleges'), curated_group_ids=criteria.get('curatedGroupIds'), entering_terms=criteria.get('enteringTerms'), epn_cpn_grading_terms=criteria.get('epnCpnGradingTerms'), ethnicities=criteria.get('ethnicities'), expected_grad_terms=criteria.get('expectedGradTerms'), genders=criteria.get('genders'), gpa_ranges=criteria.get('gpaRanges'), group_codes=criteria.get('groupCodes'), in_intensive_cohort=criteria.get('inIntensiveCohort'), include_profiles=include_profiles, intended_majors=criteria.get('intendedMajors'), is_active_asc=None if criteria.get('isInactiveAsc') is None else not criteria.get('isInactiveAsc'), is_active_coe=None if criteria.get('isInactiveCoe') is None else not criteria.get('isInactiveCoe'), last_name_ranges=criteria.get('lastNameRanges'), last_term_gpa_ranges=criteria.get('lastTermGpaRanges'), levels=criteria.get('levels'), limit=limit, majors=criteria.get('majors'), midpoint_deficient_grade=criteria.get('midpointDeficient'), minors=criteria.get('minors'), offset=offset, order_by=order_by, sids_only=sids_only, term_id=term_id, transfer=criteria.get('transfer'), underrepresented=criteria.get('underrepresented'), unit_ranges=criteria.get('unitRanges'), visa_types=criteria.get('visaTypes'), student_holds=criteria.get('studentHolds'), ) benchmark('end students query') return results
def to_api_json( self, order_by=None, offset=0, limit=50, include_students=True, include_profiles=False, include_alerts_for_uid=None, ): c = self.filter_criteria c = c if isinstance(c, dict) else json.loads(c) advisor_ldap_uids = util.get(c, 'advisorLdapUids') if not isinstance(advisor_ldap_uids, list): advisor_ldap_uids = [advisor_ldap_uids ] if advisor_ldap_uids else None cohort_name = self.label cohort_json = { 'id': self.id, 'code': self.id, 'label': cohort_name, 'name': cohort_name, 'owners': [user.uid for user in self.owners], } coe_prep_statuses = c.get('coePrepStatuses') ethnicities = c.get('ethnicities') genders = c.get('genders') gpa_ranges = c.get('gpaRanges') group_codes = c.get('groupCodes') in_intensive_cohort = util.to_bool_or_none(c.get('inIntensiveCohort')) is_inactive_asc = util.to_bool_or_none(c.get('isInactiveAsc')) last_name_range = c.get('lastNameRange') levels = c.get('levels') majors = c.get('majors') team_groups = athletics.get_team_groups( group_codes) if group_codes else [] underrepresented = util.to_bool_or_none(c.get('underrepresented')) unit_ranges = c.get('unitRanges') cohort_json.update({ 'filterCriteria': { 'advisorLdapUids': advisor_ldap_uids, 'coePrepStatuses': coe_prep_statuses, 'ethnicities': ethnicities, 'genders': genders, 'gpaRanges': gpa_ranges, 'groupCodes': group_codes, 'inIntensiveCohort': in_intensive_cohort, 'isInactiveAsc': is_inactive_asc, 'lastNameRange': last_name_range, 'levels': levels, 'majors': majors, 'unitRanges': unit_ranges, 'underrepresented': underrepresented, }, 'teamGroups': team_groups, }) if not include_students and not include_alerts_for_uid and self.student_count is not None: # No need for a students query; return the database-stashed student count. cohort_json.update({ 'totalStudentCount': self.student_count, }) return cohort_json owner = self.owners[0] if len(self.owners) else None if owner and 'UWASC' in get_dept_codes(owner): is_active_asc = not is_inactive_asc else: is_active_asc = None if is_inactive_asc is None else not is_inactive_asc sids_only = not include_students results = query_students( advisor_ldap_uids=advisor_ldap_uids, coe_prep_statuses=coe_prep_statuses, ethnicities=ethnicities, genders=genders, gpa_ranges=gpa_ranges, group_codes=group_codes, in_intensive_cohort=in_intensive_cohort, include_profiles=(include_students and include_profiles), is_active_asc=is_active_asc, last_name_range=last_name_range, levels=levels, limit=limit, majors=majors, offset=offset, order_by=order_by, sids_only=sids_only, underrepresented=underrepresented, unit_ranges=unit_ranges, ) if results: # If the cohort is newly created or a cache refresh is underway, store the student count in the database # to save future queries. if self.student_count is None: self.update_student_count(results['totalStudentCount']) cohort_json.update({ 'totalStudentCount': results['totalStudentCount'], }) if include_students: cohort_json.update({ 'students': results['students'], }) if include_alerts_for_uid: alert_counts = Alert.include_alert_counts_for_students( viewer_uid=include_alerts_for_uid, cohort=results) cohort_json.update({ 'alerts': alert_counts, }) return cohort_json