{"action":"created","comment":{"url":"https://api.github.com/repos/edx/edx-analytics-data-api/pulls/comments/47113465","id":47113465,"diff_hunk":"@@ -220,3 +223,64 @@ class Meta:\n     def get_course_user(cls, course_id, username):\n         return cls.search().query('term', course_id=course_id).query(\n             'term', username=username).execute()\n+\n+\n+class ModuleEngagement(models.Model):\n+    \"\"\"User interactions with entities within the courseware.\"\"\"\n+\n+    course_id = models.CharField(db_index=True, max_length=255)\n+    username = models.CharField(max_length=255)\n+    date = models.DateTimeField()\n+    # This will be one of \"problem\", \"video\" or \"forum\"\n+    entity_type = models.CharField(max_length=255)\n+    # For problems this will be the usage key, for videos it will be the html encoded module ID,\n+    # for forums it will be the commentable_id\n+    entity_id = models.CharField(max_length=255)\n+    # A description of what interaction occurred.\n+    event = models.CharField(max_length=255)\n+    # The number of times the user interacted with this entity in this way on this day.\n+    count = models.IntegerField()\n+\n+    class Meta(object):\n+        db_table = 'module_engagement'\n+\n+\n+class ModuleEngagementTimelineManager(models.Manager):\n+    \"\"\"\n+    Modifies the ModuleEngagement queryset to aggregate engagement data for\n+    the learner engagement timeline.\n+    \"\"\"\n+    def get_timelines(self, course_id, username):\n+        queryset = ModuleEngagement.objects.all().filter(course_id=course_id, username=username)\\\n+            .values('date', 'entity_type', 'event')\\\n+            .annotate(count=Sum('count'))\\\n+            .order_by('date')\n+\n+        timelines = []\n+\n+        for key, group in groupby(queryset, lambda x: (x['date'])):\n+            # Iterate over groups and create a single item with engagement data\n+            item = {\n+                u'date': key,\n+            }\n+            for engagement in group:\n+                entity_type = engagement_entity_types.SINGULAR_TO_PLURAL[engagement['entity_type']]\n+                engagement_type = '{}_{}'.format(entity_type, engagement['event'])\n+                count = item.get(engagement_type, 0)\n+                count += engagement['count']\n+                item[engagement_type] = count\n+            timelines.append(item)\n+\n+        return timelines\n+\n+\n+class ModuleEngagementTimeline(models.Model):\n+    \"\"\"\n+    Learner engagement timeline data.\n+    \"\"\"\n+    date = models.DateField()\n+    problems_attempted = models.IntegerField()\n+    problems_completed = models.IntegerField()\n+    discussions_contributed = models.IntegerField()\n+    videos_viewed = models.IntegerField()\n+    objects = ModuleEngagementTimelineManager()","path":"analytics_data_api/v0/models.py","position":77,"original_position":77,"commit_id":"c3975bc9462818fb02b3d30a5af60be05753a367","original_commit_id":"c3975bc9462818fb02b3d30a5af60be05753a367","user":{"login":"dan-f","id":2146312,"avatar_url":"https://avatars.githubusercontent.com/u/2146312?v=3","gravatar_id":"","url":"https://api.github.com/users/dan-f","html_url":"https://github.com/dan-f","followers_url":"https://api.github.com/users/dan-f/followers","following_url":"https://api.github.com/users/dan-f/following{/other_user}","gists_url":"https://api.github.com/users/dan-f/gists{/gist_id}","starred_url":"https://api.github.com/users/dan-f/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dan-f/subscriptions","organizations_url":"https://api.github.com/users/dan-f/orgs","repos_url":"https://api.github.com/users/dan-f/repos","events_url":"https://api.github.com/users/dan-f/events{/privacy}","received_events_url":"https://api.github.com/users/dan-f/received_events","type":"User","site_admin":false},"body":"I'm used to the django convention of the manager belonging to the model it manages. If the `ModulelEngagementTimelineManager` were to belong to the `ModuleEngagement` class, would we be able to get rid of this model?\r\n\r\nI'm also skeptical of having a model that doesn't map to a database table.","created_at":"2015-12-09T16:26:39Z","updated_at":"2015-12-09T16:26:39Z","html_url":"https://github.com/edx/edx-analytics-data-api/pull/92#discussion_r47113465","pull_request_url":"https://api.github.com/repos/edx/edx-analytics-data-api/pulls/92","_links":{"self":{"href":"https://api.github.com/repos/edx/edx-analytics-data-api/pulls/comments/47113465"},"html":{"href":"https://github.com/edx/edx-analytics-data-api/pull/92#discussion_r47113465"},"pull_request":{"href":"https://api.github.com/repos/edx/edx-analytics-data-api/pulls/92"}}},"pull_request":{"url":"https://api.github.com/repos/edx/edx-analytics-data-api/pulls/92","id":53041934,"html_url":"https://github.com/edx/edx-analytics-data-api/pull/92","diff_url":"https://github.com/edx/edx-analytics-data-api/pull/92.diff","patch_url":"https://github.com/edx/edx-analytics-data-api/pull/92.patch","issue_url":"https://api.github.com/repos/edx/edx-analytics-data-api/issues/92","number":92,"state":"open","locked":false,"title":"Added learner engagement timeline.","user":{"login":"dsjen","id":5265058,"avatar_url":"https://avatars.githubusercontent.com/u/5265058?v=3","gravatar_id":"","url":"https://api.github.com/users/dsjen","html_url":"https://github.com/dsjen","followers_url":"https://api.github.com/users/dsjen/followers","following_url":"https://api.github.com/users/dsjen/following{/other_user}","gists_url":"https://api.github.com/users/dsjen/gists{/gist_id}","starred_url":"https://api.github.com/users/dsjen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dsjen/subscriptions","organizations_url":"https://api.github.com/users/dsjen/orgs","repos_url":"https://api.github.com/users/dsjen/repos","events_url":"https://api.github.com/users/dsjen/events{/privacy}","received_events_url":"https://api.github.com/users/dsjen/received_events","type":"User","site_admin":false},"body":"@dan-f, this adds in the learn engagement timeline endpoint.  Please review when you get the chance.","created_at":"2015-12-08T21:34:34Z","updated_at":"2015-12-09T16:26:39Z","closed_at":null,"merged_at":null,"merge_commit_sha":"88ac2a4f89db6e3dc9106986e8aee8b3227dce3c","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/edx/edx-analytics-data-api/pulls/92/commits","review_comments_url":"https://api.github.com/repos/edx/edx-analytics-data-api/pulls/92/comments","review_comment_url":"https://api.github.com/repos/edx/edx-analytics-data-api/pulls/comments{/number}","comments_url":"https://api.github.com/repos/edx/edx-analytics-data-api/issues/92/comments","statuses_url":"https://api.github.com/repos/edx/edx-analytics-data-api/statuses/c3975bc9462818fb02b3d30a5af60be05753a367","head":{"label":"edx:dsjen/learner-timeline","ref":"dsjen/learner-timeline","sha":"c3975bc9462818fb02b3d30a5af60be05753a367","user":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"repo":{"id":20415476,"name":"edx-analytics-data-api","full_name":"edx/edx-analytics-data-api","owner":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/edx/edx-analytics-data-api","description":"","fork":false,"url":"https://api.github.com/repos/edx/edx-analytics-data-api","forks_url":"https://api.github.com/repos/edx/edx-analytics-data-api/forks","keys_url":"https://api.github.com/repos/edx/edx-analytics-data-api/keys{/key_id}","collaborators_url":"https://api.github.com/repos/edx/edx-analytics-data-api/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/edx/edx-analytics-data-api/teams","hooks_url":"https://api.github.com/repos/edx/edx-analytics-data-api/hooks","issue_events_url":"https://api.github.com/repos/edx/edx-analytics-data-api/issues/events{/number}","events_url":"https://api.github.com/repos/edx/edx-analytics-data-api/events","assignees_url":"https://api.github.com/repos/edx/edx-analytics-data-api/assignees{/user}","branches_url":"https://api.github.com/repos/edx/edx-analytics-data-api/branches{/branch}","tags_url":"https://api.github.com/repos/edx/edx-analytics-data-api/tags","blobs_url":"https://api.github.com/repos/edx/edx-analytics-data-api/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/edx/edx-analytics-data-api/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/edx/edx-analytics-data-api/git/refs{/sha}","trees_url":"https://api.github.com/repos/edx/edx-analytics-data-api/git/trees{/sha}","statuses_url":"https://api.github.com/repos/edx/edx-analytics-data-api/statuses/{sha}","languages_url":"https://api.github.com/repos/edx/edx-analytics-data-api/languages","stargazers_url":"https://api.github.com/repos/edx/edx-analytics-data-api/stargazers","contributors_url":"https://api.github.com/repos/edx/edx-analytics-data-api/contributors","subscribers_url":"https://api.github.com/repos/edx/edx-analytics-data-api/subscribers","subscription_url":"https://api.github.com/repos/edx/edx-analytics-data-api/subscription","commits_url":"https://api.github.com/repos/edx/edx-analytics-data-api/commits{/sha}","git_commits_url":"https://api.github.com/repos/edx/edx-analytics-data-api/git/commits{/sha}","comments_url":"https://api.github.com/repos/edx/edx-analytics-data-api/comments{/number}","issue_comment_url":"https://api.github.com/repos/edx/edx-analytics-data-api/issues/comments{/number}","contents_url":"https://api.github.com/repos/edx/edx-analytics-data-api/contents/{+path}","compare_url":"https://api.github.com/repos/edx/edx-analytics-data-api/compare/{base}...{head}","merges_url":"https://api.github.com/repos/edx/edx-analytics-data-api/merges","archive_url":"https://api.github.com/repos/edx/edx-analytics-data-api/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/edx/edx-analytics-data-api/downloads","issues_url":"https://api.github.com/repos/edx/edx-analytics-data-api/issues{/number}","pulls_url":"https://api.github.com/repos/edx/edx-analytics-data-api/pulls{/number}","milestones_url":"https://api.github.com/repos/edx/edx-analytics-data-api/milestones{/number}","notifications_url":"https://api.github.com/repos/edx/edx-analytics-data-api/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/edx/edx-analytics-data-api/labels{/name}","releases_url":"https://api.github.com/repos/edx/edx-analytics-data-api/releases{/id}","created_at":"2014-06-02T18:55:15Z","updated_at":"2015-10-13T15:21:25Z","pushed_at":"2015-12-08T22:22:19Z","git_url":"git://github.com/edx/edx-analytics-data-api.git","ssh_url":"git@github.com:edx/edx-analytics-data-api.git","clone_url":"https://github.com/edx/edx-analytics-data-api.git","svn_url":"https://github.com/edx/edx-analytics-data-api","homepage":null,"size":1276,"stargazers_count":5,"watchers_count":5,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":25,"mirror_url":null,"open_issues_count":4,"forks":25,"open_issues":4,"watchers":5,"default_branch":"master"}},"base":{"label":"edx:learner-analytics","ref":"learner-analytics","sha":"c938210cf848a7917a47d562f348d8066004991d","user":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"repo":{"id":20415476,"name":"edx-analytics-data-api","full_name":"edx/edx-analytics-data-api","owner":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/edx/edx-analytics-data-api","description":"","fork":false,"url":"https://api.github.com/repos/edx/edx-analytics-data-api","forks_url":"https://api.github.com/repos/edx/edx-analytics-data-api/forks","keys_url":"https://api.github.com/repos/edx/edx-analytics-data-api/keys{/key_id}","collaborators_url":"https://api.github.com/repos/edx/edx-analytics-data-api/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/edx/edx-analytics-data-api/teams","hooks_url":"https://api.github.com/repos/edx/edx-analytics-data-api/hooks","issue_events_url":"https://api.github.com/repos/edx/edx-analytics-data-api/issues/events{/number}","events_url":"https://api.github.com/repos/edx/edx-analytics-data-api/events","assignees_url":"https://api.github.com/repos/edx/edx-analytics-data-api/assignees{/user}","branches_url":"https://api.github.com/repos/edx/edx-analytics-data-api/branches{/branch}","tags_url":"https://api.github.com/repos/edx/edx-analytics-data-api/tags","blobs_url":"https://api.github.com/repos/edx/edx-analytics-data-api/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/edx/edx-analytics-data-api/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/edx/edx-analytics-data-api/git/refs{/sha}","trees_url":"https://api.github.com/repos/edx/edx-analytics-data-api/git/trees{/sha}","statuses_url":"https://api.github.com/repos/edx/edx-analytics-data-api/statuses/{sha}","languages_url":"https://api.github.com/repos/edx/edx-analytics-data-api/languages","stargazers_url":"https://api.github.com/repos/edx/edx-analytics-data-api/stargazers","contributors_url":"https://api.github.com/repos/edx/edx-analytics-data-api/contributors","subscribers_url":"https://api.github.com/repos/edx/edx-analytics-data-api/subscribers","subscription_url":"https://api.github.com/repos/edx/edx-analytics-data-api/subscription","commits_url":"https://api.github.com/repos/edx/edx-analytics-data-api/commits{/sha}","git_commits_url":"https://api.github.com/repos/edx/edx-analytics-data-api/git/commits{/sha}","comments_url":"https://api.github.com/repos/edx/edx-analytics-data-api/comments{/number}","issue_comment_url":"https://api.github.com/repos/edx/edx-analytics-data-api/issues/comments{/number}","contents_url":"https://api.github.com/repos/edx/edx-analytics-data-api/contents/{+path}","compare_url":"https://api.github.com/repos/edx/edx-analytics-data-api/compare/{base}...{head}","merges_url":"https://api.github.com/repos/edx/edx-analytics-data-api/merges","archive_url":"https://api.github.com/repos/edx/edx-analytics-data-api/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/edx/edx-analytics-data-api/downloads","issues_url":"https://api.github.com/repos/edx/edx-analytics-data-api/issues{/number}","pulls_url":"https://api.github.com/repos/edx/edx-analytics-data-api/pulls{/number}","milestones_url":"https://api.github.com/repos/edx/edx-analytics-data-api/milestones{/number}","notifications_url":"https://api.github.com/repos/edx/edx-analytics-data-api/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/edx/edx-analytics-data-api/labels{/name}","releases_url":"https://api.github.com/repos/edx/edx-analytics-data-api/releases{/id}","created_at":"2014-06-02T18:55:15Z","updated_at":"2015-10-13T15:21:25Z","pushed_at":"2015-12-08T22:22:19Z","git_url":"git://github.com/edx/edx-analytics-data-api.git","ssh_url":"git@github.com:edx/edx-analytics-data-api.git","clone_url":"https://github.com/edx/edx-analytics-data-api.git","svn_url":"https://github.com/edx/edx-analytics-data-api","homepage":null,"size":1276,"stargazers_count":5,"watchers_count":5,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":25,"mirror_url":null,"open_issues_count":4,"forks":25,"open_issues":4,"watchers":5,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/edx/edx-analytics-data-api/pulls/92"},"html":{"href":"https://github.com/edx/edx-analytics-data-api/pull/92"},"issue":{"href":"https://api.github.com/repos/edx/edx-analytics-data-api/issues/92"},"comments":{"href":"https://api.github.com/repos/edx/edx-analytics-data-api/issues/92/comments"},"review_comments":{"href":"https://api.github.com/repos/edx/edx-analytics-data-api/pulls/92/comments"},"review_comment":{"href":"https://api.github.com/repos/edx/edx-analytics-data-api/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/edx/edx-analytics-data-api/pulls/92/commits"},"statuses":{"href":"https://api.github.com/repos/edx/edx-analytics-data-api/statuses/c3975bc9462818fb02b3d30a5af60be05753a367"}}}}