{"action":"created","comment":{"url":"https://api.github.com/repos/edx/edx-platform/pulls/comments/157619147","pull_request_review_id":84291651,"id":157619147,"diff_hunk":"@@ -0,0 +1,144 @@\n+(function(define) {\n+    'use strict';\n+    /**\n+     * Completion handler\n+     * @exports video/09_completion.js\n+     * @constructor\n+     * @param {Object} state The object containing the state of the video\n+     * @return {jquery Promise}\n+     */\n+    define('video/09_completion.js', [], function() {\n+        var VideoCompletionHandler = function(state) {\n+            if (!(this instanceof VideoCompletionHandler)) {\n+                return new VideoCompletionHandler(state);\n+            }\n+            this.state = state;\n+            this.state.completionHandler = this;\n+            this.initialize();\n+            return $.Deferred().resolve().promise();\n+        };\n+\n+        VideoCompletionHandler.prototype = {\n+            destroy: function() {\n+                this.el.remove();\n+                this.state.el.off('destroy', this.destroy);\n+                delete this.state.completionHandler;\n+            },\n+\n+            /** Initializes the module. */\n+            initialize: function() {\n+                this.lastSentTime = undefined;\n+                this.this.isComplete = false;\n+                this.completionPercentage = this.state.config.completionPercentage;\n+                this.startTime = this.state.config.startTime;\n+                this.endTime = this.state.config.endTime;\n+                this.isEnabled = this.state.config.completionEnabled;\n+                if (this.endTime) {\n+                    this.completeAfterSeconds = this.calculateCompleteAfterSeconds(this.startTime, this.endTime);\n+                }\n+                if (this.isEnabled) {\n+                    this.bindHandlers();\n+                }\n+            },\n+\n+            /** Bind any necessary function callbacks to DOM events. */\n+            bindHandlers: function() {\n+                var self = this;\n+\n+                /** Event handler to check if the video is complete, and submit\n+                 *  a completion if it is.\n+                 *\n+                 *  If the timeupdate handler doesn't fire after the required\n+                 *  percentage, this will catch any fully complete videos.\n+                 */\n+                this.state.el.on('ended.completion', function() {\n+                    self.handleEnded();\n+                });\n+\n+                /** Event handler to check video progress, and mark complete if\n+                 *  greater than completionPercentage\n+                 **/\n+                this.state.el.on('timeupdate.completion', function(ev, currentTime) {\n+                    self.handleCompletion(currentTime);\n+                });\n+            },\n+\n+            handleEnded: function() {\n+                if (this.isComplete) {\n+                    return;\n+                }\n+                this.markCompletion();\n+            },\n+\n+            handleCompletion: function(currentTime) {\n+                var duration;\n+                if (this.isComplete) {\n+                    return;\n+                }\n+                if (this.lastSentTime !== undefined && currentTime - this.lastSentTime < this.repostDelaySeconds()) {\n+                    // Throttle attempts to submit in case of network issues\n+                    return;\n+                }\n+                if (this.completeAfterSeconds === undefined) {\n+                    // Duration is not available at initialization time\n+                    duration = this.state.videoPlayer.duration();\n+                    if (!duration) {\n+                        // duration is not yet set. Wait for another event,\n+                        // or fall back to 'ended' handler.\n+                        return;\n+                    }\n+                    this.completeAfterSeconds = this.calculateCompleteAfterSeconds(this.startTime, duration);\n+                }\n+\n+                if (currentTime > this.completeAfterSeconds) {","path":"common/lib/xmodule/xmodule/js/src/video/09_completion.js","position":null,"original_position":93,"commit_id":"c515e5104477e20830bdd21b84fbe9d57f91ec19","original_commit_id":"831ba4886e6d82c0354eda50d4ad883de7f07ed4","user":{"login":"jcdyer","id":77486,"avatar_url":"https://avatars0.githubusercontent.com/u/77486?v=4","gravatar_id":"","url":"https://api.github.com/users/jcdyer","html_url":"https://github.com/jcdyer","followers_url":"https://api.github.com/users/jcdyer/followers","following_url":"https://api.github.com/users/jcdyer/following{/other_user}","gists_url":"https://api.github.com/users/jcdyer/gists{/gist_id}","starred_url":"https://api.github.com/users/jcdyer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jcdyer/subscriptions","organizations_url":"https://api.github.com/users/jcdyer/orgs","repos_url":"https://api.github.com/users/jcdyer/repos","events_url":"https://api.github.com/users/jcdyer/events{/privacy}","received_events_url":"https://api.github.com/users/jcdyer/received_events","type":"User","site_admin":false},"body":"On further look, I've settled on calling everything \"Time.\"  I was hoping to distinguish between DateTime-like objects (which in javascript are represented as \"seconds since the epoch\", and the number of seconds from the beginning of a video, but even that distinction was going to end up muddied, because the video player exposes \"startTime\" and \"endTime\" (which are seconds since the start of the video), so as per your initial recommendation, I'm settling on a consistent use of \"Time,\" with a comment to explain where meaning might be confusing.","created_at":"2017-12-18T22:32:32Z","updated_at":"2017-12-18T22:32:32Z","html_url":"https://github.com/edx/edx-platform/pull/16569#discussion_r157619147","pull_request_url":"https://api.github.com/repos/edx/edx-platform/pulls/16569","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/comments/157619147"},"html":{"href":"https://github.com/edx/edx-platform/pull/16569#discussion_r157619147"},"pull_request":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/16569"}},"in_reply_to_id":157498982},"pull_request":{"url":"https://api.github.com/repos/edx/edx-platform/pulls/16569","id":152630800,"html_url":"https://github.com/edx/edx-platform/pull/16569","diff_url":"https://github.com/edx/edx-platform/pull/16569.diff","patch_url":"https://github.com/edx/edx-platform/pull/16569.patch","issue_url":"https://api.github.com/repos/edx/edx-platform/issues/16569","number":16569,"state":"open","locked":false,"title":"Define custom completion for VideoModule","user":{"login":"jcdyer","id":77486,"avatar_url":"https://avatars0.githubusercontent.com/u/77486?v=4","gravatar_id":"","url":"https://api.github.com/users/jcdyer","html_url":"https://github.com/jcdyer","followers_url":"https://api.github.com/users/jcdyer/followers","following_url":"https://api.github.com/users/jcdyer/following{/other_user}","gists_url":"https://api.github.com/users/jcdyer/gists{/gist_id}","starred_url":"https://api.github.com/users/jcdyer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jcdyer/subscriptions","organizations_url":"https://api.github.com/users/jcdyer/orgs","repos_url":"https://api.github.com/users/jcdyer/repos","events_url":"https://api.github.com/users/jcdyer/events{/privacy}","received_events_url":"https://api.github.com/users/jcdyer/received_events","type":"User","site_admin":false},"body":"Update the VideoModule to publish a completion event when the player\r\nreaches 95% complete, and submit a BlockCompletion when that event\r\noccurs.\r\n\r\n## Testing instructions\r\n\r\n1.  In the lms shell, remove all existing BlockCompletions for video blocks.\r\n    ```python\r\n    from lms.djangoapps.completion.models import BlockCompletion\r\n    BlockCompletion.objects.filter(block_type='video').delete()\r\n    ```\r\n2.  Sign in to the LMS and visit the demo course.\r\n3.  Navigate to \"Example Week 1: Getting Started > Lesson 1 - Getting Started > (Vertical 2) Working with Videos\".  Open the browser's development panel, and view network traffic.  \r\n4. Verify that when the video reach 4m 3s, a POST is sent to a URL for the block ending with \"publish_completion\", and receives a 200 response.  \r\n5.  Verify that a BlockCompletion now exists for the video block with completion value == 1.0.\r\n    ```python\r\n    from lms.djangoapps.completion.models import BlockCompletion\r\n    print(BlockCompletion.objects.filter(block_type='video', completion=1.0))\r\n    ```\r\n6.  Navigate to the next vertical: \"Videos on edX\".  This one uses more features (a custom start time and end-time).  Watch to the end of the video (or jump to a few seconds before the end of the video, and just watch the end). \r\n7.  Verify that a publish_completion POST is sent after 7m 17s (Start time is 5m 10s, end time is 7m 24s = 134s; 134 * .95 = 127.3s; 5m 10s + 127s = 7m 17s), and receives a 200 response.\r\n8.  Verify that a BlockCompletion object exists for this block as well.\r\n\r\n## Author concerns\r\n\r\nI'm rusty at Javascript, and entirely new to the video player.  I may not be doing things the right way. \r\n\r\n## Reviewers\r\n- [x] @pomegranited \r\n- [ ] @robrap \r\n\r\nFYI: @yro for video player expertise.\r\n\r\n## TODO:\r\n\r\n- [ ] Squash commits\r\n- [ ] Rebase PR against edx:master after cliff/complete-default-blocks merges ( #16234 )","created_at":"2017-11-14T22:05:27Z","updated_at":"2017-12-18T22:32:32Z","closed_at":null,"merged_at":null,"merge_commit_sha":"5f7c526e0028462e96bf82293ff117a1753f4b46","assignee":null,"assignees":[],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/edx/edx-platform/pulls/16569/commits","review_comments_url":"https://api.github.com/repos/edx/edx-platform/pulls/16569/comments","review_comment_url":"https://api.github.com/repos/edx/edx-platform/pulls/comments{/number}","comments_url":"https://api.github.com/repos/edx/edx-platform/issues/16569/comments","statuses_url":"https://api.github.com/repos/edx/edx-platform/statuses/c515e5104477e20830bdd21b84fbe9d57f91ec19","head":{"label":"open-craft:cliff/video-completion","ref":"cliff/video-completion","sha":"c515e5104477e20830bdd21b84fbe9d57f91ec19","user":{"login":"open-craft","id":7414786,"avatar_url":"https://avatars0.githubusercontent.com/u/7414786?v=4","gravatar_id":"","url":"https://api.github.com/users/open-craft","html_url":"https://github.com/open-craft","followers_url":"https://api.github.com/users/open-craft/followers","following_url":"https://api.github.com/users/open-craft/following{/other_user}","gists_url":"https://api.github.com/users/open-craft/gists{/gist_id}","starred_url":"https://api.github.com/users/open-craft/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/open-craft/subscriptions","organizations_url":"https://api.github.com/users/open-craft/orgs","repos_url":"https://api.github.com/users/open-craft/repos","events_url":"https://api.github.com/users/open-craft/events{/privacy}","received_events_url":"https://api.github.com/users/open-craft/received_events","type":"Organization","site_admin":false},"repo":{"id":19940883,"name":"edx-platform","full_name":"open-craft/edx-platform","owner":{"login":"open-craft","id":7414786,"avatar_url":"https://avatars0.githubusercontent.com/u/7414786?v=4","gravatar_id":"","url":"https://api.github.com/users/open-craft","html_url":"https://github.com/open-craft","followers_url":"https://api.github.com/users/open-craft/followers","following_url":"https://api.github.com/users/open-craft/following{/other_user}","gists_url":"https://api.github.com/users/open-craft/gists{/gist_id}","starred_url":"https://api.github.com/users/open-craft/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/open-craft/subscriptions","organizations_url":"https://api.github.com/users/open-craft/orgs","repos_url":"https://api.github.com/users/open-craft/repos","events_url":"https://api.github.com/users/open-craft/events{/privacy}","received_events_url":"https://api.github.com/users/open-craft/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/open-craft/edx-platform","description":"the edX learning management system (LMS) and course authoring tool, Studio","fork":true,"url":"https://api.github.com/repos/open-craft/edx-platform","forks_url":"https://api.github.com/repos/open-craft/edx-platform/forks","keys_url":"https://api.github.com/repos/open-craft/edx-platform/keys{/key_id}","collaborators_url":"https://api.github.com/repos/open-craft/edx-platform/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/open-craft/edx-platform/teams","hooks_url":"https://api.github.com/repos/open-craft/edx-platform/hooks","issue_events_url":"https://api.github.com/repos/open-craft/edx-platform/issues/events{/number}","events_url":"https://api.github.com/repos/open-craft/edx-platform/events","assignees_url":"https://api.github.com/repos/open-craft/edx-platform/assignees{/user}","branches_url":"https://api.github.com/repos/open-craft/edx-platform/branches{/branch}","tags_url":"https://api.github.com/repos/open-craft/edx-platform/tags","blobs_url":"https://api.github.com/repos/open-craft/edx-platform/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/open-craft/edx-platform/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/open-craft/edx-platform/git/refs{/sha}","trees_url":"https://api.github.com/repos/open-craft/edx-platform/git/trees{/sha}","statuses_url":"https://api.github.com/repos/open-craft/edx-platform/statuses/{sha}","languages_url":"https://api.github.com/repos/open-craft/edx-platform/languages","stargazers_url":"https://api.github.com/repos/open-craft/edx-platform/stargazers","contributors_url":"https://api.github.com/repos/open-craft/edx-platform/contributors","subscribers_url":"https://api.github.com/repos/open-craft/edx-platform/subscribers","subscription_url":"https://api.github.com/repos/open-craft/edx-platform/subscription","commits_url":"https://api.github.com/repos/open-craft/edx-platform/commits{/sha}","git_commits_url":"https://api.github.com/repos/open-craft/edx-platform/git/commits{/sha}","comments_url":"https://api.github.com/repos/open-craft/edx-platform/comments{/number}","issue_comment_url":"https://api.github.com/repos/open-craft/edx-platform/issues/comments{/number}","contents_url":"https://api.github.com/repos/open-craft/edx-platform/contents/{+path}","compare_url":"https://api.github.com/repos/open-craft/edx-platform/compare/{base}...{head}","merges_url":"https://api.github.com/repos/open-craft/edx-platform/merges","archive_url":"https://api.github.com/repos/open-craft/edx-platform/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/open-craft/edx-platform/downloads","issues_url":"https://api.github.com/repos/open-craft/edx-platform/issues{/number}","pulls_url":"https://api.github.com/repos/open-craft/edx-platform/pulls{/number}","milestones_url":"https://api.github.com/repos/open-craft/edx-platform/milestones{/number}","notifications_url":"https://api.github.com/repos/open-craft/edx-platform/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/open-craft/edx-platform/labels{/name}","releases_url":"https://api.github.com/repos/open-craft/edx-platform/releases{/id}","deployments_url":"https://api.github.com/repos/open-craft/edx-platform/deployments","created_at":"2014-05-19T12:28:25Z","updated_at":"2017-08-14T18:35:23Z","pushed_at":"2017-12-18T22:27:32Z","git_url":"git://github.com/open-craft/edx-platform.git","ssh_url":"git@github.com:open-craft/edx-platform.git","clone_url":"https://github.com/open-craft/edx-platform.git","svn_url":"https://github.com/open-craft/edx-platform","homepage":"http://code.edx.org/","size":869888,"stargazers_count":3,"watchers_count":3,"language":"Python","has_issues":false,"has_projects":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":2,"mirror_url":null,"archived":false,"open_issues_count":5,"license":null,"forks":2,"open_issues":5,"watchers":3,"default_branch":"open-craft-notice"}},"base":{"label":"edx:cliff/complete-default-blocks","ref":"cliff/complete-default-blocks","sha":"b8202e40de604a9a426934d3dabd1b350dcb5251","user":{"login":"edx","id":3179841,"avatar_url":"https://avatars3.githubusercontent.com/u/3179841?v=4","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":10391073,"name":"edx-platform","full_name":"edx/edx-platform","owner":{"login":"edx","id":3179841,"avatar_url":"https://avatars3.githubusercontent.com/u/3179841?v=4","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-platform","description":"The Open edX platform, the software that powers edX!","fork":false,"url":"https://api.github.com/repos/edx/edx-platform","forks_url":"https://api.github.com/repos/edx/edx-platform/forks","keys_url":"https://api.github.com/repos/edx/edx-platform/keys{/key_id}","collaborators_url":"https://api.github.com/repos/edx/edx-platform/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/edx/edx-platform/teams","hooks_url":"https://api.github.com/repos/edx/edx-platform/hooks","issue_events_url":"https://api.github.com/repos/edx/edx-platform/issues/events{/number}","events_url":"https://api.github.com/repos/edx/edx-platform/events","assignees_url":"https://api.github.com/repos/edx/edx-platform/assignees{/user}","branches_url":"https://api.github.com/repos/edx/edx-platform/branches{/branch}","tags_url":"https://api.github.com/repos/edx/edx-platform/tags","blobs_url":"https://api.github.com/repos/edx/edx-platform/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/edx/edx-platform/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/edx/edx-platform/git/refs{/sha}","trees_url":"https://api.github.com/repos/edx/edx-platform/git/trees{/sha}","statuses_url":"https://api.github.com/repos/edx/edx-platform/statuses/{sha}","languages_url":"https://api.github.com/repos/edx/edx-platform/languages","stargazers_url":"https://api.github.com/repos/edx/edx-platform/stargazers","contributors_url":"https://api.github.com/repos/edx/edx-platform/contributors","subscribers_url":"https://api.github.com/repos/edx/edx-platform/subscribers","subscription_url":"https://api.github.com/repos/edx/edx-platform/subscription","commits_url":"https://api.github.com/repos/edx/edx-platform/commits{/sha}","git_commits_url":"https://api.github.com/repos/edx/edx-platform/git/commits{/sha}","comments_url":"https://api.github.com/repos/edx/edx-platform/comments{/number}","issue_comment_url":"https://api.github.com/repos/edx/edx-platform/issues/comments{/number}","contents_url":"https://api.github.com/repos/edx/edx-platform/contents/{+path}","compare_url":"https://api.github.com/repos/edx/edx-platform/compare/{base}...{head}","merges_url":"https://api.github.com/repos/edx/edx-platform/merges","archive_url":"https://api.github.com/repos/edx/edx-platform/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/edx/edx-platform/downloads","issues_url":"https://api.github.com/repos/edx/edx-platform/issues{/number}","pulls_url":"https://api.github.com/repos/edx/edx-platform/pulls{/number}","milestones_url":"https://api.github.com/repos/edx/edx-platform/milestones{/number}","notifications_url":"https://api.github.com/repos/edx/edx-platform/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/edx/edx-platform/labels{/name}","releases_url":"https://api.github.com/repos/edx/edx-platform/releases{/id}","deployments_url":"https://api.github.com/repos/edx/edx-platform/deployments","created_at":"2013-05-30T20:20:38Z","updated_at":"2017-12-17T20:07:01Z","pushed_at":"2017-12-18T22:28:11Z","git_url":"git://github.com/edx/edx-platform.git","ssh_url":"git@github.com:edx/edx-platform.git","clone_url":"https://github.com/edx/edx-platform.git","svn_url":"https://github.com/edx/edx-platform","homepage":"http://open.edx.org/","size":867318,"stargazers_count":3449,"watchers_count":3449,"language":"Python","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":1953,"mirror_url":null,"archived":false,"open_issues_count":211,"license":{"key":"agpl-3.0","name":"GNU Affero General Public License v3.0","spdx_id":"AGPL-3.0","url":"https://api.github.com/licenses/agpl-3.0"},"forks":1953,"open_issues":211,"watchers":3449,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/16569"},"html":{"href":"https://github.com/edx/edx-platform/pull/16569"},"issue":{"href":"https://api.github.com/repos/edx/edx-platform/issues/16569"},"comments":{"href":"https://api.github.com/repos/edx/edx-platform/issues/16569/comments"},"review_comments":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/16569/comments"},"review_comment":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/16569/commits"},"statuses":{"href":"https://api.github.com/repos/edx/edx-platform/statuses/c515e5104477e20830bdd21b84fbe9d57f91ec19"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"jcdyer"}}