def create_task(self, store_anonymous_username=None):  # pylint: disable=arguments-differ
     """Allow arguments to be passed to the task constructor."""
     self.task = ModuleEngagementDataTask(
         date=luigi.DateParameter().parse(self.DEFAULT_DATE),
         store_anonymous_username=store_anonymous_username,
         output_root='/fake/output',
     )
     self.task.init_local()
 def create_task(self, date=None):  # pylint: disable=arguments-differ
     """Allow arguments to be passed to the task constructor."""
     if not date:
         date = self.DEFAULT_DATE
     self.task = ModuleEngagementDataTask(
         date=luigi.DateParameter().parse(date),
         output_root='/fake/output',
     )
     self.task.init_local()
class ModuleEngagementTaskMapTest(InitializeOpaqueKeysMixin, MapperTestMixin, TestCase):
    """Base class for test analysis of detailed student engagement"""

    DEFAULT_USER_ID = 10
    DEFAULT_TIMESTAMP = "2013-12-17T15:38:32.805444"
    DEFAULT_DATE = "2013-12-17"

    def setUp(self):
        super(ModuleEngagementTaskMapTest, self).setUp()

        self.initialize_ids()
        self.video_id = 'i4x-foo-bar-baz'
        self.forum_id = 'a2cb123f9c2146f3211cdc6901acb00e'
        self.event_templates = {
            'play_video': {
                "username": "******",
                "host": "test_host",
                "event_source": "browser",
                "event_type": "play_video",
                "context": {
                    "course_id": self.course_id,
                    "org_id": self.org_id,
                    "user_id": self.DEFAULT_USER_ID,
                },
                "time": "{0}+00:00".format(self.DEFAULT_TIMESTAMP),
                "ip": "127.0.0.1",
                "event": '{"id": "%s", "currentTime": "23.4398", "code": "87389iouhdfh"}' % self.video_id,
                "agent": "blah, blah, blah",
                "page": None
            },
            'problem_check': {
                "username": "******",
                "host": "test_host",
                "event_source": "server",
                "event_type": "problem_check",
                "context": {
                    "course_id": self.course_id,
                    "org_id": self.org_id,
                    "user_id": self.DEFAULT_USER_ID,
                },
                "time": "{0}+00:00".format(self.DEFAULT_TIMESTAMP),
                "ip": "127.0.0.1",
                "event": {
                    "problem_id": self.problem_id,
                    "success": "incorrect",
                },
                "agent": "blah, blah, blah",
                "page": None
            },
            'edx.forum.object.created': {
                "username": "******",
                "host": "test_host",
                "event_source": "server",
                "event_type": "edx.forum.comment.created",
                "name": "edx.forum.comment.created",
                "time": "{0}+00:00".format(self.DEFAULT_TIMESTAMP),
                "event": {
                    "commentable_id": self.forum_id,
                },
                "context": {
                    "course_id": self.course_id,
                    "org_id": self.org_id,
                    "user_id": self.DEFAULT_USER_ID,
                },
                "ip": "127.0.0.1",
                "page": None,
            }
        }
        self.default_event_template = 'problem_check'
        self.create_task()

    def create_task(self, date=None):  # pylint: disable=arguments-differ
        """Allow arguments to be passed to the task constructor."""
        if not date:
            date = self.DEFAULT_DATE
        self.task = ModuleEngagementDataTask(
            date=luigi.DateParameter().parse(date),
            output_root='/fake/output',
        )
        self.task.init_local()

    @data(
        {'time': "2013-12-01T15:38:32.805444"},
        {'username': ''},
        {'event_type': None},
        {'context': {'course_id': 'lskdjfslkdj'}},
        {'event': 'sdfasdf'}
    )
    def test_invalid_events(self, kwargs):
        self.assert_no_map_output_for(self.create_event_log_line(**kwargs))

    def test_browser_problem_check_event(self):
        template = self.event_templates['problem_check']
        self.assert_no_map_output_for(self.create_event_log_line(template=template, event_source='browser'))

    def test_incorrect_problem_check(self):
        self.assert_single_map_output(
            json.dumps(self.event_templates['problem_check']),
            self.get_expected_output_key('problem', self.encoded_problem_id, 'attempted'),
            1
        )

    def get_expected_output_key(self, entity_type, entity_id, action):
        """Generate the expected key"""
        return self.encoded_course_id, 'test_user', self.DEFAULT_DATE, entity_type, entity_id, action

    def test_correct_problem_check(self):
        template = self.event_templates['problem_check']
        template['event']['success'] = 'correct'

        self.assert_map_output(
            json.dumps(template),
            [
                (self.get_expected_output_key('problem', self.encoded_problem_id, 'completed'), 1),
                (self.get_expected_output_key('problem', self.encoded_problem_id, 'attempted'), 1)
            ]
        )

    def test_missing_problem_id(self):
        template = self.event_templates['problem_check']
        del template['event']['problem_id']
        self.assert_no_map_output_for(self.create_event_log_line(template=template))

    def test_missing_video_id(self):
        template = self.event_templates['play_video']
        template['event'] = '{"currentTime": "23.4398", "code": "87389iouhdfh"}'
        self.assert_no_map_output_for(self.create_event_log_line(template=template))

    def test_play_video(self):
        self.assert_single_map_output(
            json.dumps(self.event_templates['play_video']),
            self.get_expected_output_key('video', self.video_id, 'viewed'),
            1
        )

    def test_missing_forum_id(self):
        template = self.event_templates['edx.forum.object.created']
        del template['event']['commentable_id']
        self.assert_no_map_output_for(self.create_event_log_line(template=template))

    @data(
        ('edx.forum.comment.created', 'contributed'),
        ('edx.forum.response.created', 'contributed'),
        ('edx.forum.thread.created', 'contributed'),
    )
    @unpack
    def test_forum_posting_events(self, event_type, expected_action):
        template = self.event_templates['edx.forum.object.created']
        template['event_type'] = event_type
        template['name'] = event_type
        self.assert_single_map_output(
            json.dumps(template),
            self.get_expected_output_key('discussion', self.forum_id, expected_action),
            1
        )
