Exemple #1
0
    def complete_upload_for_token(cls, upload_token_id):
        kaltura_user_id = KalturaCore.user_id()
        if current_app.config.get('KALTURA_USE_GLOBAL_UNIQUE_IDENTIFIER', False) and current_user and current_user.global_unique_identifier:
            kaltura_user_id = current_user.global_unique_identifier

        kaltura_media = KalturaMedia.query \
            .filter_by(
                upload_token_id=upload_token_id,
                user_id=current_user.id,
                entry_id=None
            ) \
            .first()

        if not kaltura_media:
            abort(400, title="File Not Uploaded",
                message="The upload token does not exist or is already used. Please contact support for assistance.")

        with KalturaSession.generate_api_session(kaltura_user_id) as ks:
            # fetch upload, and update kaltura_media filename
            upload_token = UploadToken.get_upload_token(ks, upload_token_id)
            kaltura_media.file_name = upload_token.get('fileName')

            # create the media entry and associate it with the completed upload
            entry = Media.generate_media_entry(ks, upload_token_id, kaltura_media.media_type)
            kaltura_media.entry_id = entry.get('id')
            kaltura_media.download_url = entry.get('downloadUrl')

        # delete the upload session
        KalturaSession._api_end(kaltura_media.upload_ks)
        db.session.commit()

        return kaltura_media
Exemple #2
0
def check_valid_pairing_algorithm(pairing_algorithm):
    pairing_algorithms = [
        PairingAlgorithm.adaptive.value,
        PairingAlgorithm.random.value
    ]
    if pairing_algorithm not in pairing_algorithms:
        abort(400, title="Assignment Not Saved", message="'"+pairing_algorithm+"' is not a valid answer pairing algorithm. Please select one of the pairing algorithm options listed.")
Exemple #3
0
    def post(self, user_uuid):
        user = User.get_by_uuid_or_404(user_uuid)
        # anyone who passes checking below should be an instructor or admin
        require(
            EDIT,
            user,
            title="Notifications Not Updated",
            message=
            "Sorry, your system role does not allow you to update notification settings for this user."
        )

        if not user.email:
            abort(
                400,
                title="Notifications Not Updated",
                message=
                "Sorry, you cannot update notification settings since this user does not have an email address in ComPAIR."
            )

        params = update_notification_settings_parser.parse_args()

        email_notification_method = params.get("email_notification_method")
        check_valid_email_notification_method(email_notification_method)
        user.email_notification_method = EmailNotificationMethod(
            email_notification_method)

        db.session.commit()
        on_user_notifications_update.send(
            self,
            event_name=on_user_notifications_update.name,
            user=current_user)
        return marshal(user,
                       dataformat.get_user(is_user_access_restricted(user)))
Exemple #4
0
    def delete(self, course_uuid, assignment_uuid, answer_uuid):
        course = Course.get_active_by_uuid_or_404(course_uuid)
        assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid)
        answer = Answer.get_active_by_uuid_or_404(answer_uuid)
        require(DELETE, answer,
            title="Answer Not Deleted",
            message="Sorry, your role in this course does not allow you to delete this answer.")

        if current_app.config.get('DEMO_INSTALLATION', False):
            from data.fixtures import DemoDataFixture
            if assignment.id in DemoDataFixture.DEFAULT_ASSIGNMENT_IDS and answer.user_id in DemoDataFixture.DEFAULT_STUDENT_IDS:
                abort(400, title="Answer Not Deleted", message="Sorry, you cannot delete the default student demo answers.")

        answer.active = False
        db.session.commit()

        # update course & assignment grade for user if answer was fully submitted
        if not answer.draft:
            if answer.user:
                assignment.calculate_grade(answer.user)
                course.calculate_grade(answer.user)
            elif answer.group:
                assignment.calculate_group_grade(answer.group)
                course.calculate_group_grade(answer.group)

        on_answer_delete.send(
            self,
            event_name=on_answer_delete.name,
            user=current_user,
            course_id=course.id,
            answer=answer,
            data={'assignment_id': assignment.id, 'answer_id': answer.id})

        return {'id': answer.uuid}
Exemple #5
0
    def post(self):
        params = new_consumer_parser.parse_args()

        consumer = LTIConsumer()
        require(CREATE, consumer,
            title="Consumer Not Saved",
            message="Sorry, your system role does not allow you to save LTI consumers.")

        consumer.oauth_consumer_key = params.get("oauth_consumer_key")
        consumer.oauth_consumer_secret = params.get("oauth_consumer_secret")
        consumer.global_unique_identifier_param = params.get("global_unique_identifier_param")
        consumer.student_number_param = params.get("student_number_param")

        try:
            db.session.add(consumer)
            db.session.commit()
            on_consumer_create.send(
                self,
                event_name=on_consumer_create.name,
                user=current_user,
                consumer=consumer,
                data={'consumer': marshal(consumer, dataformat.get_lti_consumer())}
            )
        except exc.IntegrityError:
            db.session.rollback()
            abort(409, title="Consumer Not Saved", message="An LTI consumer with the same consumer key already exists. Please double-check the consumer key and try saving again.")

        return marshal(consumer, dataformat.get_lti_consumer(include_sensitive=True))
Exemple #6
0
    def post(self, consumer_uuid):
        consumer = LTIConsumer.get_by_uuid_or_404(consumer_uuid)
        require(EDIT, consumer,
            title="Consumer Not Saved",
            message="Sorry, your system role does not allow you to save LTI consumers.")

        params = existing_consumer_parser.parse_args()

        # make sure the course id in the url and the course id in the params match
        if params['id'] != consumer_uuid:
            abort(400, title="Consumer Not Saved", message="The LTI consumer ID does not match the URL, which is required in order to save the LTI consumer.")

        consumer.oauth_consumer_key = params.get("oauth_consumer_key")
        consumer.oauth_consumer_secret = params.get("oauth_consumer_secret")
        consumer.global_unique_identifier_param = params.get("global_unique_identifier_param")
        consumer.student_number_param = params.get("student_number_param")
        consumer.active = params.get("active")

        try:
            db.session.commit()
            on_consumer_update.send(
                self,
                event_name=on_consumer_update.name,
                user=current_user,
                consumer=consumer
            )
        except exc.IntegrityError:
            db.session.rollback()
            abort(409, title="Consumer Not Saved", message="An LTI consumer with the same consumer key already exists. Please double-check the consumer key and try saving again.")

        return marshal(consumer, dataformat.get_lti_consumer(include_sensitive=True))
