Esempio n. 1
0
def unschedule():
    params = request.get_json()
    term_id = params.get('termId')
    section_id = params.get('sectionId')
    course = SisSection.get_course(term_id, section_id, include_deleted=True) if (term_id and section_id) else None

    if not course:
        raise BadRequestError('Required params missing or invalid')

    if not (course['scheduled'] or course['hasNecessaryApprovals']):
        raise BadRequestError(f'Section id {section_id}, term id {term_id} is not currently scheduled or queued for scheduling')

    Approval.delete(term_id=term_id, section_id=section_id)
    Scheduled.delete(term_id=term_id, section_id=section_id)

    event_id = (course.get('scheduled') or {}).get('kalturaScheduleId')
    if event_id:
        try:
            Kaltura().delete(event_id)
        except (KalturaClientException, KalturaException) as e:
            message = f'Failed to delete Kaltura schedule: {event_id}'
            app.logger.error(message)
            app.logger.exception(e)
            send_system_error_email(
                message=f'{message}\n\n<pre>{traceback.format_exc()}</pre>',
                subject=message,
            )

    CoursePreference.update_opt_out(
        term_id=term_id,
        section_id=section_id,
        opt_out=True,
    )
    return tolerant_jsonify(SisSection.get_course(term_id, section_id, include_deleted=True))
Esempio n. 2
0
def queue_emails():
    params = request.get_json()
    term_id = params.get('termId')
    section_ids = params.get('sectionIds')
    template_type = params.get('emailTemplateType')

    if term_id and section_ids and template_type:
        section_ids_already_queued = QueuedEmail.get_all_section_ids(
            template_type=template_type, term_id=term_id)
        section_ids_to_queue = [
            id_ for id_ in section_ids if id_ not in section_ids_already_queued
        ]

        for section_id in section_ids_to_queue:
            QueuedEmail.create(section_id=section_id,
                               template_type=template_type,
                               term_id=term_id)

        section_id_count = len(section_ids)
        queued_count = len(section_ids_to_queue)
        if queued_count < section_id_count:
            message = f"""
                {len(section_ids_already_queued)} '{template_type}' emails were already queued up for the
                {'course' if section_id_count == 1 else 'courses'} you submitted.
                Thus, {'no' if queued_count == 0 else f'only {queued_count}'} emails added to the queue.
            """
        else:
            message = f'{queued_count} \'{template_type}\' emails will be sent.'
        return tolerant_jsonify({
            'message': message,
        })
    else:
        raise BadRequestError('Required parameters are missing.')
Esempio n. 3
0
def ping():
    b_connected_ping = None
    canvas_ping = None
    db_ping = None
    kaltura_ping = None
    status = 200
    try:
        b_connected_ping = BConnected().ping()
        canvas_ping = _ping_canvas()
        db_ping = _db_status()
        kaltura_ping = Kaltura().ping()
    except Exception as e:
        status = 500
        subject = str(e)
        subject = f'{subject[:50]}...' if len(subject) > 50 else subject
        message = f'Error during /api/ping: {subject}'
        app.logger.error(message)
        app.logger.exception(e)
        if app.config['EMAIL_IF_PING_HAS_ERROR']:
            send_system_error_email(
                message=f'{message}\n\n<pre>{traceback.format_exc()}</pre>',
                subject=message,
            )
    finally:
        return tolerant_jsonify(
            {
                'app': True,
                'bConnected': b_connected_ping,
                'canvas': canvas_ping,
                'db': db_ping,
                'kaltura': kaltura_ping,
            },
            status=status,
        )
Esempio n. 4
0
def test_email_template(template_id):
    email_template = EmailTemplate.get_template(template_id)
    if email_template:
        course = SisSection.get_course(term_id=app.config['CURRENT_TERM_ID'],
                                       section_id='12597')
        template = EmailTemplate.get_template(template_id)
        subject_line = interpolate_email_content(
            course=course,
            recipient_name=current_user.name,
            templated_string=template.subject_line,
        )
        message = interpolate_email_content(
            course=course,
            recipient_name=current_user.name,
            templated_string=template.message,
        )
        BConnected().send(
            recipients=[
                {
                    'email': current_user.email_address,
                    'name': current_user.name,
                    'uid': current_user.uid,
                },
            ],
            message=message,
            subject_line=subject_line,
        )
        return tolerant_jsonify(
            {'message': f'Email sent to {current_user.email_address}'}), 200
    else:
        raise ResourceNotFoundError('No such email_template')