class ModuleEngagementTaskMapTest(InitializeOpaqueKeysMixin, MapperTestMixin,
                                  TestCase):
    """Base class for test analysis of detailed student engagement"""

    DEFAULT_USER_ID = 10
    DEFAULT_TIMESTAMP = "2013-12-17T15:38:32.805444"
    DEFAULT_DATE = "2013-12-17"

    def setUp(self):
        super(ModuleEngagementTaskMapTest, self).setUp()

        self.initialize_ids()
        self.video_id = 'i4x-foo-bar-baz'
        self.forum_id = 'a2cb123f9c2146f3211cdc6901acb00e'
        self.event_templates = {
            'play_video': {
                "username":
                "******",
                "host":
                "test_host",
                "event_source":
                "browser",
                "event_type":
                "play_video",
                "context": {
                    "course_id": self.course_id,
                    "org_id": self.org_id,
                    "user_id": self.DEFAULT_USER_ID,
                },
                "time":
                "{0}+00:00".format(self.DEFAULT_TIMESTAMP),
                "ip":
                "127.0.0.1",
                "event":
                '{"id": "%s", "currentTime": "23.4398", "code": "87389iouhdfh"}'
                % self.video_id,
                "agent":
                "blah, blah, blah",
                "page":
                None
            },
            'problem_check': {
                "username": "******",
                "host": "test_host",
                "event_source": "server",
                "event_type": "problem_check",
                "context": {
                    "course_id": self.course_id,
                    "org_id": self.org_id,
                    "user_id": self.DEFAULT_USER_ID,
                },
                "time": "{0}+00:00".format(self.DEFAULT_TIMESTAMP),
                "ip": "127.0.0.1",
                "event": {
                    "problem_id": self.problem_id,
                    "success": "incorrect",
                },
                "agent": "blah, blah, blah",
                "page": None
            },
            'edx.forum.object.created': {
                "username": "******",
                "host": "test_host",
                "event_source": "server",
                "event_type": "edx.forum.comment.created",
                "name": "edx.forum.comment.created",
                "time": "{0}+00:00".format(self.DEFAULT_TIMESTAMP),
                "event": {
                    "commentable_id": self.forum_id,
                },
                "context": {
                    "course_id": self.course_id,
                    "org_id": self.org_id,
                    "user_id": self.DEFAULT_USER_ID,
                },
                "ip": "127.0.0.1",
                "page": None,
            }
        }
        self.default_event_template = 'problem_check'
        self.create_task()

    def create_task(self, date=None):  # pylint: disable=arguments-differ
        """Allow arguments to be passed to the task constructor."""
        if not date:
            date = self.DEFAULT_DATE
        self.task = ModuleEngagementDataTask(
            date=luigi.DateParameter().parse(date),
            output_root='/fake/output',
        )
        self.task.init_local()

    @data({'time': "2013-12-01T15:38:32.805444"}, {'username': ''},
          {'event_type': None}, {'context': {
              'course_id': 'lskdjfslkdj'
          }}, {'event': 'sdfasdf'})
    def test_invalid_events(self, kwargs):
        self.assert_no_map_output_for(self.create_event_log_line(**kwargs))

    def test_browser_problem_check_event(self):
        template = self.event_templates['problem_check']
        self.assert_no_map_output_for(
            self.create_event_log_line(template=template,
                                       event_source='browser'))

    def test_incorrect_problem_check(self):
        self.assert_single_map_output(
            json.dumps(self.event_templates['problem_check']),
            self.get_expected_output_key('problem', self.encoded_problem_id,
                                         'attempted'), 1)

    def get_expected_output_key(self, entity_type, entity_id, action):
        """Generate the expected key"""
        return self.encoded_course_id, 'test_user', self.DEFAULT_DATE, entity_type, entity_id, action

    def test_correct_problem_check(self):
        template = self.event_templates['problem_check']
        template['event']['success'] = 'correct'

        self.assert_map_output(
            json.dumps(template),
            [(self.get_expected_output_key('problem', self.encoded_problem_id,
                                           'completed'), 1),
             (self.get_expected_output_key('problem', self.encoded_problem_id,
                                           'attempted'), 1)])

    def test_missing_problem_id(self):
        template = self.event_templates['problem_check']
        del template['event']['problem_id']
        self.assert_no_map_output_for(
            self.create_event_log_line(template=template))

    def test_missing_video_id(self):
        template = self.event_templates['play_video']
        template[
            'event'] = '{"currentTime": "23.4398", "code": "87389iouhdfh"}'
        self.assert_no_map_output_for(
            self.create_event_log_line(template=template))

    def test_play_video(self):
        self.assert_single_map_output(
            json.dumps(self.event_templates['play_video']),
            self.get_expected_output_key('video', self.video_id, 'viewed'), 1)

    def test_missing_forum_id(self):
        template = self.event_templates['edx.forum.object.created']
        del template['event']['commentable_id']
        self.assert_no_map_output_for(
            self.create_event_log_line(template=template))

    @data(
        ('edx.forum.comment.created', 'contributed'),
        ('edx.forum.response.created', 'contributed'),
        ('edx.forum.thread.created', 'contributed'),
    )
    @unpack
    def test_forum_posting_events(self, event_type, expected_action):
        template = self.event_templates['edx.forum.object.created']
        template['event_type'] = event_type
        template['name'] = event_type
        self.assert_single_map_output(
            json.dumps(template),
            self.get_expected_output_key('discussion', self.forum_id,
                                         expected_action), 1)
