コード例 #1
0
ファイル: learning_records.py プロジェクト: ubc/compair
    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}
コード例 #2
0
ファイル: emit_learning_record.py プロジェクト: ubc/compair
def emit_lrs_caliper_event(self, caliper_log_id):
    from compair.learning_records import CaliperSensor

    caliper_log = CaliperLog.query \
        .filter_by(
            id=caliper_log_id,
            transmitted=False
        ) \
        .one_or_none()

    if caliper_log:
        try:
            CaliperSensor._emit_to_lrs(json.loads(caliper_log.event))
        except socket.error as error:
            # don't raise connection refused error when in eager mode
            if error.errno != socket.errno.ECONNREFUSED:
                current_app.logger.error(
                    "emit_lrs_caliper_event connection refused: " +
                    socket.error.strerror)
                return
            raise error

        CaliperLog.query \
            .filter_by(id=caliper_log_id) \
            .delete()
        db.session.commit()
コード例 #3
0
ファイル: emit_learning_record.py プロジェクト: ubc/compair
def resend_learning_records(self):
    from compair.learning_records import XAPI, CaliperSensor

    # only re-send learning records that have last started over an hour ago
    # (this is to try and prevent sending duplicates if possible)
    one_hour_ago = datetime.datetime.utcnow() - datetime.timedelta(hours=1)

    if XAPI.enabled() and not XAPI.storing_locally():
        xapi_logs = XAPILog.query \
            .filter(and_(
                XAPILog.transmitted == False,
                XAPILog.modified <= one_hour_ago
            )) \
            .all()

        for xapi_log in xapi_logs:
            emit_lrs_xapi_statement(xapi_log.id)

    if CaliperSensor.enabled() and not CaliperSensor.storing_locally():
        caliper_logs = CaliperLog.query \
            .filter(and_(
                CaliperLog.transmitted == False,
                CaliperLog.modified <= one_hour_ago
            )) \
            .all()

        for caliper_log in caliper_logs:
            emit_lrs_caliper_event(caliper_log.id)
コード例 #4
0
def resend_learning_records(self):
    from compair.learning_records import XAPI, CaliperSensor

    # only re-send learning records that have last started over an hour ago
    # (this is to try and prevent sending duplicates if possible)
    one_hour_ago = datetime.datetime.utcnow() - datetime.timedelta(hours=1)

    if XAPI.enabled() and not XAPI.storing_locally():
        xapi_logs = XAPILog.query \
            .filter(and_(
                XAPILog.transmitted == False,
                XAPILog.modified <= one_hour_ago
            )) \
            .all()

        for xapi_log in xapi_logs:
            emit_lrs_xapi_statement.delay(xapi_log.id)

    if CaliperSensor.enabled() and not CaliperSensor.storing_locally():
        caliper_logs = CaliperLog.query \
            .filter(and_(
                CaliperLog.transmitted == False,
                CaliperLog.modified <= one_hour_ago
            )) \
            .all()

        for caliper_log in caliper_logs:
            emit_lrs_caliper_event.delay(caliper_log.id)
コード例 #5
0
ファイル: learning_records.py プロジェクト: ubc/acj-versus
    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 }
コード例 #6
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}
コード例 #7
0
def emit_lrs_caliper_event(self, caliper_log_id):
    from compair.learning_records import CaliperSensor

    caliper_log = CaliperLog.query.filter_by(id=caliper_log_id).one_or_none()

    if caliper_log:
        try:
            CaliperSensor._emit_to_lrs(json.loads(caliper_log.event))
        except socket.error as error:
            # don't raise connection refused error when in eager mode
            if error.errno != socket.errno.ECONNREFUSED:
                return
            raise error

        caliper_log.transmitted = True
        db.session.commit()
