def create_cohort(): params = request.get_json() domain = get_param(params, 'domain', 'default') if is_unauthorized_domain(domain): raise ForbiddenRequestError( f'You are unauthorized to query the \'{domain}\' domain') name = get_param(params, 'name', None) filters = get_param(params, 'filters', None) order_by = params.get('orderBy') # Authorization check filter_keys = list(map(lambda f: f['key'], filters)) if is_unauthorized_search(filter_keys, order_by): raise ForbiddenRequestError( 'You are unauthorized to access student data managed by other departments' ) filter_criteria = _translate_filters_to_cohort_criteria(filters, domain) if not name or not filter_criteria: raise BadRequestError( 'Cohort creation requires \'name\' and \'filters\'') cohort = CohortFilter.create( uid=current_user.get_uid(), name=name, filter_criteria=filter_criteria, domain=domain, order_by=order_by, include_alerts_for_user_id=current_user.get_id(), ) _decorate_cohort(cohort) return tolerant_jsonify(cohort)
def update_note(): params = request.form body = params.get('body', None) is_private = to_bool_or_none(params.get('isPrivate', False)) note_id = params.get('id', None) subject = params.get('subject', None) topics = get_note_topics_from_http_post() note = Note.find_by_id(note_id=note_id) if note_id else None if not note: raise ResourceNotFoundError('Note not found') if not subject: raise BadRequestError('Note subject is required') if note.author_uid != current_user.get_uid(): raise ForbiddenRequestError( 'Sorry, you are not the author of this note.') if (is_private is not note.is_private ) and not current_user.can_access_private_notes: raise ForbiddenRequestError( 'Sorry, you are not authorized to manage note privacy') note = Note.update( body=process_input_from_rich_text_editor(body), is_private=is_private, note_id=note_id, subject=subject, topics=topics, ) note_read = NoteRead.find_or_create(current_user.get_id(), note_id) return tolerant_jsonify( _boa_note_to_compatible_json(note=note, note_read=note_read))
def _update_drop_in_status(uid, dept_code, active): dept_code = dept_code.upper() if uid == 'me': uid = current_user.get_uid() else: authorized_to_toggle = current_user.is_admin or dept_code in [ d['code'] for d in current_user.departments if d.get('isScheduler') ] if not authorized_to_toggle: raise errors.ForbiddenRequestError( f'Unauthorized to toggle drop-in status for department {dept_code}' ) drop_in_status = None user = AuthorizedUser.find_by_uid(uid) if user: drop_in_status = next( (d for d in user.drop_in_departments if d.dept_code == dept_code), None) if drop_in_status: drop_in_status.update_availability(active) UserSession.flush_cache_for_id(user.id) return tolerant_jsonify(drop_in_status.to_api_json()) else: raise errors.ResourceNotFoundError( f'No drop-in advisor status found: (uid={uid}, dept_code={dept_code})' )
def add_appointment_scheduler_to_dept(dept_code): _verify_membership_and_appointments_enabled(current_user, dept_code) params = request.get_json() or {} scheduler_uid = params.get('uid', None) if not scheduler_uid: raise errors.BadRequestError('Scheduler UID missing') calnet_user = calnet.get_calnet_user_for_uid(app, scheduler_uid, skip_expired_users=True) if not calnet_user or not calnet_user.get('csid'): raise errors.BadRequestError('Invalid scheduler UID') user = AuthorizedUser.create_or_restore( scheduler_uid, created_by=current_user.get_uid(), is_admin=False, is_blocked=False, can_access_canvas_data=False, ) Scheduler.create_or_update_membership( dept_code, user.id, drop_in=True, same_day=True, ) _create_department_memberships(user, [{ 'code': dept_code, 'role': 'scheduler', 'automateMembership': False }]) UserSession.flush_cache_for_id(user.id) return tolerant_jsonify( _get_appointment_scheduler_list(current_user, dept_code))
def _update_drop_in_availability(uid, dept_code, new_availability): dept_code = dept_code.upper() if uid != current_user.get_uid(): authorized_to_toggle = current_user.is_admin or dept_code in [ d['code'] for d in current_user.departments if d.get('role') == 'scheduler' ] if not authorized_to_toggle: raise errors.ForbiddenRequestError( f'Unauthorized to toggle drop-in availability for department {dept_code}' ) drop_in_membership = None user = AuthorizedUser.find_by_uid(uid) if user: drop_in_membership = next( (d for d in user.drop_in_departments if d.dept_code == dept_code), None) if drop_in_membership: if drop_in_membership.is_available is True and new_availability is False: Appointment.unreserve_all_for_advisor(uid, current_user.get_id()) drop_in_membership.update_availability(new_availability) UserSession.flush_cache_for_id(user.id) return tolerant_jsonify(drop_in_membership.to_api_json()) else: raise errors.ResourceNotFoundError( f'No drop-in advisor membership found: (uid={uid}, dept_code={dept_code})' )
def _get_filter_db_type_per_key(domain): filter_type_per_key = {} option_groups = CohortFilterOptions.get_cohort_filter_option_groups( current_user.get_uid(), domain) for label, option_group in option_groups.items(): for option in option_group: filter_type_per_key[option['key']] = option['type']['db'] return filter_type_per_key
def _update_or_create_authorized_user(memberships, profile, include_deleted=False): user_id = profile.get('id') automate_degree_progress_permission = profile.get( 'automateDegreeProgressPermission') can_access_canvas_data = to_bool_or_none( profile.get('canAccessCanvasData')) can_access_advising_data = to_bool_or_none( profile.get('canAccessAdvisingData')) degree_progress_permission = profile.get('degreeProgressPermission') if (automate_degree_progress_permission or degree_progress_permission ) and 'COENG' not in dept_codes_where_advising( {'departments': memberships}): raise errors.BadRequestError( 'Degree Progress feature is only available to the College of Engineering.' ) is_admin = to_bool_or_none(profile.get('isAdmin')) is_blocked = to_bool_or_none(profile.get('isBlocked')) if user_id: user = AuthorizedUser.update_user( automate_degree_progress_permission= automate_degree_progress_permission, can_access_advising_data=can_access_advising_data, can_access_canvas_data=can_access_canvas_data, degree_progress_permission=degree_progress_permission, include_deleted=include_deleted, is_admin=is_admin, is_blocked=is_blocked, user_id=user_id, ) UserSession.flush_cache_for_id(user_id=user_id) return user else: uid = profile.get('uid') if AuthorizedUser.get_id_per_uid(uid, include_deleted=True): raise errors.BadRequestError( f'User with UID {uid} is already in the BOA database.') calnet_user = calnet.get_calnet_user_for_uid(app, uid, skip_expired_users=True) if calnet_user and calnet_user.get('csid', None): return AuthorizedUser.create_or_restore( automate_degree_progress_permission= automate_degree_progress_permission, can_access_advising_data=can_access_advising_data, can_access_canvas_data=can_access_canvas_data, created_by=current_user.get_uid(), degree_progress_permission=degree_progress_permission, is_admin=is_admin, is_blocked=is_blocked, uid=uid, ) else: raise errors.BadRequestError('Invalid UID')
def translate_cohort_filter_to_menu(cohort_owner_uid): params = request.get_json() domain = get_param(params, 'domain', 'default') if is_unauthorized_domain(domain): raise ForbiddenRequestError( f'You are unauthorized to query the \'{domain}\' domain') if cohort_owner_uid == 'me': cohort_owner_uid = current_user.get_uid() criteria = get_param(params, 'criteria') return tolerant_jsonify( CohortFilterOptions.translate_to_filter_options( cohort_owner_uid, domain, criteria))
def all_cohort_filter_options(cohort_owner_uid): if cohort_owner_uid == 'me': cohort_owner_uid = current_user.get_uid() params = request.get_json() existing_filters = get_param(params, 'existingFilters', []) domain = get_param(params, 'domain', 'default') if is_unauthorized_domain(domain): raise ForbiddenRequestError( f'You are unauthorized to query the \'{domain}\' domain') return tolerant_jsonify( CohortFilterOptions.get_cohort_filter_option_groups( cohort_owner_uid, domain, existing_filters, ), )
def remove_attachment(note_id, attachment_id): existing_note = Note.find_by_id(note_id=note_id) if not existing_note: raise BadRequestError('Note id not found.') if existing_note.author_uid != current_user.get_uid() and not current_user.is_admin: raise ForbiddenRequestError('You are not authorized to remove attachments from this note.') note = Note.delete_attachment( note_id=note_id, attachment_id=int(attachment_id), ) return tolerant_jsonify( _boa_note_to_compatible_json( note=note, note_read=NoteRead.find_or_create(current_user.get_id(), note_id), ), )
def create_cohort(): params = request.get_json() name = get_param(params, 'name', None) filters = get_param(params, 'filters', None) order_by = params.get('orderBy') filter_criteria = _filters_to_filter_criteria(filters, order_by) if not name or not filter_criteria: raise BadRequestError( 'Cohort creation requires \'name\' and \'filters\'') cohort = CohortFilter.create( uid=current_user.get_uid(), name=name, filter_criteria=filter_criteria, order_by=order_by, include_alerts_for_user_id=current_user.get_id(), ) _decorate_cohort(cohort) return tolerant_jsonify(cohort)
def update_note(): params = request.form note_id = params.get('id', None) subject = params.get('subject', None) body = params.get('body', None) topics = get_note_topics_from_http_post() if not note_id or not subject: raise BadRequestError('Note requires \'id\' and \'subject\'') if Note.find_by_id(note_id=note_id).author_uid != current_user.get_uid(): raise ForbiddenRequestError('Sorry, you are not the author of this note.') note = Note.update( note_id=note_id, subject=subject, body=process_input_from_rich_text_editor(body), topics=topics, ) note_read = NoteRead.find_or_create(current_user.get_id(), note_id) return tolerant_jsonify(_boa_note_to_compatible_json(note=note, note_read=note_read))
def add_attachment(note_id): if Note.find_by_id(note_id=note_id).author_uid != current_user.get_uid(): raise ForbiddenRequestError('Sorry, you are not the author of this note.') attachments = _get_attachments(request.files) if len(attachments) != 1: raise BadRequestError('A single attachment file must be supplied.') note = Note.add_attachment( note_id=note_id, attachment=attachments[0], ) note_json = note.to_api_json() return tolerant_jsonify( note_to_compatible_json( note=note_json, note_read=NoteRead.find_or_create(current_user.get_id(), note_id), attachments=note_json.get('attachments'), topics=note_json.get('topics'), ), )
def add_attachments(note_id): note = Note.find_by_id(note_id=note_id) if note.author_uid != current_user.get_uid(): raise ForbiddenRequestError('Sorry, you are not the author of this note.') attachments = get_note_attachments_from_http_post() attachment_limit = app.config['NOTES_ATTACHMENTS_MAX_PER_NOTE'] if len(attachments) + len(note.attachments) > attachment_limit: raise BadRequestError(f'No more than {attachment_limit} attachments may be uploaded at once.') for attachment in attachments: note = Note.add_attachment( note_id=note_id, attachment=attachment, ) return tolerant_jsonify( _boa_note_to_compatible_json( note=note, note_read=NoteRead.find_or_create(current_user.get_id(), note_id), ), )
def update_note(): params = request.form note_id = params.get('id', None) subject = params.get('subject', None) body = params.get('body', None) topics = _get_topics(params) delete_ids_ = params.get('deleteAttachmentIds') or [] delete_ids_ = delete_ids_ if isinstance(delete_ids_, list) else str(delete_ids_).split(',') delete_attachment_ids = [int(id_) for id_ in delete_ids_] if not note_id or not subject: raise BadRequestError('Note requires \'id\' and \'subject\'') if Note.find_by_id(note_id=note_id).author_uid != current_user.get_uid(): raise ForbiddenRequestError('Sorry, you are not the author of this note.') note = Note.update( note_id=note_id, subject=subject, body=process_input_from_rich_text_editor(body), topics=topics, attachments=_get_attachments(request.files, tolerate_none=True), delete_attachment_ids=delete_attachment_ids, ) note_read = NoteRead.find_or_create(current_user.get_id(), note_id) return tolerant_jsonify(_boa_note_to_compatible_json(note=note, note_read=note_read))
def _get_filter_db_type_per_key(): filter_type_per_key = {} for category in get_cohort_filter_options(current_user.get_uid()): for _filter in category: filter_type_per_key[_filter['key']] = _filter['type']['db'] return filter_type_per_key
def _decorate_cohort(cohort): owner_uids = [o['uid'] for o in cohort['owners']] cohort.update( {'isOwnedByCurrentUser': current_user.get_uid() in owner_uids})
def translate_cohort_filter_to_menu(cohort_owner_uid): if cohort_owner_uid == 'me': cohort_owner_uid = current_user.get_uid() criteria = get_param(request.get_json(), 'criteria') return tolerant_jsonify( translate_to_filter_options(cohort_owner_uid, criteria))
def all_cohort_filter_options(cohort_owner_uid): if cohort_owner_uid == 'me': cohort_owner_uid = current_user.get_uid() existing_filters = get_param(request.get_json(), 'existingFilters', []) return tolerant_jsonify( get_cohort_filter_options(cohort_owner_uid, existing_filters))
def _decorate_cohort(cohort): if cohort.get('owner'): cohort.update({ 'isOwnedByCurrentUser': cohort['owner'].get('uid') == current_user.get_uid() })