Exemple #7
0
    def complete_upload_for_token(cls, upload_token_id):
        kaltura_media = KalturaMedia.query \
            .filter_by(
                upload_token_id=upload_token_id,
                user_id=current_user.id,
                entry_id=None
            ) \
            .first()

        if not kaltura_media:
            abort(
                400,
                title="File Not Uploaded",
                message=
                "The upload token does not exist or is already used. Please contact support for assistance."
            )

        with KalturaSession.generate_api_session() as ks:
            # fetch upload, and update kaltura_media filename
            upload_token = UploadToken.get_upload_token(ks, upload_token_id)
            kaltura_media.file_name = upload_token.get('fileName')

            # create the media entry and associate it with the completed upload
            entry = Media.generate_media_entry(ks, upload_token_id,
                                               kaltura_media.media_type)
            kaltura_media.entry_id = entry.get('id')
            kaltura_media.download_url = entry.get('downloadUrl')

        # delete the upload session
        KalturaSession._api_end(kaltura_media.upload_ks)
        db.session.commit()

        return kaltura_media
Exemple #8
0
    def delete(self, course_uuid, assignment_uuid):
        course = Course.get_active_by_uuid_or_404(course_uuid)
        assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid)
        require(DELETE, assignment,
            title="Assignment Not Deleted",
            message="Sorry, your role in this course does not allow you to delete assignments.")

        if current_app.config.get('DEMO_INSTALLATION', False):
            from data.fixtures import DemoDataFixture
            if assignment.id in DemoDataFixture.DEFAULT_ASSIGNMENT_IDS:
                abort(400, title="Assignment Not Deleted", message="Sorry, you cannot remove the default demo assignments.")

        formatted_assignment = marshal(assignment, dataformat.get_assignment(False))
        # delete file when assignment is deleted
        assignment.active = False
        assignment.clear_lti_links()
        db.session.commit()

        # update course grades
        course.calculate_grades()

        on_assignment_delete.send(
            self,
            event_name=on_assignment_delete.name,
            user=current_user,
            course_id=course.id,
            assignment=assignment,
            data=formatted_assignment)

        return {'id': assignment.uuid}
Exemple #9
0
    def post(self):
        if not CaliperSensor.enabled():
            # this should silently fail
            abort(404)

        raw_params = request.get_json(force=True)
        params = {}

        course_uuid = raw_params.get('course_id')
        course = _get_valid_course(course_uuid)

        # add required params
        for param in ['type', 'action', 'object']:
            if not raw_params.get(param):
                abort(400)
            params[param] = raw_params.get(param)

        # add optional params
        for param in [
                'eventTime', 'target', 'generated', 'referrer', 'extensions',
                'profile'
        ]:
            if raw_params.get(param):
                params[param] = raw_params.get(param)

        event = CaliperEvent.generate_from_params(current_user,
                                                  params,
                                                  course=course)
        CaliperSensor.emit(event)

        return {'success': True}
Exemple #10
0
    def delete(self, course_uuid):
        course = Course.get_active_by_uuid_or_404(course_uuid)
        require(
            DELETE,
            course,
            title="Course Not Deleted",
            message=
            "Sorry, your role in this course does not allow you to delete it.")

        if current_app.config.get('DEMO_INSTALLATION', False):
            from data.fixtures import DemoDataFixture
            if course.id == DemoDataFixture.DEFAULT_COURSE_ID:
                abort(
                    400,
                    title="Course Not Deleted",
                    message="Sorry, you cannot remove the default demo course."
                )

        course.active = False
        course.clear_lti_links()
        db.session.commit()

        on_course_delete.send(self,
                              event_name=on_course_delete.name,
                              user=current_user,
                              course=course,
                              data={'id': course.id})

        return {'id': course.uuid}
Exemple #11
0
    def post(self, consumer_uuid):
        consumer = LTIConsumer.get_by_uuid_or_404(consumer_uuid)
        require(EDIT, consumer,
            title="Consumer Not Saved",
            message="Sorry, your system role does not allow you to save LTI consumers.")

        params = existing_consumer_parser.parse_args()

        # make sure the course id in the url and the course id in the params match
        if params['id'] != consumer_uuid:
            abort(400, title="Consumer Not Saved", message="The LTI consumer ID does not match the URL, which is required in order to save the LTI consumer.")

        consumer.oauth_consumer_key = params.get("oauth_consumer_key")
        consumer.oauth_consumer_secret = params.get("oauth_consumer_secret")
        consumer.global_unique_identifier_param = params.get("global_unique_identifier_param")
        consumer.student_number_param = params.get("student_number_param")
        consumer.custom_param_regex_sanitizer = params.get("custom_param_regex_sanitizer")
        consumer.active = params.get("active")

        try:
            db.session.commit()
            on_consumer_update.send(
                self,
                event_name=on_consumer_update.name,
                user=current_user,
                consumer=consumer
            )
        except exc.IntegrityError:
            db.session.rollback()
            abort(409, title="Consumer Not Saved", message="An LTI consumer with the same consumer key already exists. Please double-check the consumer key and try saving again.")

        return marshal(consumer, dataformat.get_lti_consumer(include_sensitive=True))