コード例 #8
0
ファイル: test_remote.py プロジェクト: vishnu-meera/ACJ
    def test_send_remote_caliper_event(self, mocked_send_event):
        self.app.config['XAPI_ENABLED'] = False

        def send_event_override(event):
            self.sent_caliper_event = json.loads(event.as_json())
            return {}

        mocked_send_event.side_effect = send_event_override

        expected_assignment = {
            'name':
            self.assignment.name,
            'type':
            'Assessment',
            'dateCreated':
            self.assignment.created.replace(tzinfo=pytz.utc).isoformat(),
            'dateModified':
            self.assignment.modified.replace(tzinfo=pytz.utc).isoformat(),
            'dateToStartOn':
            self.assignment.answer_start.replace(tzinfo=pytz.utc).isoformat(),
            'description':
            self.assignment.description,
            'id':
            "https://localhost:8888/app/course/" + self.course.uuid +
            "/assignment/" + self.assignment.uuid,
            'isPartOf': {
                'academicSession':
                self.course.term,
                'dateCreated':
                self.course.created.replace(tzinfo=pytz.utc).isoformat(),
                'dateModified':
                self.course.modified.replace(tzinfo=pytz.utc).isoformat(),
                'id':
                "https://localhost:8888/app/course/" + self.course.uuid,
                'name':
                self.course.name,
                'type':
                'CourseOffering',
                'extensions': {
                    'ltiContexts': [{
                        'context_id':
                        self.lti_context.context_id,
                        'oauth_consumer_key':
                        self.lti_data.lti_consumer.oauth_consumer_key,
                        'lis_course_offering_sourcedid':
                        "sis_course_id",
                        'lis_course_section_sourcedid':
                        "sis_section_id",
                    }]
                }
            },
            'items': [{
                'id':
                "https://localhost:8888/app/course/" + self.course.uuid +
                "/assignment/" + self.assignment.uuid + "/question",
                'type':
                'AssessmentItem'
            }, {
                'id':
                "https://localhost:8888/app/course/" + self.course.uuid +
                "/assignment/" + self.assignment.uuid +
                "/comparison/question/1",
                'type':
                'AssessmentItem'
            }, {
                'id':
                "https://localhost:8888/app/course/" + self.course.uuid +
                "/assignment/" + self.assignment.uuid +
                "/evaluation/question/1",
                'type':
                'AssessmentItem'
            }, {
                'id':
                "https://localhost:8888/app/course/" + self.course.uuid +
                "/assignment/" + self.assignment.uuid +
                "/evaluation/question/2",
                'type':
                'AssessmentItem'
            }, {
                'id':
                "https://localhost:8888/app/course/" + self.course.uuid +
                "/assignment/" + self.assignment.uuid +
                "/comparison/question/2",
                'type':
                'AssessmentItem'
            }, {
                'id':
                "https://localhost:8888/app/course/" + self.course.uuid +
                "/assignment/" + self.assignment.uuid +
                "/evaluation/question/3",
                'type':
                'AssessmentItem'
            }, {
                'id':
                "https://localhost:8888/app/course/" + self.course.uuid +
                "/assignment/" + self.assignment.uuid +
                "/evaluation/question/4",
                'type':
                'AssessmentItem'
            }, {
                'id':
                "https://localhost:8888/app/course/" + self.course.uuid +
                "/assignment/" + self.assignment.uuid +
                "/comparison/question/3",
                'type':
                'AssessmentItem'
            }, {
                'id':
                "https://localhost:8888/app/course/" + self.course.uuid +
                "/assignment/" + self.assignment.uuid +
                "/evaluation/question/5",
                'type':
                'AssessmentItem'
            }, {
                'id':
                "https://localhost:8888/app/course/" + self.course.uuid +
                "/assignment/" + self.assignment.uuid +
                "/evaluation/question/6",
                'type':
                'AssessmentItem'
            }],
        }

        expected_assignment_question = {
            'name':
            self.assignment.name,
            'type':
            'AssessmentItem',
            'dateCreated':
            self.assignment.created.replace(tzinfo=pytz.utc).isoformat(),
            'dateModified':
            self.assignment.modified.replace(tzinfo=pytz.utc).isoformat(),
            'dateToStartOn':
            self.assignment.answer_start.replace(tzinfo=pytz.utc).isoformat(),
            'dateToSubmit':
            self.assignment.answer_end.replace(tzinfo=pytz.utc).isoformat(),
            'description':
            self.assignment.description,
            'id':
            "https://localhost:8888/app/course/" + self.course.uuid +
            "/assignment/" + self.assignment.uuid + "/question",
            'isPartOf':
            expected_assignment,
        }

        expected_attempt = {
            'assignable':
            expected_assignment_question,
            'assignee':
            self.get_compair_caliper_actor(self.user),
            'id':
            "https://localhost:8888/app/course/" + self.course.uuid +
            "/assignment/" + self.assignment.uuid + "/question/attempt/" +
            self.answer.attempt_uuid,
            'duration':
            "PT05M00S",
            'startedAtTime':
            self.answer.attempt_started.replace(tzinfo=pytz.utc).isoformat(),
            'endedAtTime':
            self.answer.attempt_ended.replace(tzinfo=pytz.utc).isoformat(),
            'type':
            'Attempt'
        }

        expected_answer = {
            'attempt':
            expected_attempt,
            'id':
            "https://localhost:8888/app/course/" + self.course.uuid +
            "/assignment/" + self.assignment.uuid + "/answer/" +
            self.answer.uuid,
            'type':
            'Response',
            'dateCreated':
            self.answer.created.replace(tzinfo=pytz.utc).isoformat(),
            'dateModified':
            self.answer.modified.replace(tzinfo=pytz.utc).isoformat(),
            'extensions': {
                'characterCount': len(self.answer.content),
                'content': self.answer.content,
                'isDraft': False,
                'wordCount': len(self.answer.content.split(" ")),
                'scoreDetails': {
                    'algorithm': self.assignment.scoring_algorithm.value,
                    'loses': 0,
                    'opponents': 0,
                    'rounds': 0,
                    'score': 5,
                    'wins': 0,
                    'criteria': {
                        "https://localhost:8888/app/criterion/" + self.criterion.uuid:
                        {
                            'loses': 0,
                            'opponents': 0,
                            'rounds': 0,
                            'score': 5,
                            'wins': 0
                        },
                    }
                },
            }
        }

        expected_answer_comment = {
            'commenter':
            self.get_compair_caliper_actor(self.user),
            'commented':
            expected_answer,
            'value':
            self.answer_comment.content,
            'id':
            "https://localhost:8888/app/course/" + self.course.uuid +
            "/assignment/" + self.assignment.uuid + "/answer/" +
            self.answer.uuid + "/comment/" + self.answer_comment.uuid,
            'type':
            'Comment',
            'dateCreated':
            self.answer_comment.created.replace(tzinfo=pytz.utc).isoformat(),
            'dateModified':
            self.answer_comment.modified.replace(tzinfo=pytz.utc).isoformat(),
            'extensions': {
                'characterCount': len(self.answer_comment.content),
                'isDraft': False,
                'type': 'Public',
                'wordCount': len(self.answer_comment.content.split(" ")),
            },
        }

        expected_event = {
            'action':
            'Completed',
            'actor':
            self.get_compair_caliper_actor(self.user),
            'membership':
            self.get_caliper_membership(self.course, self.user,
                                        self.lti_context),
            'generated':
            expected_answer,
            'object':
            expected_assignment_question,
            'session':
            self.get_caliper_session(self.get_compair_caliper_actor(
                self.user)),
            'type':
            'AssessmentItemEvent'
        }

        # test with answer normal content
        event = caliper.events.AssessmentItemEvent(
            action=caliper.constants.
            ASSESSMENT_ITEM_EVENT_ACTIONS["COMPLETED"],
            object=CaliperEntities.assignment_question(self.answer.assignment),
            generated=CaliperEntities.answer(self.answer),
            **CaliperEvent._defaults(self.user, self.course))

        CaliperSensor._emit_to_lrs(json.loads(event.as_json()))
        self._validate_and_cleanup_caliper_event(self.sent_caliper_event)
        self.assertEqual(self.sent_caliper_event, expected_event)

        # test with extremely long answer content

        # content should be ~ LRS_USER_INPUT_FIELD_SIZE_LIMIT bytes long + 100 characters
        content = "c" * (self.character_limit + 100)

        self.answer.content = content
        db.session.commit()

        # expected_answer content should be <= LRS_USER_INPUT_FIELD_SIZE_LIMIT bytes long + " [TEXT TRIMMED]..."
        expected_answer['extensions']['content'] = (
            "c" * self.character_limit) + " [TEXT TRIMMED]..."
        expected_answer['extensions']['wordCount'] = 1
        expected_answer['extensions']['characterCount'] = len(content)
        expected_answer['dateModified'] = self.answer.modified.replace(
            tzinfo=pytz.utc).isoformat()

        event = caliper.events.AssessmentItemEvent(
            action=caliper.constants.
            ASSESSMENT_ITEM_EVENT_ACTIONS["COMPLETED"],
            object=CaliperEntities.assignment_question(self.answer.assignment),
            generated=CaliperEntities.answer(self.answer),
            **CaliperEvent._defaults(self.user, self.course))

        CaliperSensor._emit_to_lrs(json.loads(event.as_json()))
        self._validate_and_cleanup_caliper_event(self.sent_caliper_event)
        self.assertEqual(self.sent_caliper_event, expected_event)

        # test with answer comment normal content
        event = caliper.events.Event(
            action=caliper.constants.BASIC_EVENT_ACTIONS["MODIFIED"],
            object=CaliperEntities.answer_comment(self.answer_comment),
            **CaliperEvent._defaults(self.user, self.course))

        CaliperSensor._emit_to_lrs(json.loads(event.as_json()))

        self._validate_and_cleanup_caliper_event(self.sent_caliper_event)

        expected_event = {
            'action':
            'Modified',
            'actor':
            self.get_compair_caliper_actor(self.user),
            'membership':
            self.get_caliper_membership(self.course, self.user,
                                        self.lti_context),
            'object':
            expected_answer_comment,
            'session':
            self.get_caliper_session(self.get_compair_caliper_actor(
                self.user)),
            'type':
            'Event'
        }

        self.assertEqual(self.sent_caliper_event, expected_event)

        # test with extremely long answer comment content

        # content should be ~ LRS_USER_INPUT_FIELD_SIZE_LIMIT bytes long + 100 characters
        content = "d" * (self.character_limit + 100)

        self.answer_comment.content = content
        db.session.commit()

        # expected_assignment name and description should be <= LRS_USER_INPUT_FIELD_SIZE_LIMIT bytes long + " [TEXT TRIMMED]..."
        expected_answer_comment['value'] = (
            "d" * self.character_limit) + " [TEXT TRIMMED]..."
        expected_answer_comment['extensions']['wordCount'] = 1
        expected_answer_comment['extensions']['characterCount'] = len(content)
        expected_answer_comment[
            'dateModified'] = self.answer_comment.modified.replace(
                tzinfo=pytz.utc).isoformat()

        event = caliper.events.Event(
            action=caliper.constants.BASIC_EVENT_ACTIONS["MODIFIED"],
            object=CaliperEntities.answer_comment(self.answer_comment),
            **CaliperEvent._defaults(self.user, self.course))

        CaliperSensor._emit_to_lrs(json.loads(event.as_json()))
        self._validate_and_cleanup_caliper_event(self.sent_caliper_event)
        self.assertEqual(self.sent_caliper_event, expected_event)

        # test with assignment normal content
        event = caliper.events.Event(
            action=caliper.constants.BASIC_EVENT_ACTIONS["MODIFIED"],
            object=CaliperEntities.assignment(self.assignment),
            **CaliperEvent._defaults(self.user, self.course))

        CaliperSensor._emit_to_lrs(json.loads(event.as_json()))

        self._validate_and_cleanup_caliper_event(self.sent_caliper_event)

        expected_event = {
            'action':
            'Modified',
            'actor':
            self.get_compair_caliper_actor(self.user),
            'membership':
            self.get_caliper_membership(self.course, self.user,
                                        self.lti_context),
            'object':
            expected_assignment,
            'session':
            self.get_caliper_session(self.get_compair_caliper_actor(
                self.user)),
            'type':
            'Event'
        }

        self.assertEqual(self.sent_caliper_event, expected_event)

        # test with extremely long assignment content

        # content should be ~ LRS_USER_INPUT_FIELD_SIZE_LIMIT bytes long + 100 characters
        name = "a" * (self.character_limit + 100)
        description = "b" * (self.character_limit + 100)

        self.assignment.name = name
        self.assignment.description = description
        db.session.commit()

        # expected_assignment name and description should be <= LRS_USER_INPUT_FIELD_SIZE_LIMIT bytes long + " [TEXT TRIMMED]..."
        expected_assignment['name'] = (
            "a" * self.character_limit) + " [TEXT TRIMMED]..."
        expected_assignment['description'] = (
            "b" * self.character_limit) + " [TEXT TRIMMED]..."
        expected_assignment['dateModified'] = self.assignment.modified.replace(
            tzinfo=pytz.utc).isoformat()

        event = caliper.events.Event(
            action=caliper.constants.BASIC_EVENT_ACTIONS["MODIFIED"],
            object=CaliperEntities.assignment(self.assignment),
            **CaliperEvent._defaults(self.user, self.course))

        CaliperSensor._emit_to_lrs(json.loads(event.as_json()))

        self._validate_and_cleanup_caliper_event(self.sent_caliper_event)

        self.assertEqual(self.sent_caliper_event, expected_event)