Esempio n. 5
0
def app_config():
    term_id = app.config['CURRENT_TERM_ID']
    return tolerant_jsonify({
        'canvasBaseUrl':
        app.config['CANVAS_BASE_URL'],
        'courseCaptureExplainedUrl':
        app.config['COURSE_CAPTURE_EXPLAINED_URL'],
        'courseCapturePoliciesUrl':
        app.config['COURSE_CAPTURE_POLICIES_URL'],
        'currentTermId':
        term_id,
        'currentTermName':
        term_name_for_sis_id(term_id),
        'devAuthEnabled':
        app.config['DEVELOPER_AUTH_ENABLED'],
        'diabloEnv':
        app.config['DIABLO_ENV'],
        'ebEnvironment':
        app.config['EB_ENVIRONMENT']
        if 'EB_ENVIRONMENT' in app.config else None,
        'emailTemplateTypes':
        EmailTemplate.get_template_type_options(),
        'publishTypeOptions':
        NAMES_PER_PUBLISH_TYPE,
        'roomCapabilityOptions':
        Room.get_room_capability_options(),
        'searchFilterOptions':
        get_search_filter_options(),
        'searchItemsPerPage':
        app.config['SEARCH_ITEMS_PER_PAGE'],
        'supportEmailAddress':
        app.config['EMAIL_DIABLO_SUPPORT'],
        'timezone':
        app.config['TIMEZONE'],
    })
def test_email_template(template_id):
    email_template = EmailTemplate.get_template(template_id)
    if email_template:
        course = SisSection.get_random_co_taught_course(app.config['CURRENT_TERM_ID'])
        template = EmailTemplate.get_template(template_id)
        publish_types = get_all_publish_types()
        recording_types = get_all_recording_types()

        def _get_interpolated_content(templated_string):
            return interpolate_content(
                course=course,
                pending_instructors=course['instructors'],
                previous_publish_type_name=NAMES_PER_PUBLISH_TYPE[publish_types[0]],
                previous_recording_type_name=NAMES_PER_RECORDING_TYPE[recording_types[0]],
                publish_type_name=NAMES_PER_PUBLISH_TYPE[publish_types[1]],
                recording_type_name=NAMES_PER_RECORDING_TYPE[recording_types[1]],
                recipient_name=current_user.name,
                templated_string=templated_string,
            )
        BConnected().send(
            recipient={
                'email': current_user.email_address,
                'name': current_user.name,
                'uid': current_user.uid,
            },
            message=_get_interpolated_content(template.message),
            subject_line=_get_interpolated_content(template.subject_line),
        )
        return tolerant_jsonify({'message': f'Email sent to {current_user.email_address}'}), 200
    else:
        raise ResourceNotFoundError('No such email_template')
Esempio n. 7
0
def get_course(term_id, section_id):
    course = SisSection.get_course(term_id, section_id)
    if not course:
        raise ResourceNotFoundError(f'No section for term_id = {term_id} and section_id = {section_id}')

    if not current_user.is_admin and current_user.uid not in [i['uid'] for i in course['instructors']]:
        raise ForbiddenRequestError(f'Sorry, you are unauthorized to view the course {course["label"]}.')
    return tolerant_jsonify(course)
Esempio n. 8
0
def available_jobs():
    jobs = []
    for job in _available_jobs():
        jobs.append({
            'key': job['key'],
            'description': job['description'],
        })
    return tolerant_jsonify(jobs)
Esempio n. 9
0
def logout():
    logout_user()
    redirect_url = app.config['VUE_LOCALHOST_BASE_URL'] or request.url_root
    cas_logout_url = _cas_client().get_logout_url(redirect_url=redirect_url)
    return tolerant_jsonify({
        'casLogoutUrl': cas_logout_url,
        **current_user.to_api_json(),
    })