Exemple #12
0
    def post(self, course_uuid):
        """
        link current session's lti context with a course
        """
        course = Course.get_active_by_uuid_or_404(course_uuid)
        require(EDIT, course,
            title="Course Not Linked",
            message="Sorry, you do not have permission to link this course since you are not enrolled as an instructor in the course.")

        if not sess.get('LTI'):
            abort(400, title="Course Not Linked",
                message="Sorry, your LTI session has expired. Please log in via LTI and try linking again.")

        if not sess.get('lti_context'):
            abort(400, title="Course Not Linked",
                message="Sorry, your LTI link settings have no course context. Please edit your LTI link settings and try linking again.")

        lti_context = LTIContext.query.get_or_404(sess.get('lti_context'))
        lti_context.compair_course_id = course.id
        db.session.commit()

        # automatically fetch membership if enabled for context
        if lti_context.membership_enabled:
            update_lti_course_membership.delay(course.id)

        on_lti_course_link_create.send(
            self,
            event_name=on_lti_course_link_create.name,
            user=current_user,
            data={ 'course_id': course.id, 'lti_context_id': lti_context.id })

        return { 'success': True }
Exemple #13
0
    def _api_start(cls, privileges=None):
        url = KalturaCore.base_url() + "/service/session/action/start"
        params = {
            'partnerId': KalturaCore.partner_id(),
            'userId': KalturaCore.user_id(),
            'secret': KalturaCore.secret(),
            'type': KalturaCore.SESSION_TYPE_USER,  # safer to go with user
            'format': 1,  # json return value
        }
        if privileges:
            params['privileges'] = privileges
        result = requests.get(url,
                              params=params,
                              verify=KalturaCore.enforce_ssl())

        if result.status_code == 200:
            return result.json()
        else:
            current_app.logger.error(result)
            abort(
                400,
                title="File Not Uploaded",
                message=
                "There was a problem with the Kaltura server. Please try again later."
            )
Exemple #14
0
    def post(self, user_uuid):
        user = User.get_by_uuid_or_404(user_uuid)
        # anyone who passes checking below should be an admin or current user
        if not allow(MANAGE, user) and not user.id == current_user.id and not \
                (allow(EDIT, user) and current_app.config.get('EXPOSE_EMAIL_TO_INSTRUCTOR', False)):
            abort(
                403,
                title="Notifications Not Updated",
                message=
                "Sorry, your system role does not allow you to update notification settings for this user."
            )

        if not user.email:
            abort(
                400,
                title="Notifications Not Updated",
                message=
                "Sorry, you cannot update notification settings since this user does not have an email address in ComPAIR."
            )

        params = update_notification_settings_parser.parse_args()

        email_notification_method = params.get("email_notification_method")
        check_valid_email_notification_method(email_notification_method)
        user.email_notification_method = EmailNotificationMethod(
            email_notification_method)

        db.session.commit()
        on_user_notifications_update.send(
            self,
            event_name=on_user_notifications_update.name,
            user=current_user)

        return marshal_user_data(user)
Exemple #15
0
    def post(self):
        params = new_consumer_parser.parse_args()

        consumer = LTIConsumer()
        require(CREATE, consumer,
            title="Consumer Not Saved",
            message="Sorry, your system role does not allow you to save LTI consumers.")

        consumer.oauth_consumer_key = params.get("oauth_consumer_key")
        consumer.oauth_consumer_secret = params.get("oauth_consumer_secret")
        consumer.global_unique_identifier_param = params.get("global_unique_identifier_param")
        consumer.student_number_param = params.get("student_number_param")
        consumer.custom_param_regex_sanitizer = params.get("custom_param_regex_sanitizer")

        try:
            db.session.add(consumer)
            db.session.commit()
            on_consumer_create.send(
                self,
                event_name=on_consumer_create.name,
                user=current_user,
                consumer=consumer,
                data={'consumer': marshal(consumer, dataformat.get_lti_consumer())}
            )
        except exc.IntegrityError:
            db.session.rollback()
            abort(409, title="Consumer Not Saved", message="An LTI consumer with the same consumer key already exists. Please double-check the consumer key and try saving again.")

        return marshal(consumer, dataformat.get_lti_consumer(include_sensitive=True))
Exemple #16
0
    def post(self):
        if not XAPI.enabled():
            # this should silently fail
            abort(404)

        raw_params = request.get_json(force=True)
        params = {}

        course_uuid = raw_params.get('course_id')
        course = _get_valid_course(course_uuid)

        # add required params
        for param in ['verb', 'object']:
            if not raw_params.get(param):
                abort(400)
            params[param] = raw_params.get(param)

        # add optional params
        for param in ['context', 'result', 'timestamp']:
            if raw_params.get(param):
                params[param] = raw_params.get(param)

        statement = XAPIStatement.generate_from_params(current_user,
                                                       params,
                                                       course=course)
        XAPI.emit(statement)

        return {'success': True}
Exemple #17
0
    def delete(self, course_uuid, assignment_uuid):
        course = Course.get_active_by_uuid_or_404(course_uuid)
        assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid)
        require(DELETE, assignment,
            title="Assignment Not Deleted",
            message="Sorry, your role in this course does not allow you to delete assignments.")

        if current_app.config.get('DEMO_INSTALLATION', False):
            from data.fixtures import DemoDataFixture
            if assignment.id in DemoDataFixture.DEFAULT_ASSIGNMENT_IDS:
                abort(400, title="Assignment Not Deleted", message="Sorry, you cannot remove the default demo assignments.")

        formatted_assignment = marshal(assignment, dataformat.get_assignment(False))
        # delete file when assignment is deleted
        assignment.active = False
        assignment.clear_lti_links()
        db.session.commit()

        # update course grades
        course.calculate_grades()

        on_assignment_delete.send(
            self,
            event_name=on_assignment_delete.name,
            user=current_user,
            course_id=course.id,
            assignment=assignment,
            data=formatted_assignment)

        return {'id': assignment.uuid}
Exemple #18
0
def check_valid_email_notification_method(email_notification_method):
    email_notification_methods = [
        EmailNotificationMethod.enable.value,
        EmailNotificationMethod.disable.value
    ]
    if email_notification_method not in email_notification_methods:
        abort(400, title="User Not Saved", message="Please try again with an email notification checked or unchecked.")
Exemple #19
0
def check_valid_email_notification_method(email_notification_method):
    email_notification_methods = [
        EmailNotificationMethod.enable.value,
        EmailNotificationMethod.disable.value
    ]
    if email_notification_method not in email_notification_methods:
        abort(400, title="User Not Saved", message="Please try again with an email notification checked or unchecked.")