class ModuleEngagementTaskAnonymousUserTest(InitializeOpaqueKeysMixin,
                                            MapperTestMixin, TestCase):
    """
    Tests to verify that anonymous users are stored or omitted as configured.
    """

    DEFAULT_USER_ID = 10
    DEFAULT_TIMESTAMP = "2013-12-17T15:38:32.805444"
    DEFAULT_DATE = "2013-12-17"

    def setUp(self):
        super(ModuleEngagementTaskAnonymousUserTest, self).setUp()

        self.initialize_ids()
        self.anonymous_username = '******'
        self.video_id = 'i4x-foo-bar-baz'
        self.event_templates = {
            'play_video': {
                "host":
                "test_host",
                "event_source":
                "browser",
                "event_type":
                "play_video",
                "context": {
                    "course_id": self.course_id,
                    "org_id": self.org_id,
                },
                "time":
                "{0}+00:00".format(self.DEFAULT_TIMESTAMP),
                "ip":
                "127.0.0.1",
                "event":
                '{"id": "%s", "currentTime": "23.4398", "code": "87389iouhdfh"}'
                % self.video_id,
                "agent":
                "blah, blah, blah",
                "page":
                None
            },
        }
        self.default_event_template = 'play_video'

    def create_task(self, store_anonymous_username=None):  # pylint: disable=arguments-differ
        """Allow arguments to be passed to the task constructor."""
        self.task = ModuleEngagementDataTask(
            date=luigi.DateParameter().parse(self.DEFAULT_DATE),
            store_anonymous_username=store_anonymous_username,
            output_root='/fake/output',
        )
        self.task.init_local()

    def test_no_store_anonymous_username(self):
        self.create_task()
        self.assert_no_map_output_for(self.create_event_log_line())

    def test_store_anonymous_username(self):
        self.create_task(store_anonymous_username=self.anonymous_username)
        self.assert_single_map_output(
            self.create_event_log_line(),
            (self.encoded_course_id, self.anonymous_username,
             self.DEFAULT_DATE, 'video', self.video_id, 'viewed'), 1)