Esempio n. 10
0
def approve():
    term_id = app.config['CURRENT_TERM_ID']
    term_name = term_name_for_sis_id(term_id)

    params = request.get_json()
    publish_type = params.get('publishType')
    recording_type = params.get('recordingType')
    section_id = params.get('sectionId')

    course = SisSection.get_course(term_id, section_id) if section_id else None

    if not course or publish_type not in get_all_publish_types() or recording_type not in get_all_recording_types():
        raise BadRequestError('One or more required params are missing or invalid')

    if not current_user.is_admin and current_user.uid not in [i['uid'] for i in course['instructors']]:
        raise ForbiddenRequestError('Sorry, request unauthorized')

    if Approval.get_approval(approved_by_uid=current_user.uid, section_id=section_id, term_id=term_id):
        raise ForbiddenRequestError(f'You have already approved recording of {course["courseName"]}, {term_name}')

    meetings = course.get('meetings', {}).get('eligible', [])
    if len(meetings) != 1:
        raise BadRequestError('Unique eligible meeting pattern not found for course')
    meeting = meetings[0]

    location = meeting and meeting.get('location')
    room = Room.find_room(location=location)
    if not room:
        raise BadRequestError(f'{location} is not eligible for Course Capture.')

    previous_approvals = Approval.get_approvals_per_section_ids(section_ids=[section_id], term_id=term_id)
    approval = Approval.create(
        approved_by_uid=current_user.uid,
        approver_type_='admin' if current_user.is_admin else 'instructor',
        course_display_name=course['label'],
        publish_type_=publish_type,
        recording_type_=recording_type,
        room_id=room.id,
        section_id=section_id,
        term_id=term_id,
    )

    if previous_approvals:
        # Compare the current approval with preferences submitted in previous approval
        previous_approval = previous_approvals[-1]
        if (approval.publish_type, approval.recording_type) != (previous_approval.publish_type, previous_approval.recording_type):
            notify_instructors_of_changes(course, approval, previous_approvals)

    all_approvals = previous_approvals + [approval]
    if len(course['instructors']) > len(all_approvals):
        approval_uids = [a.approved_by_uid for a in all_approvals]
        pending_instructors = [i for i in course['instructors'] if i['uid'] not in approval_uids]
        last_approver = next((i for i in course['instructors'] if i['uid'] == approval.approved_by_uid), None)
        if last_approver:
            notify_instructor_waiting_for_approval(course, last_approver, pending_instructors)

    return tolerant_jsonify(_after_approval(course=SisSection.get_course(term_id, section_id)))
Esempio n. 11
0
def update_room_capability():
    params = request.get_json()
    room_id = params.get('roomId')
    room = Room.get_room(room_id) if room_id else None
    if not room or 'capability' not in params:
        raise BadRequestError('Missing required parameters')
    capability = params.get('capability')
    room = Room.update_capability(room_id, capability)
    return tolerant_jsonify(room.to_api_json())
Esempio n. 12
0
def admin_users():
    api_json = []
    admin_uids = [admin_user.uid for admin_user in AdminUser.all_admin_users()]
    for admin_user in get_calnet_users_for_uids(app=app, uids=admin_uids).values():
        api_json.append({
            'email': admin_user.get('email'),
            'name': admin_user.get('name'),
            'uid': admin_user['uid'],
        })
    return tolerant_jsonify(api_json)
Esempio n. 13
0
def dev_auth_login():
    if app.config['DEV_AUTH_ENABLED']:
        params = request.get_json() or {}
        uid = params.get('uid')
        password = params.get('password')
        if password != app.config['DEV_AUTH_PASSWORD']:
            return tolerant_jsonify({'message': 'Invalid credentials'}, 401)
        user = User(uid)
        if not user.is_active:
            msg = f'UID {uid} is neither an Admin user nor active in CalNet.'
            app.logger.error(msg)
            return tolerant_jsonify({'message': msg}, 403)
        if not login_user(user, force=True, remember=True):
            msg = f'The system failed to log in user with UID {uid}.'
            app.logger.error(msg)
            return tolerant_jsonify({'message': msg}, 403)
        return tolerant_jsonify(current_user.to_api_json(include_courses=True))
    else:
        raise ResourceNotFoundError('Unknown path')
Esempio n. 14
0
def start_job(job_key):
    job_class = next((job
                      for job in BackgroundJobManager.available_job_classes()
                      if job.key() == job_key), None)
    if job_class:
        app.logger.info(
            f'Current user ({current_user.uid}) started job {job_class.key()}')
        job_class(app.app_context).run(force_run=True)
        return tolerant_jsonify(_job_class_to_json(job_class))
    else:
        raise ResourceNotFoundError(f'Invalid job_key: {job_key}')