Exemple #20
0
    def post(self, upload_token_id):
        if not KalturaAPI.enabled():
            abort(400, title="File Not Uploaded",
                message="Please use a valid upload method to attach files. You are not able to upload with this method based on the current settings.")

        kaltura_media = KalturaAPI.complete_upload_for_token(upload_token_id)

        on_save_kaltura_file.send(
            self,
            event_name=on_save_kaltura_file.name,
            user=current_user,
            data={'upload_token_id': upload_token_id})

        try:
            db_file = File(
                user_id=current_user.id,
                name='',
                alias=kaltura_media.file_name,
                kaltura_media=kaltura_media
            )
            db.session.add(db_file)
            db.session.commit()

            # use uuid generated by file model for name
            name = db_file.uuid + '.' + kaltura_media.extension

            # update file record with name
            db_file.name = name
            db.session.commit()
        except Exception as e:
            db.session.rollback()
            raise e

        return {'file': marshal(db_file, dataformat.get_file())}
Exemple #21
0
    def delete(self):
        if not current_app.config.get(
                'IMPERSONATION_ENABLED',
                False) or not impersonation.is_impersonating():
            abort(404,
                  title="Cannot stop impersonation",
                  message="Sorry, you can't perform that action")

        original_user_id = impersonation.get_impersonation_original_user_id()
        impersonate_as_user_id = impersonation.get_impersonation_act_as_user_id(
        )
        original_user = User.query.get(original_user_id)
        impersonate_as_user = User.query.get(impersonate_as_user_id)
        if original_user is None or impersonate_as_user is None:
            abort(404, title="Cannot stop impersonation", \
                message="Cannot find corresponding information to stop impersonation")

        on_impersonation_stopped.send(
            self,
            event_name=on_impersonation_stopped.name,
            user=original_user,
            data={'impersonate_as': impersonate_as_user.id})

        impersonation.end_impersonation()
        return {'success': True}
Exemple #22
0
    def get_direct_access_url(cls, entry_id, download_url, expiry=300):
        """
            Generate a new Kaltura Session (KS) and return an URL for direct download of Kaltura media file.
            :param entry_id: the Kaltura entry ID of the file to download
            :param download_url: URL to download the file
            :param expiry: optional expiry time of the KS, in seconds. Default is 300 seconds.
            :return: a URL with KS attached for downloading the given Kaltura media file
        """
        kaltura_user_id = KalturaCore.user_id()
        if current_app.config.get(
                'KALTURA_USE_GLOBAL_UNIQUE_IDENTIFIER', False
        ) and current_user and current_user.global_unique_identifier:
            kaltura_user_id = current_user.global_unique_identifier

        url = download_url
        if not url:
            abort(
                404,
                title="File Not Found",
                message=
                "The file is not available on Kaltura server yet. Please try again later."
            )

        session = KalturaSession.generate_direct_media_access_ks(
            kaltura_user_id, entry_id, url, expiry)
        url = url + "/ks/" + quote_plus(session, '')

        return url
Exemple #23
0
    def delete(self, course_uuid, lti_context_uuid):
        """
        unlink lti context from course
        """
        course = Course.get_active_by_uuid_or_404(course_uuid)
        lti_context = LTIContext.get_by_uuid_or_404(lti_context_uuid)
        require(DELETE, lti_context,
            title="Course Not Unlinked",
            message="Sorry, your system role does not allow you to unlink LTI courses.")

        if lti_context.compair_course_id != course.id:
            abort(400, title="Course Not Unlinked", message="Sorry, The LTI context is already not linked to the course.")

        lti_context.compair_course_id = None
        db.session.commit()

        # automatically refresh membership if it was enabled for the removed context
        if lti_context.membership_enabled:
            update_lti_course_membership.delay(course.id)

        on_lti_course_unlink.send(
            self,
            event_name=on_lti_course_unlink.name,
            user=current_user,
            data={ 'course_id': course.id, 'lti_context_id': lti_context.id })

        return { 'success': True }
Exemple #24
0
def check_valid_system_role(system_role):
    system_roles = [
        SystemRole.sys_admin.value,
        SystemRole.instructor.value,
        SystemRole.student.value
    ]
    if system_role not in system_roles:
        abort(400, title="User Not Saved", message="Please try again with a system role from the list of roles provided.")
Exemple #25
0
def check_valid_system_role(system_role):
    system_roles = [
        SystemRole.sys_admin.value,
        SystemRole.instructor.value,
        SystemRole.student.value
    ]
    if system_role not in system_roles:
        abort(400, title="Demo Account Not Saved", message="Please try again with a system role from the list of roles provided.")
Exemple #26
0
def check_valid_pairing_algorithm(pairing_algorithm):
    pairing_algorithms = [
        PairingAlgorithm.adaptive.value,
        PairingAlgorithm.random.value,
        PairingAlgorithm.adaptive_min_delta.value
    ]
    if pairing_algorithm not in pairing_algorithms:
        abort(400, title="Assignment Not Saved", message="'"+pairing_algorithm+"' is not a valid answer pairing algorithm. Please select one of the pairing algorithm options listed.")
Exemple #27
0
    def post(self):
        uploaded_file = request.files.get('file')

        if not uploaded_file:
            abort(
                400,
                title="File Not Uploaded",
                message=
                "Sorry, no file was found to upload. Please try uploading again."
            )
        elif not allowed_file(
                uploaded_file.filename,
                current_app.config['ATTACHMENT_ALLOWED_EXTENSIONS']):
            extensions = [
                extension.upper() for extension in list(
                    current_app.config['ATTACHMENT_ALLOWED_EXTENSIONS'])
            ]
            extensions.sort()
            extensions = ", ".join(extensions)
            abort(
                400,
                title="File Not Uploaded",
                message=
                "Please try again with an approved file type, which includes: "
                + extensions + ".")

        on_save_file.send(self,
                          event_name=on_save_file.name,
                          user=current_user,
                          data={'file': uploaded_file.filename})

        try:
            db_file = File(user_id=current_user.id,
                           name='',
                           alias=uploaded_file.filename)
            db.session.add(db_file)
            db.session.commit()

            # use uuid generated by file model for name
            name = db_file.uuid + '.' + uploaded_file.filename.lower().rsplit(
                '.', 1)[1]

            # create new file with name
            full_path = os.path.join(
                current_app.config['ATTACHMENT_UPLOAD_FOLDER'], name)
            uploaded_file.save(full_path)
            current_app.logger.debug("Saved attachment {}/{}".format(
                current_app.config['ATTACHMENT_UPLOAD_FOLDER'], name))

            # update file record with name
            db_file.name = name
            db.session.commit()
        except Exception as e:
            db.session.rollback()
            raise e

        return {'file': marshal(db_file, dataformat.get_file())}
Exemple #28
0
    def post(self):
        """
        Create new course
        """
        require(
            CREATE,
            Course,
            title="Course Not Saved",
            message=
            "Sorry, your role in the system does not allow you to save courses."
        )
        params = new_course_parser.parse_args()

        new_course = Course(name=params.get("name"),
                            year=params.get("year"),
                            term=params.get("term"),
                            sandbox=params.get("sandbox"),
                            start_date=params.get('start_date'),
                            end_date=params.get('end_date', None))
        if new_course.start_date is not None:
            new_course.start_date = datetime.datetime.strptime(
                new_course.start_date, '%Y-%m-%dT%H:%M:%S.%fZ')

        if new_course.end_date is not None:
            new_course.end_date = datetime.datetime.strptime(
                new_course.end_date, '%Y-%m-%dT%H:%M:%S.%fZ')

        if new_course.start_date and new_course.end_date and new_course.start_date > new_course.end_date:
            abort(400,
                  title="Course Not Saved",
                  message="Course end time must be after course start time.")

        try:
            # create the course
            db.session.add(new_course)
            # also need to enrol the user as an instructor
            new_user_course = UserCourse(course=new_course,
                                         user_id=current_user.id,
                                         course_role=CourseRole.instructor)
            db.session.add(new_user_course)

            db.session.commit()

        except exc.SQLAlchemyError as e:
            db.session.rollback()
            current_app.logger.error("Failed to add new course. " + str(e))
            raise

        on_course_create.send(self,
                              event_name=on_course_create.name,
                              user=current_user,
                              course=new_course,
                              data=marshal(new_course,
                                           dataformat.get_course()))

        return marshal(new_course, dataformat.get_course())
Exemple #29
0
    def post(self):
        """
        Create new course
        """
        require(CREATE, Course,
            title="Course Not Saved",
            message="Sorry, your role in the system does not allow you to save courses.")
        params = new_course_parser.parse_args()

        new_course = Course(
            name=params.get("name"),
            year=params.get("year"),
            term=params.get("term"),
            sandbox=params.get("sandbox"),
            start_date=params.get('start_date'),
            end_date=params.get('end_date', None)
        )
        if new_course.start_date is not None:
            new_course.start_date = datetime.datetime.strptime(
                new_course.start_date,
                '%Y-%m-%dT%H:%M:%S.%fZ')

        if new_course.end_date is not None:
            new_course.end_date = datetime.datetime.strptime(
                new_course.end_date,
                '%Y-%m-%dT%H:%M:%S.%fZ')

        if new_course.start_date and new_course.end_date and new_course.start_date > new_course.end_date:
            abort(400, title="Course Not Saved", message="Course end time must be after course start time.")

        try:
            # create the course
            db.session.add(new_course)
            # also need to enrol the user as an instructor
            new_user_course = UserCourse(
                course=new_course,
                user_id=current_user.id,
                course_role=CourseRole.instructor
            )
            db.session.add(new_user_course)

            db.session.commit()

        except exc.SQLAlchemyError as e:
            db.session.rollback()
            current_app.logger.error("Failed to add new course. " + str(e))
            raise

        on_course_create.send(
            self,
            event_name=on_course_create.name,
            user=current_user,
            course=new_course,
            data=marshal(new_course, dataformat.get_course()))

        return marshal(new_course, dataformat.get_course())
Exemple #30
0
def cas_logout():
    if not current_app.config.get('CAS_LOGIN_ENABLED'):
        abort(
            403,
            title="Log Out Failed",
            message=
            "Please try an alternate way of logging out. The CWL logout has been disabled by your system administrator."
        )

    return redirect(get_cas_logout_url())
Exemple #31
0
def saml_logout():
    if not current_app.config.get('SAML_LOGIN_ENABLED'):
        abort(
            403,
            title="Not Logged Out",
            message=
            "Please use a valid way to log out. You are not able to use CWL logout based on the current settings."
        )

    return redirect(get_saml_logout_url(request))
Exemple #32
0
    def get_by_uuid_or_404(cls, model_uuid, joinedloads=[], title=None, message=None):
        query = cls.query
        # load relationships if needed
        for load_string in joinedloads:
            query.options(joinedload(load_string))

        model = query.filter_by(uuid=model_uuid).one_or_none()
        if model is None:
            abort(404, title=title, message=message)
        return model
Exemple #33
0
    def post(self, course_uuid, assignment_uuid):
        course = Course.get_active_by_uuid_or_404(course_uuid)
        assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid)
        require(
            CREATE,
            ComparisonExample(assignment=Assignment(course_id=course.id)),
            title="Comparison Example Not Saved",
            message=
            "Sorry, your role in this course does not allow you to save practice answers."
        )

        new_comparison_example = ComparisonExample(assignment_id=assignment.id)

        params = new_comparison_example_parser.parse_args()
        answer1_uuid = params.get("answer1_id")
        answer2_uuid = params.get("answer2_id")

        if answer1_uuid:
            answer1 = Answer.get_active_by_uuid_or_404(answer1_uuid)
            answer1.practice = True
            new_comparison_example.answer1 = answer1
        else:
            abort(
                400,
                title="Comparison Example Not Saved",
                message=
                "Please add two answers with content to the practice answers and try again."
            )

        if answer2_uuid:
            answer2 = Answer.get_active_by_uuid_or_404(answer2_uuid)
            answer2.practice = True
            new_comparison_example.answer2 = answer2
        else:
            abort(
                400,
                title="Comparison Example Not Saved",
                message=
                "Please add two answers with content to the practice answers and try again."
            )

        on_comparison_example_create.send(
            self,
            event_name=on_comparison_example_create.name,
            user=current_user,
            course_id=course.id,
            data=marshal(
                new_comparison_example,
                dataformat.get_comparison_example(with_answers=False)))

        db.session.add(new_comparison_example)
        db.session.commit()

        return marshal(new_comparison_example,
                       dataformat.get_comparison_example())