Esempio n. 15
0
def start_job(job_key):
    job = next((job for job in _available_jobs() if job['key'] == job_key),
               None)
    if job:
        job['class'](app.app_context).run_with_app_context()
        return tolerant_jsonify({
            'key': job['key'],
            'description': job['description'],
        })
    else:
        raise ResourceNotFoundError(f'Invalid job_key: {job_key}')
Esempio n. 16
0
def update_opt_out():
    params = request.get_json()
    term_id = params.get('termId')
    section_id = params.get('sectionId')
    opt_out = params.get('optOut')
    preferences = CoursePreference.update_opt_out(
        term_id=term_id,
        section_id=section_id,
        opt_out=opt_out,
    )
    return tolerant_jsonify(preferences.to_api_json())
Esempio n. 17
0
def get_room(room_id):
    room = Room.get_room(room_id)
    if room:
        api_json = room.to_api_json()
        api_json['courses'] = SisSection.get_courses_per_location(
            term_id=app.config['CURRENT_TERM_ID'],
            location=room.location,
        )
        return tolerant_jsonify(api_json)
    else:
        raise ResourceNotFoundError('No such room')
Esempio n. 18
0
def get_user(uid):
    user = get_calnet_user_for_uid(app=app, uid=uid)
    if user.get('isExpiredPerLdap', True):
        raise ResourceNotFoundError('No such user')
    else:
        courses = SisSection.get_courses_per_instructor_uid(
            term_id=app.config['CURRENT_TERM_ID'],
            instructor_uid=uid,
        )
        user['courses'] = courses
        return tolerant_jsonify(user)
Esempio n. 19
0
def app_version():
    v = {
        'version': version,
    }
    build_stats = load_json('config/build-summary.json')
    if build_stats:
        v.update(build_stats)
    else:
        v.update({
            'build': None,
        })
    return tolerant_jsonify(v)
Esempio n. 20
0
def get_course(term_id, section_id):
    course = SisSection.get_course(term_id, section_id, include_deleted=True)
    if not course:
        raise ResourceNotFoundError(f'No section for term_id = {term_id} and section_id = {section_id}')
    if not current_user.is_admin and current_user.uid not in [i['uid'] for i in course['instructors']]:
        raise ForbiddenRequestError(f'Sorry, you are unauthorized to view the course {course["label"]}.')

    if current_user.is_admin and course['scheduled']:
        # When debugging, the raw Kaltura-provided JSON is useful.
        event_id = course['scheduled'].get('kalturaScheduleId')
        course['scheduled']['kalturaSchedule'] = Kaltura().get_event(event_id)
    return tolerant_jsonify(course)
Esempio n. 21
0
def job_disable():
    params = request.get_json()
    job_id = params.get('jobId')
    disable = params.get('disable')

    if not job_id or disable is None:
        raise BadRequestError('Required parameters are missing.')
    job = Job.update_disabled(job_id=job_id, disable=disable)

    background_job_manager.restart()

    return tolerant_jsonify(job.to_api_json())
Esempio n. 22
0
def auditorium():
    params = request.get_json()
    room_id = params.get('roomId')
    room = Room.get_room(room_id) if room_id else None
    if room:
        is_auditorium = params.get('isAuditorium')
        if not room_id or is_auditorium is None:
            raise BadRequestError("'roomId' and 'isAuditorium' are required.")
        room = Room.set_auditorium(room_id, is_auditorium)
        return tolerant_jsonify(room.to_api_json())
    else:
        raise ResourceNotFoundError('No such room')
Esempio n. 23
0
def job_history(day_count):
    def _raise_error():
        raise BadRequestError(f'Invalid day_count: {day_count}')

    try:
        days = int(day_count)
        if days < 1:
            _raise_error()
        return tolerant_jsonify([
            h.to_api_json()
            for h in JobHistory.get_job_history_in_past_days(day_count=days)
        ])
    except ValueError:
        _raise_error()