Exemple #34
0
    def post(self):
        if not XAPI.enabled():
            # this should silently fail
            abort(404)

        params = statement_parser.parse_args()

        statement = XAPIStatement.generate_from_params(current_user, params)
        XAPI.send_statement(statement)

        return {'success': True}
Exemple #35
0
    def post(self):
        if not XAPI.enabled():
            # this should silently fail
            abort(404)

        params = statement_parser.parse_args()

        statement = XAPIStatement.generate_from_params(current_user, params)
        XAPI.send_statement(statement)

        return { 'success': True }
Exemple #36
0
    def post(self, course_uuid, assignment_uuid, comparison_example_uuid):
        course = Course.get_active_by_uuid_or_404(course_uuid)
        assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid)
        comparison_example = ComparisonExample.get_active_by_uuid_or_404(
            comparison_example_uuid)
        require(
            EDIT,
            comparison_example,
            title="Comparison Example Not Saved",
            message=
            "Sorry, your role in this course does not allow you to save practice answers."
        )

        params = existing_comparison_example_parser.parse_args()
        answer1_uuid = params.get("answer1_id")
        answer2_uuid = params.get("answer2_id")

        if answer1_uuid:
            answer1 = Answer.get_active_by_uuid_or_404(answer1_uuid)
            answer1.practice = True
            comparison_example.answer1 = answer1
        else:
            abort(
                400,
                title="Comparison Example Not Saved",
                message=
                "Please add two answers with content to the practice answers and try again."
            )

        if answer2_uuid:
            answer2 = Answer.get_active_by_uuid_or_404(answer2_uuid)
            answer2.practice = True
            comparison_example.answer2 = answer2
        else:
            abort(
                400,
                title="Comparison Example Not Saved",
                message=
                "Please add two answers with content to the practice answers and try again."
            )

        model_changes = get_model_changes(comparison_example)
        db.session.add(comparison_example)
        db.session.commit()

        on_comparison_example_modified.send(
            self,
            event_name=on_comparison_example_modified.name,
            user=current_user,
            course_id=course.id,
            data=model_changes)

        return marshal(comparison_example, dataformat.get_comparison_example())
Exemple #37
0
    def post(self):
        if not CaliperSensor.enabled():
            # this should silently fail
            abort(404)

        params = caliper_event_parser.parse_args()
        course_uuid = params.pop('course_id')
        course = _get_valid_course(course_uuid)

        event = CaliperEvent.generate_from_params(current_user, params, course=course)
        CaliperSensor.emit(event)

        return { 'success': True }
Exemple #38
0
    def post(self):
        if not XAPI.enabled():
            # this should silently fail
            abort(404)

        params = xapi_statement_parser.parse_args()
        course_uuid = params.pop('course_id')
        course = _get_valid_course(course_uuid)

        statement = XAPIStatement.generate_from_params(current_user, params, course=course)
        XAPI.emit(statement)

        return { 'success': True }
Exemple #39
0
    def post(self, user_uuid):
        user = User.get_by_uuid_or_404(user_uuid)
        # anyone who passes checking below should be an instructor or admin
        require(EDIT, user,
            title="Password Not Saved",
            message="Sorry, your system role does not allow you to update passwords for this user.")

        if not user.uses_compair_login:
            abort(400, title="Password Not Saved",
                message="Sorry, you cannot update the password since this user does not use the ComPAIR account login method.")

        params = update_password_parser.parse_args()
        oldpassword = params.get('oldpassword')

        if current_user.id == user.id and not oldpassword:
            abort(400, title="Password Not Saved", message="Sorry, the old password is required. Please enter the old password and try saving again.")
        elif current_user.id == user.id and not user.verify_password(oldpassword):
            abort(400, title="Password Not Saved", message="Sorry, the old password is not correct. Please double-check the old password and try saving again.")
        elif len(params.get('newpassword')) < 4:
            abort(400, title="Password Not Saved", message="The new password must be at least 4 characters long.")

        user.password = params.get('newpassword')
        db.session.commit()
        on_user_password_update.send(
            self,
            event_name=on_user_password_update.name,
            user=current_user)

        return marshal_user_data(user)
Exemple #40
0
    def get_active_by_uuid_or_404(cls,
                                  model_uuid,
                                  joinedloads=[],
                                  title=None,
                                  message=None):
        query = cls.query
        # load relationships if needed
        for load_string in joinedloads:
            query.options(joinedload(load_string))

        model = query.filter_by(uuid=model_uuid).one_or_none()
        if model is None or not model.active:
            abort(404, title=title, message=message)
        return model
Exemple #41
0
    def post(self, course_uuid):
        """
        refresh the course membership if able
        """
        course = Course.get_active_by_uuid_or_404(course_uuid)
        require(EDIT, course,
            title="Membership Not Updated",
            message="Sorry, your role in this course does not allow you to update membership.")

        if not course.lti_linked:
            abort(400, title="Membership Not Updated",
                message="Sorry, your LTI link settings have no course context. Please edit your LTI link settings and try linking again.")

        try:
            LTIMembership.update_membership_for_course(course)
        except MembershipNoValidContextsException as err:
            abort(400, title="Membership Not Updated",
                message="The LTI link does not support the membership extension. Please edit your LTI link settings or contact your system administrator and try again.")
        except MembershipNoResultsException as err:
            abort(400, title="Membership Not Updated",
                message="The membership service did not return any users. Please check your LTI course and try again.")
        except MembershipInvalidRequestException as err:
            abort(400, title="Membership Not Updated",
                message="The membership request was invalid. Please relaunch the LTI link and try again.")

        on_lti_course_membership_update.send(
            self,
            event_name=on_lti_course_membership_update.name,
            user=current_user,
            data={ 'course_id': course.id })

        return { 'imported': True }
Exemple #42
0
    def get(self):
        if not KalturaAPI.enabled():
            abort(400, title="File Not Uploaded",
                message="Please use a valid upload method to attach files. You are not able to upload with this method based on the current settings.")

        upload_url = KalturaAPI.generate_new_upload_token()

        on_get_kaltura_token.send(
            self,
            event_name=on_get_kaltura_token.name,
            user=current_user,
            data={'upload_url': upload_url})

        return {'upload_url': upload_url}
Exemple #43
0
    def post(self, course_uuid):
        course = Course.get_active_by_uuid_or_404(course_uuid)
        require(
            EDIT,
            UserCourse(course_id=course.id),
            title="Group Not Saved",
            message=
            "Sorry, your role in this course does not allow you to save groups."
        )

        params = user_list_parser.parse_args()

        if len(params.get('ids')) == 0:
            abort(
                400,
                title="Group Not Saved",
                message=
                "Please select at least one user below and then try to apply the group again."
            )

        user_courses = UserCourse.query \
            .join(User, UserCourse.user_id == User.id) \
            .filter(and_(
                UserCourse.course_id == course.id,
                User.uuid.in_(params.get('ids')),
                UserCourse.course_role != CourseRole.dropped
            )) \
            .all()

        if len(params.get('ids')) != len(user_courses):
            abort(
                400,
                title="Group Not Saved",
                message=
                "One or more users selected are not enrolled in the course yet."
            )

        for user_course in user_courses:
            user_course.group_name = None

        db.session.commit()

        on_course_group_user_list_delete.send(
            current_app._get_current_object(),
            event_name=on_course_group_user_list_delete.name,
            user=current_user,
            course_id=course.id,
            data={'user_uuids': params.get('ids')})

        return {'course_id': course.uuid}
Exemple #44
0
    def _api_get(cls, ks, entry_id):
        url = KalturaCore.base_url()+"/service/media/action/get"
        params = {
            'entryId': entry_id,
            'ks': ks,
            'format': 1, # json return value
        }
        result = requests.get(url, params=params, verify=KalturaCore.enforce_ssl())

        if result.status_code == 200:
            return result.json()
        else:
            current_app.logger.error(result)
            abort(400, title="File Not Accessible",
                message="There was a problem with the Kaltura server. Please try again later.")
Exemple #45
0
    def get(self):
        if not current_app.config.get('IMPERSONATION_ENABLED', False) or not impersonation.is_impersonating():
            return {'impersonating' : False}

        original_user = impersonation.get_impersonation_original_user()
        impersonate_as_user = current_user
        if original_user is None or impersonate_as_user is None:
            # something went wrong...
            abort(503, title="Cannot check impersonation status", \
                message="Sorry, cannot find information on impersonation status")

        # user is impersonating. treat as restrict_user when calling dataformat
        return { \
            'impersonating' : True,
            'original_user' : marshal(original_user, dataformat.get_user(True))
        }
Exemple #46
0
    def _api_update(cls, ks, entry_id, update_params={}):
        url = KalturaCore.base_url()+"/service/media/action/update"
        params = {
            'entryId': entry_id,
            'ks': ks,
            'format': 1, # json return value
            'mediaEntry[objectType]': "KalturaMediaEntry"
        }
        params.update(update_params)
        result = requests.get(url, params=params, verify=KalturaCore.enforce_ssl())

        if result.status_code == 200:
            return result.json()
        else:
            current_app.logger.error(result)
            abort(400, title="File Not Updated",
                message="There was a problem with the Kaltura server. Please try again later.")
Exemple #47
0
    def _api_add_content(cls, ks, entry_id, upload_token_id):
        url = KalturaCore.base_url()+"/service/media/action/addContent"
        params = {
            'entryId': entry_id,
            'resource[objectType]': 'KalturaUploadedFileTokenResource',
            'resource[token]': upload_token_id,
            'ks': ks,
            'format': 1, # json return value
        }
        result = requests.get(url, params=params, verify=KalturaCore.enforce_ssl())

        if result.status_code == 200:
            return result.json()
        else:
            current_app.logger.error(result)
            abort(400, title="File Not Uploaded",
                message="There was a problem with the Kaltura server. Please try again later.")
Exemple #48
0
    def get(self, course_uuid):
        """
        refresh the course membership if able
        """
        course = Course.get_active_by_uuid_or_404(course_uuid)
        require(EDIT, course,
            title="Membership Status Unavailable",
            message="Sorry, your role in this course does not allow you to view LTI membership status.")

        if not course.lti_linked:
            abort(400, title="Membership Status Unavailable",
                message="The course is not linked to an LTI context yet. Launch an LTI link to link this course first, then check the status.")

        valid_membership_contexts = [
            lti_context for lti_context in course.lti_contexts \
            if lti_context.membership_enabled
        ]

        pending = 0
        enabled = len(valid_membership_contexts) > 0
        if enabled:
            lti_context_ids = [lti_context.id for lti_context in valid_membership_contexts]

            pending = LTIMembership.query \
                .join(LTIUser) \
                .filter(and_(
                    LTIUser.compair_user_id == None,
                    LTIMembership.lti_context_id.in_(lti_context_ids)
                )) \
                .count()

        status = {
            'enabled': enabled,
            'pending': pending
        }

        on_lti_course_membership_status_get.send(
            self,
            event_name=on_lti_course_membership_status_get.name,
            user=current_user,
            data={ 'course_id': course.id })

        return { 'status': status }