Esempio n. 24
0
def queue_emails():
    params = request.get_json()
    term_id = params.get('termId')
    section_id = params.get('sectionId')
    template_type = params.get('emailTemplateType')

    if not (term_id and section_id and template_type):
        raise BadRequestError('Required parameters are missing.')
    course = SisSection.get_course(term_id=term_id, section_id=section_id)
    for instructor in course['instructors']:
        if not QueuedEmail.create(section_id=section_id, recipient=instructor, template_type=template_type, term_id=term_id):
            raise BadRequestError(f"Failed to queue email of type '{template_type}'.")
    return tolerant_jsonify({
        'message': f"An email of type '{template_type}' has been queued.",
    })
def create_blackout():
    params = request.get_json()
    name = params.get('name')
    start_date = params.get('startDate')
    end_date = params.get('endDate')
    if None in [name, start_date, end_date]:
        raise BadRequestError('Required parameters are missing.')

    start_date = _local_blackout_date_to_utc(f'{start_date}T00:00:00')
    end_date = _local_blackout_date_to_utc(f'{end_date}T23:59:59')
    _validate_date_range(start_date, end_date)

    blackout = Blackout.create(name=name,
                               start_date=start_date,
                               end_date=end_date)
    return tolerant_jsonify(blackout.to_api_json())
Esempio n. 26
0
def app_config():
    def _to_api_key(key):
        chunks = key.split('_')
        return f"{chunks[0].lower()}{''.join(chunk.title() for chunk in chunks[1:])}"
    return tolerant_jsonify(
        {
            **dict((_to_api_key(key), app.config[key]) for key in PUBLIC_CONFIGS),
            **{
                'currentTermName': term_name_for_sis_id(app.config['CURRENT_TERM_ID']),
                'ebEnvironment': get_eb_environment(),
                'emailTemplateTypes': EmailTemplate.get_template_type_options(),
                'publishTypeOptions': NAMES_PER_PUBLISH_TYPE,
                'roomCapabilityOptions': Room.get_room_capability_options(),
                'searchFilterOptions': get_search_filter_options(),
            },
        },
    )
Esempio n. 27
0
def create():
    params = request.get_json()
    template_type = params.get('templateType')
    name = params.get('name')
    subject_line = params.get('subjectLine')
    message = params.get('message')

    if None in [template_type, name, subject_line, message]:
        raise BadRequestError('Required parameters are missing.')

    email_template = EmailTemplate.create(
        template_type=template_type,
        name=name,
        subject_line=subject_line,
        message=message,
    )
    return tolerant_jsonify(email_template.to_api_json())
Esempio n. 28
0
def update_opt_out():
    params = request.get_json()
    term_id = params.get('termId')
    section_id = params.get('sectionId')

    course = SisSection.get_course(term_id, section_id) if (term_id and section_id) else None
    opt_out = params.get('optOut')
    if not course or opt_out is None:
        raise BadRequestError('Required params missing or invalid')
    if course['scheduled']:
        raise BadRequestError('Cannot update opt-out on scheduled course')

    preferences = CoursePreference.update_opt_out(
        term_id=term_id,
        section_id=section_id,
        opt_out=opt_out,
    )
    return tolerant_jsonify(preferences.to_api_json())
Esempio n. 29
0
def job_schedule():
    api_json = {
        'autoStart': app.config['JOBS_AUTO_START'],
        'jobs': [],
        'secondsBetweenJobsCheck':
        app.config['JOBS_SECONDS_BETWEEN_PENDING_CHECK'],
        'startedAt': to_isoformat(background_job_manager.get_started_at()),
    }
    for job in Job.get_all(include_disabled=True):
        job_class = next(
            (j for j in BackgroundJobManager.available_job_classes()
             if j.key() == job.key), None)
        if job_class:
            api_json['jobs'].append({
                **job.to_api_json(),
                **_job_class_to_json(job_class),
            })
    return tolerant_jsonify(api_json)
Esempio n. 30
0
def update_schedule():
    params = request.get_json()
    job_id = params.get('jobId')
    schedule_type = params.get('type')
    schedule_value = params.get('value')

    if not job_id or not schedule_type or not schedule_value:
        raise BadRequestError('Required parameters are missing.')
    job = Job.get_job(job_id=job_id)
    if not job.disabled or JobHistory.is_job_running(job_key=job.key):
        raise BadRequestError(
            'You cannot edit job schedule if job is either enabled or running.'
        )
    job = Job.update_schedule(job_id=job_id,
                              schedule_type=schedule_type,
                              schedule_value=schedule_value)

    background_job_manager.restart()

    return tolerant_jsonify(job.to_api_json())