Exemple #49
0
    def post(self):
        uploaded_file = request.files.get('file')

        if not uploaded_file:
            abort(400, title="File Not Uploaded", message="Sorry, no file was found to upload. Please try uploading again.")
        elif not allowed_file(uploaded_file.filename, current_app.config['ATTACHMENT_ALLOWED_EXTENSIONS']):
            extensions = [extension.upper() for extension in list(current_app.config['ATTACHMENT_ALLOWED_EXTENSIONS'])]
            extensions.sort()
            extensions = ", ".join(extensions)
            abort(400, title="File Not Uploaded", message="Please try again with an approved file type, which includes: "+extensions+".")

        on_save_file.send(
            self,
            event_name=on_save_file.name,
            user=current_user,
            data={'file': uploaded_file.filename})

        try:
            db_file = File(
                user_id=current_user.id,
                name='',
                alias=uploaded_file.filename
            )
            db.session.add(db_file)
            db.session.commit()

            # use uuid generated by file model for name
            name = db_file.uuid + '.' + uploaded_file.filename.lower().rsplit('.', 1)[1]

            # create new file with name
            full_path = os.path.join(current_app.config['ATTACHMENT_UPLOAD_FOLDER'], name)
            uploaded_file.save(full_path)
            current_app.logger.debug("Saved attachment {}/{}".format(current_app.config['ATTACHMENT_UPLOAD_FOLDER'], name))

            # update file record with name
            db_file.name = name
            db.session.commit()
        except Exception as e:
            db.session.rollback()
            raise e

        return {'file': marshal(db_file, dataformat.get_file())}
Exemple #50
0
    def get(self, course_uuid, assignment_uuid):
        course = Course.get_active_by_uuid_or_404(course_uuid)
        assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid)
        require(READ, assignment,
            title="Assignment Unavailable",
            message="Assignments can be saved only by those enrolled in the course. Please double-check your enrollment in this course.")

        now = datetime.datetime.utcnow()
        if assignment.answer_start and not allow(MANAGE, assignment) and not (assignment.answer_start <= now):
            abort(403, title="Assignment Unavailable", message="This assignment is not yet open to students. Please check back after the start date the instructor has set.")
        restrict_user = not allow(MANAGE, assignment)

        on_assignment_get.send(
            self,
            event_name=on_assignment_get.name,
            user=current_user,
            course_id=course.id,
            data={'id': assignment.id})

        return marshal(assignment, dataformat.get_assignment(restrict_user))
Exemple #51
0
    def delete(self):
        if not current_app.config.get('IMPERSONATION_ENABLED', False) or not impersonation.is_impersonating():
            abort(404, title="Cannot stop impersonation", message="Sorry, you can't perform that action")

        original_user_id = impersonation.get_impersonation_original_user_id()
        impersonate_as_user_id = impersonation.get_impersonation_act_as_user_id()
        original_user = User.query.get(original_user_id)
        impersonate_as_user = User.query.get(impersonate_as_user_id)
        if original_user is None or impersonate_as_user is None:
            abort(404, title="Cannot stop impersonation", \
                message="Cannot find corresponding information to stop impersonation")

        on_impersonation_stopped.send(
            self,
            event_name=on_impersonation_stopped.name,
            user=original_user,
            data={ 'impersonate_as': impersonate_as_user.id })

        impersonation.end_impersonation()
        return {'success': True}
Exemple #52
0
    def delete(self, course_uuid, group_uuid):
        course = Course.get_active_by_uuid_or_404(course_uuid)
        group = Group.get_active_by_uuid_or_404(group_uuid)

        require(DELETE, group,
            title="Group Not Deleted",
            message="Sorry, your role in this course does not allow you to delete groups.")

        # check if group has submitted any answers
        group_answer_exists = Answer.query \
            .filter_by(
                group_id=group.id,
                practice=False,
                active=True,
                draft=False
            ) \
            .first()

        if group_answer_exists:
            abort(400, title="Group Not Deleted",
                message="Sorry, you cannot remove groups that have submitted answers.")

        group.active = False

        # remove members from the group
        user_courses = UserCourse.query \
            .filter_by(group_id=group.id) \
            .all()
        for user_course in user_courses:
            user_course.group_id = None

        db.session.commit()

        on_group_delete.send(
            current_app._get_current_object(),
            event_name=on_group_delete.name,
            user=current_user,
            course_id=course.id
        )

        return {'id': group.uuid }
Exemple #53
0
    def _api_start(cls, kaltura_user_id, privileges=None, expiry=None):
        url = KalturaCore.base_url()+"/service/session/action/start"
        params = {
            'partnerId': KalturaCore.partner_id(),
            'userId': kaltura_user_id,
            'secret': KalturaCore.secret(),
            'type': KalturaCore.SESSION_TYPE_USER, # safer to go with user
            'format': 1, # json return value
        }
        if privileges:
            params['privileges'] = privileges
        if expiry:
            params['expiry'] = expiry
        result = requests.get(url, params=params, verify=KalturaCore.enforce_ssl())

        if result.status_code == 200:
            return result.json()
        else:
            current_app.logger.error(result)
            abort(400, title="File Not Uploaded",
                message="There was a problem with the Kaltura server. Please try again later.")
Exemple #54
0
    def get_direct_access_url(cls, entry_id, download_url, expiry=300):
        """
            Generate a new Kaltura Session (KS) and return an URL for direct download of Kaltura media file.
            :param entry_id: the Kaltura entry ID of the file to download
            :param download_url: URL to download the file
            :param expiry: optional expiry time of the KS, in seconds. Default is 300 seconds.
            :return: a URL with KS attached for downloading the given Kaltura media file
        """
        kaltura_user_id = KalturaCore.user_id()
        if current_app.config.get('KALTURA_USE_GLOBAL_UNIQUE_IDENTIFIER', False) and current_user and current_user.global_unique_identifier:
            kaltura_user_id = current_user.global_unique_identifier

        url = download_url
        if not url:
            abort(404, title="File Not Found",
                message="The file is not available on Kaltura server yet. Please try again later.")

        session = KalturaSession.generate_direct_media_access_ks(kaltura_user_id, entry_id, url, expiry)
        url = url + "/ks/" + quote_plus(session, '')

        return url