From 4c7bfb44dd7fe5b5ab3f32abd0cf9ca711fafd47 Mon Sep 17 00:00:00 2001
From: Alexander Kryklia <kryklia@gmail.com>
Date: Mon, 4 May 2015 13:09:42 +0300
Subject: [PATCH] Add Video Bumper.

Fix n-click behaviour on poster.
Fix unit tests.
Fix handler for non_en lang for bumper.
Add more tests.
Fix docstrings.
Fix pep8.
Fix static redirection with bumper.
Fix button in IE11.
Add video_bumper field in bok_choy.

Fix pylink violations.

Update docstrings and some clean up.

Rename edx_video_id in bumper tests.

Fix too long lines in help text.

Address ui comments.

Fix bumper events.

Refactor bumper-transcripts code, fix bugs, address comments.
Squashed commits:
Fix download transcript button.
[74e0c8c] Fix quality
[a759f33] Fix error, when sub contains extension.
[b30755c] Revert "Add video files to host for transcripts."

This reverts commit cf8a96bf84346e17b6ad57ad4cc6a27d7a9118cd.
[36f038a] Add video files to host for transcripts.
[23f1655] Fix pep8 and pyling issues.
[0f1f9d2] Update acceptance test.
[765a27d] Wait for ajax in captions.
[8ae72a3] Fix logic.
[063450f] Fix unit tests.
[d1075fc] Fix handlers tests.
[25d31ad] Update bumper_utils.
[cb5f9df] Remove maxDiff.
[8738b1a] Code cleanup.
[87dbcb7] Fix issues with transcripts.
[ec899de] Fix transcripts in serializers.
[444b1fc] Fix transcripts typo.
[d524cb5] Fix bumper.
[f62cf22] Fix video mongo tests.
[8f1b55a] Fix dispatches.
[53bc308] Add more fixes.
[d5e3723] Fix test_video_handlers and rename the method.
[93efc23] Fix mobile tests.
[740e2ae] Fix pep8 and pylint.
[47cfb66] Address comments, add fixes.
[4e499d9] Add fixes.
[8353553] Add improvements.

Updated dispatch values)
.

Use ddt in bumper handler tests.

Move common metadata to single place.

Fix style.

Update docstring.

Fix poster button.

Improve bumper events.

Fix test after rebase.

Address comments.

Download transcript: use def video lang, not bump.

Renamed date_last_view_bumper to bumper_last_view_date.

Rename do_not_show_again_bumper to bumper_...

Address comments.

Fix tests for download for en lang.

Fix bumper logic.

Update strings.

Update resizer.

Remove resizer.

Fix unit tests.

Add tests.

Fix bumper events.

Clean up tests.

Fix pylint violations.

Fix pep8 and pylint violations.

Update docs and method names.

Update events.

Make /static/ prefix a must.

Fix wrong code.
---
 .../models/settings/course_metadata.py        |   3 +
 cms/envs/bok_choy.py                          |   3 +
 cms/envs/common.py                            |   9 +
 .../xmodule/css/video/accessible_menu.scss    |   2 +-
 .../xmodule/xmodule/css/video/display.scss    | 130 +++--
 .../xmodule/xmodule/js/fixtures/poster.jpg    | Bin 0 -> 13764 bytes
 .../xmodule/xmodule/js/fixtures/video.html    |  45 +-
 .../xmodule/js/fixtures/video_all.html        |  46 +-
 .../xmodule/js/fixtures/video_html5.html      |  20 +-
 .../js/fixtures/video_no_captions.html        |  17 +-
 .../js/fixtures/video_with_bumper.html        |  36 ++
 .../js/fixtures/video_yt_multiple.html        | 101 +---
 common/lib/xmodule/xmodule/js/spec/helper.js  |  10 +-
 .../xmodule/js/spec/video/general_spec.js     |  72 +--
 .../xmodule/js/spec/video/html5_video_spec.js |   2 +-
 .../xmodule/js/spec/video/initialize_spec.js  | 164 +-----
 .../spec/video/video_accessible_menu_spec.js  |  19 +-
 .../js/spec/video/video_bumper_spec.js        | 109 ++++
 .../js/spec/video/video_caption_spec.js       |  58 +-
 .../js/spec/video/video_context_menu_spec.js  |   6 +-
 .../js/spec/video/video_control_spec.js       | 319 ++--------
 .../video/video_events_bumper_plugin_spec.js  | 157 +++++
 .../js/spec/video/video_events_plugin_spec.js | 166 ++++++
 .../js/spec/video/video_focus_grabber_spec.js |   1 +
 .../js/spec/video/video_full_screen_spec.js   | 102 ++++
 .../video/video_play_pause_control_spec.js    |  68 +++
 .../spec/video/video_play_placeholder_spec.js | 151 +++++
 .../video/video_play_skip_control_spec.js     |  64 ++
 .../js/spec/video/video_player_spec.js        | 220 +------
 .../js/spec/video/video_poster_spec.js        |  42 ++
 .../spec/video/video_progress_slider_spec.js  |  20 +
 .../spec/video/video_quality_control_spec.js  |   8 +-
 .../video/video_save_state_plugin_spec.js     | 230 ++++++++
 .../js/spec/video/video_skip_control_spec.js  |  55 ++
 .../js/spec/video/video_speed_control_spec.js |   9 +
 .../spec/video/video_volume_control_spec.js   |   8 +-
 .../xmodule/js/src/video/00_resizer.js        |  18 +-
 .../xmodule/js/src/video/01_initialize.js     | 156 ++---
 .../xmodule/js/src/video/02_html5_video.js    |  85 ++-
 .../js/src/video/035_video_accessible_menu.js | 501 +++++++---------
 .../xmodule/js/src/video/03_video_player.js   | 163 ++----
 .../xmodule/js/src/video/04_video_control.js  | 198 +------
 .../js/src/video/04_video_full_screen.js      | 175 ++++++
 .../js/src/video/05_video_quality_control.js  |  29 +-
 .../js/src/video/06_video_progress_slider.js  |  20 +-
 .../js/src/video/07_video_volume_control.js   |  72 ++-
 .../js/src/video/08_video_speed_control.js    |  88 ++-
 .../js/src/video/095_video_context_menu.js    |   6 +
 .../xmodule/xmodule/js/src/video/09_bumper.js | 109 ++++
 .../js/src/video/09_events_bumper_plugin.js   | 112 ++++
 .../xmodule/js/src/video/09_events_plugin.js  | 129 +++++
 .../js/src/video/09_play_pause_control.js     |  87 +++
 .../js/src/video/09_play_placeholder.js       |  87 +++
 .../js/src/video/09_play_skip_control.js      |  84 +++
 .../xmodule/xmodule/js/src/video/09_poster.js |  66 +++
 .../js/src/video/09_save_state_plugin.js      | 118 ++++
 .../xmodule/js/src/video/09_skip_control.js   |  74 +++
 .../xmodule/js/src/video/09_video_caption.js  | 211 ++++---
 .../xmodule/js/src/video/10_commands.js       |  27 +-
 .../xmodule/xmodule/js/src/video/10_main.js   | 135 +++--
 .../xmodule/modulestore/inheritance.py        |  12 +
 .../xmodule/xmodule/video_module/__init__.py  |   1 +
 .../xmodule/video_module/bumper_utils.py      | 142 +++++
 .../xmodule/video_module/transcripts_utils.py |  80 ++-
 .../xmodule/video_module/video_handlers.py    |  56 +-
 .../xmodule/video_module/video_module.py      | 136 +++--
 .../xmodule/video_module/video_utils.py       |  42 ++
 .../xmodule/video_module/video_xfields.py     |  13 +-
 .../test/acceptance/pages/lms/video/video.py  |  58 +-
 .../pages/studio/settings_advanced.py         |   1 +
 .../tests/video/test_video_events.py          | 266 ++++++++-
 .../tests/video/test_video_module.py          |  79 +++
 common/test/db_cache/bok_choy_data.json       |   2 +-
 .../courseware/tests/test_video_handlers.py   | 289 ++++++---
 .../courseware/tests/test_video_mongo.py      | 546 +++++++++++-------
 .../mobile_api/video_outlines/serializers.py  |   5 +-
 .../mobile_api/video_outlines/views.py        |   3 +-
 lms/envs/bok_choy.py                          |   5 +
 lms/envs/common.py                            |   9 +
 lms/templates/video.html                      |  82 +--
 80 files changed, 4640 insertions(+), 2409 deletions(-)
 create mode 100644 common/lib/xmodule/xmodule/js/fixtures/poster.jpg
 create mode 100644 common/lib/xmodule/xmodule/js/fixtures/video_with_bumper.html
 create mode 100644 common/lib/xmodule/xmodule/js/spec/video/video_bumper_spec.js
 create mode 100644 common/lib/xmodule/xmodule/js/spec/video/video_events_bumper_plugin_spec.js
 create mode 100644 common/lib/xmodule/xmodule/js/spec/video/video_events_plugin_spec.js
 create mode 100644 common/lib/xmodule/xmodule/js/spec/video/video_full_screen_spec.js
 create mode 100644 common/lib/xmodule/xmodule/js/spec/video/video_play_pause_control_spec.js
 create mode 100644 common/lib/xmodule/xmodule/js/spec/video/video_play_placeholder_spec.js
 create mode 100644 common/lib/xmodule/xmodule/js/spec/video/video_play_skip_control_spec.js
 create mode 100644 common/lib/xmodule/xmodule/js/spec/video/video_poster_spec.js
 create mode 100644 common/lib/xmodule/xmodule/js/spec/video/video_save_state_plugin_spec.js
 create mode 100644 common/lib/xmodule/xmodule/js/spec/video/video_skip_control_spec.js
 create mode 100644 common/lib/xmodule/xmodule/js/src/video/04_video_full_screen.js
 create mode 100644 common/lib/xmodule/xmodule/js/src/video/09_bumper.js
 create mode 100644 common/lib/xmodule/xmodule/js/src/video/09_events_bumper_plugin.js
 create mode 100644 common/lib/xmodule/xmodule/js/src/video/09_events_plugin.js
 create mode 100644 common/lib/xmodule/xmodule/js/src/video/09_play_pause_control.js
 create mode 100644 common/lib/xmodule/xmodule/js/src/video/09_play_placeholder.js
 create mode 100644 common/lib/xmodule/xmodule/js/src/video/09_play_skip_control.js
 create mode 100644 common/lib/xmodule/xmodule/js/src/video/09_poster.js
 create mode 100644 common/lib/xmodule/xmodule/js/src/video/09_save_state_plugin.js
 create mode 100644 common/lib/xmodule/xmodule/js/src/video/09_skip_control.js
 create mode 100644 common/lib/xmodule/xmodule/video_module/bumper_utils.py

diff --git a/cms/djangoapps/models/settings/course_metadata.py b/cms/djangoapps/models/settings/course_metadata.py
index 2f22ba400f8..7d37c193a5f 100644
--- a/cms/djangoapps/models/settings/course_metadata.py
+++ b/cms/djangoapps/models/settings/course_metadata.py
@@ -78,6 +78,9 @@ class CourseMetadata(object):
         if not settings.FEATURES.get('ENABLE_TEAMS'):
             filtered_list.append('teams_configuration')
 
+        if not settings.FEATURES.get('ENABLE_VIDEO_BUMPER'):
+            filtered_list.append('video_bumper')
+
         return filtered_list
 
     @classmethod
diff --git a/cms/envs/bok_choy.py b/cms/envs/bok_choy.py
index df8d0b73b16..dcd120abf18 100644
--- a/cms/envs/bok_choy.py
+++ b/cms/envs/bok_choy.py
@@ -74,6 +74,9 @@ FEATURES['ENABLE_TEAMS'] = True
 # Enable custom content licensing
 FEATURES['LICENSING'] = True
 
+FEATURES['ENABLE_MOBILE_REST_API'] = True  # Enable video bumper in Studio
+FEATURES['ENABLE_VIDEO_BUMPER'] = True  # Enable video bumper in Studio settings
+
 ########################### Entrance Exams #################################
 FEATURES['ENTRANCE_EXAMS'] = True
 
diff --git a/cms/envs/common.py b/cms/envs/common.py
index 39a3008c307..f8d9e27354c 100644
--- a/cms/envs/common.py
+++ b/cms/envs/common.py
@@ -163,6 +163,13 @@ FEATURES = {
 
     # Teams feature
     'ENABLE_TEAMS': False,
+
+    # Show video bumper in Studio
+    'ENABLE_VIDEO_BUMPER': False,
+
+    # How many seconds to show the bumper again, default is 7 days:
+    'SHOW_BUMPER_PERIODICITY': 7 * 24 * 3600,
+
 }
 
 ENABLE_JASMINE = False
@@ -645,6 +652,8 @@ YOUTUBE = {
             'v': 'set_youtube_id_of_11_symbols_here',
         },
     },
+
+    'IMAGE_API': 'http://img.youtube.com/vi/{youtube_id}/0.jpg',  # /maxresdefault.jpg for 1920*1080
 }
 
 ############################# VIDEO UPLOAD PIPELINE #############################
diff --git a/common/lib/xmodule/xmodule/css/video/accessible_menu.scss b/common/lib/xmodule/xmodule/css/video/accessible_menu.scss
index 3c283df7706..05739ef2ade 100644
--- a/common/lib/xmodule/xmodule/css/video/accessible_menu.scss
+++ b/common/lib/xmodule/xmodule/css/video/accessible_menu.scss
@@ -22,7 +22,7 @@ $a11y--blue-s1: saturate($blue,15%);
   }
 
   .a11y-menu-list {
-    @extend %ui-depth1;
+    @extend %ui-depth3;
     top: 100%;
     margin: 0;
     padding: 0;
diff --git a/common/lib/xmodule/xmodule/css/video/display.scss b/common/lib/xmodule/xmodule/css/video/display.scss
index 8bbfdd1b258..eb7ae841496 100644
--- a/common/lib/xmodule/xmodule/css/video/display.scss
+++ b/common/lib/xmodule/xmodule/css/video/display.scss
@@ -27,6 +27,23 @@ div.video {
     }
   }
 
+  // CASE: video pre-roll state
+  &.is-pre-roll {
+    .slider {
+      visibility: hidden;
+    }
+
+    .video-player {
+      position: relative;
+      &:before {
+        display: block;
+        content: "";
+        width: 100%;
+        padding-top: 55%;
+      }
+    }
+  }
+
   div.tc-wrapper {
     @include clearfix();
     position: relative;
@@ -169,6 +186,7 @@ div.video {
       }
 
       object, iframe, video {
+        display: block;
         border: none;
         width: 100%;
       }
@@ -282,7 +300,7 @@ div.video {
         }
       }
 
-      ul.vcr {
+      .vcr {
         float: left;
         list-style: none;
         margin: 0 lh() 0 0;
@@ -293,49 +311,52 @@ div.video {
           font-size: em(14);
         }
 
-        li {
+        .video_control {
+          @extend %video-button;
           float: left;
-          margin-bottom: 0;
+          background-image: url('../images/vcr.png');
+          background-position: 15px 15px ;
+          background-repeat: no-repeat;
+          border-left: none;
+          padding: 0 lh(.75);
+          width: 14px;
 
-          a {
-            @extend %video-button;
-            background-image: url('../images/vcr.png');
-            background-position: 15px 15px ;
-            background-repeat: no-repeat;
-            border-left: none;
-            box-shadow: 1px 0 0 #555;
-            padding: 0 lh(.75);
-            width: 14px;
-
-            &:focus {
-              @extend %ui-depth4;
-              position: relative;
-              outline: $white dotted thin;
-              outline-offset: -2px;
-            }
+          &:focus {
+            @extend %ui-depth4;
+            position: relative;
+            outline: $white dotted thin;
+            outline-offset: -2px;
+          }
 
-            &:empty {
-              height: 46px;
-              background-position: 15px 15px;
-            }
+          &:empty {
+            height: 46px;
+            background-position: 15px 15px;
+          }
 
-            &.play {
-              background-position: 17px -114px;
-            }
+          &.play {
+            background-position: 17px -114px;
+          }
 
-            &.pause {
-              background-position: 16px -50px;
-            }
+          &.pause {
+            background-position: 16px -50px;
           }
 
-          div.vidtime {
-            font-weight: bold;
-            line-height: 46px; //height of play pause buttons
-            -webkit-font-smoothing: antialiased;
-            padding-left: lh(.75);
-            @media (max-width: 1120px) {
-              padding-left: lh(0.5);
-            }
+          &.skip {
+            background-image: none;
+            text-indent: 0;
+            width: initial;
+            white-space: nowrap;
+          }
+        }
+
+        div.vidtime {
+          @extend %t-strong;
+          float: left;
+          line-height: 46px; //height of play pause buttons
+          -webkit-font-smoothing: antialiased;
+          padding-left: lh(.75);
+          @media (max-width: 1120px) {
+            padding-left: lh(0.5);
           }
         }
       }
@@ -504,11 +525,14 @@ div.video {
             background-image: url('../images/volume.png');
             background-position: 10px center;
             background-repeat: no-repeat;
-            border-left: none;
             width: 30px;
             height: 46px;
           }
 
+          &:not(:first-child) > a {
+            border-left: none;
+          }
+
           .volume-slider-container {
             @include transition(none);
             @extend %ui-depth1;
@@ -686,8 +710,7 @@ div.video {
     }
 
     ol.subtitles {
-        width: 0;
-        height: 0;
+        @extend .is-hidden;
     }
 
     ol.subtitles.html5 {
@@ -792,13 +815,38 @@ div.video {
   &.is-touch {
     div.tc-wrapper {
       article.video-wrapper {
-        object, iframe, video{
+        object, iframe, video {
           width: 100%;
           height: 100%;
         }
       }
     }
   }
+
+  .video-pre-roll {
+    @extend %ui-depth3;
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background-position: 50% 50%;
+    background-repeat: no-repeat;
+    background-size: 100%;
+    background-color: $black;
+
+    &.is-html5 {
+      background-size: 15%;
+    }
+
+    .btn-play {
+      text-indent: -999px;
+      overflow: hidden;
+      border: none;
+      box-shadow: none;
+      line-height: 0;
+    }
+  }
 }
 
 
diff --git a/common/lib/xmodule/xmodule/js/fixtures/poster.jpg b/common/lib/xmodule/xmodule/js/fixtures/poster.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..7f987f944e2dc670f2687b216cdead3579effc8d
GIT binary patch
literal 13764
zcmdtJ^;erq^f#IcEwogCQXCExDPF8N9GnCQ2SSk0z`+UbTHv%rinl;;Y4KpeDFiu4
za1S1&NP@fj<=ppuzxS8>7rcAcdS>R?YwekNp1o)9`OM7i)a`G;OOPs16>#q!0C4Xv
z0dDbtU;xR3hon!QJR$vG^nddIUH#uLUy_rOKEGp;Q$Kr7K|w`DMf&VD?Q2Tfmy}eL
z_ee-cNFS5FASHW7Nls2q`Tt9=?&yy3U-m!5%=}-G`PJ>e01DE3w)cKNxW@*#PjT-7
z#l71O0OMUBN$&mE)&HUU4<3>{dVG)cj;r_*aR1)@hYub-AR)c?-~q{hBf9^9;vosE
z&?8C(Henqz*H2X6e?ESrSh4p?MAzJ{b!0J)lsen}^S-DOB)*dUP6qv*^#7Osr-A=u
z+%<8><D$50`Tji;!2SF8AN;RA+`CWl;2|ZekOG?y)hiNVMc1Fyw=)3pJMJBW0w51K
z>;C@){oh%7@xz5;>zli>;cu3|%LeGi+XQ`FPh`K+Ed=<*N%$35cW5rbbBy<5KD9NB
znuO3$N^4dvI%uK%Uo|S}ADl`XpH&=<L6^65&66BylKmnR@;aMmjwL3tM=7>mZF}|g
z>Y9@0rd<xX@$^w*9x^tF2d-QhxP&2{FZKcbi)IZ_%E*>aToS_PH*Tmlz2%Z^o7(WT
z>mH{>t*S<?<c4@hxej=CbJ7J}?|`uKzPKaMR_*LNTvTG$>A*G>VQPE}_<p5rquq84
z_h1`~TX&V&b;`V_;Rcs_;wji1yLNT0$`ng$=mtF2JXZPG(dmqh@XS9V;zoijAB?RF
zzS3&dQd46N%4^htldK-!0+i0IcL|%${YAq=j|e2XqBrdC5wN}5ro<Z4r0-8DRO$x6
z!XGhh7w5mqV#_i~nzd4TUwWiV{$$^_>+zevX`*|~@~J2b8_dOA*EAD|&v=VYUDe&{
z*<Eaybc(ARcui*>TnIWKq_dC4PfS)t#I2QOq`b})E`g6O_~+tIn69%0Q+2j_?MAf`
z$F6oW(xv?oje4F4eClOsR_2##l&Vh6tX2MHP_FpbIA@jKD=GhH^_b22N4TxNaGT)L
zy&@AmJ87}NO&FYMFj|GZ;B~rXyf&6a0O#n{$mcCzRni7dKnxrz<gO1im0?OM`ecmX
z^gWRgB`PVhOR|!;rNz<4v8!9FKq%=_*woF{vCp)giOPz;A3;L)c`jR0u!l}pEvQ?7
zd-cBH&O$x5$_AfwEI*KnH75l;`ugoVgfasC*S+;zNuXV{DS1cl=<I~ch#=CQwY-xx
zrpsK8HBPxmb7TjC3&@@n@ki<<Azptk?w$w@jeILCYFqekC-@s7mQT9ZpOpFIf#19K
zUIF;r$sg8{Zh^*kIkBL&*{%JLspvH49rf<_v;*FveLC&2JJ%6gyN!k}3$A`IMVj|V
z{jBH{vDUneO&U_h)XB{U$3ie8i1WANE2~oGMFY9>_YH<iES6zOz(ZlHz{RJ6yANg4
z<O<#`Jt=~6E_xSZMkipl9%XNe`@p(;YCIhUytSv!DwF-4PRSjQ`Ee#U541k`mC&f6
z;qt%c-eQtH{MH3xmh5<`m??F4R4yH)!+MC9Grmtf#yZEXB!H(DLCV@~E6FL-ooQ7Y
zN21eCk6r@V(PN{3zwRPG_gvpxTF*C`N@b*rLZ(IqFw~95c71Za=5D_Q;9q(2G3v&q
zu%({Y9Z8tvaMgOyO7i|r$J&-}nAqj=A38vp>AF<r4JE4^8DZz#@Qlg?rV`BppzP>5
z>viOXXV!9gi+${G;c!tTh%%(CI%+aDUMt=zVaU0$22&=4K%dQSQnW(+K)lmN*N<)i
z+a5Ex6jLph3-Rl|3$C-qASdy!izvA*#=xL>f9kWX&$-YA9Kqkzy+(sIWViU3LMAlk
zKqpWt$X$L${(Pj>CVt!re<rJ*67wjJ3m<7PJ&h*@kQlK4Z1{}pq1CRDY350{Q>*B{
z1-#H})E}B%V9}ZLIP_#9Z)Zqwr$?aG&Y7q_xvME2t$J(aF0~-1Q7Y$77Ui@-;fTWZ
zBEn6^<is)(4zuG;nf7MU5cfAF`dbjD>qdM{xS^lpybs|Yd-z%dz7u<^+0&}ys&jCu
zQtlk18~qC++G6`!-Aasc6c(altf-&U(fyI+-PTH@^eL(3{hce)to3wIajL-W3z2YZ
z_<0aS+MDv`uJV-_aDEGbh(!$97KHs^U{h342dWhw?K5Zp>{V?Eod^Ym61zfQ&$q(t
zT!t;EHOq6WX*;_VtBf=Uob(qjv1JZ*JmpQhli|ERwRGejs)<qLH;UD&i7{P_#rYzA
z^N?|hz9;JY2P>zpOQ=3fa|byNCC#^U`SMUNK&|U6tG#Jd&i>cUpGR-3W2^P)GZ}US
zl@$h0xm>#LRK03`AJ3wh^Vd`{_B08FP%^Kgn>Q)oS-?gMEE+&sF828m-cM5J_A9Im
z>bDgCJxvjIuGms+^-hM_L>3k5#wRw;rTJDR>u2sdg)<38>7Hrz<t^Os5g{hh9>><g
zC+ksrR=88=W3zr)4u4}vFC}atf4-!)`<=|-iK8>8dEM(26FOCz@(s{R(f1YD%ZBvI
zm@~%ww5i&EC2i~e_#5>e;nq)8o0{&+yLNOo6Q5MS?vWJ1|E_3TQ2yp2q^-JiHz==3
zzEzs19fNm-IO9~de0Q>xXv=d)tBxvbcCN0%^{5sGuf^vmxWzFrUVPWF8<c$^WWm!k
z4}Ca1*5wqp&XypUYfKzJKBsi~g{UcUxb{9|YE$8b!+G8L#k4(F8_ub0dxbvM0%f5y
z#K1to>%{%-`pj>6z|teOr9a}UHp-gn1YD7C`p)G8JkJxxVNNF)gF57(kwyM#HK_cY
z?zvQJQL<m1L1j}*Yekv0PsV)0WCZd?DI!pZxFNV`5w|y|^6<Q-x^!oJa)PT-;q4%q
z64<!raPshi!b~gAz+i|OVT@&85FbGzg}{HA2)TJ5Aj_J=rWV48_SpenRf=Y3u9s0-
zXI`uWLEAfC@7)&49i&}Q(6asyIW&vhZifLBV=G4M#G&c>XB9o9*7lsaVyx#s!#~p}
z_jl!r3G4qvJ?nN;q(eenc`oaAT*+~#@i+P2%pBLaBb%24N%PtlSwyyucK}{X^@G()
zN}cr$8ClhD$Ik|W$g5aXHtc=uj499bd%p~Wxy%ZO6w*~30Y3*DkhCVJn;n${MJ5Cj
zx|4AI_w}!8MXTwM-GppW4{Ns`9DRDO#C-7Ee(x-$uAWFdHWkAYkYz4_7(*hx)z3@3
z<?=p$H332t=n_w5dvtcpYp#9h%?cL-S1V|wVR1gjztjdN3V`bgQVi6|g9pcaYYzGr
z-X?{s#%Go7a;*^4|Ma)KsSQoFaP9NSjAQELk45SJ^vmWBMseQ|o95yD3Br%elZ2K$
zkE<x-agDjF??G7cvt!1xxtZuYxA=-0O)I|ad1!B%Be}9IE7RvvS*$C%ROfA@RPZH)
z^le##_|lg3FP0a*c_yae2VI?gLFg3(LBPAL50X(9$Ls3t$GlkHv*{+_8UX@h0~kkj
z_JzFRzqX=dj6)5K>C;IVY?}t%6K(oZ?6Qc?XU<~5qmH@TjzJ<rJs|;dX-O;^7orej
zt|NbR>UfJeR7MKMG2?a8a!FeNlkU_g*gW9UNv*jByzcSY+9pgAU2$t25kAs+1!bw8
z7hBTW&nFuA8%iB!U>|#?e7hD^IO}tk363<S%{lLoc*h0LRga9On9S_8aZpAY|4dUY
zVGmQc(N+%eZqOg?DF~2p8iFz--0%S<wctt2-YtM5pXOd(3A$>e)T555H8YQ#s@+~{
zbW$MH=IzLnjPOgnI_s8L!=3wY{<h&sRp`K^Fuy4rzY;SEjiu7<@86%GYXG~zitC(;
zuuCoR17qjy?OChU>jz^fE$?hx-S3P^QRi{dG?Qjk&KI6braJxX^}nyuPJ*_wi)lQT
z(wd8UcUNKyK6Vig_TgPyB2RTQDRYv%JSBhL0;q#f!$$D7;b50BsKfyl7hF2A!;QI2
zRBe{2hvVE{cCoW&vle|3RytG?{!@fCB^LNhi>}C8k#;3m<VnF2^i+E!T}}LYr3IDI
z9!@hSuj;b3a2j`$Z=}!q&%MGm<>1I~-((;9_(ikMNT&|c!`^VHxkK8c2K3QCU%cw7
z?>_t7ZYR@@f~#y8SyRlG6x8!{wy-63Mx=>I-s!xnAM?k}+Q-YR;QU~#KN6yh>Wn;0
zz-7V2z2TrgVE)fm9Su*L?c_8=tkW_ryXrG<0m3=JwxM#7Onx;V$S?6sCy&fzg`5iG
z=v_fQ!;z`7E~h3<{Rs*0DV}}!EnqU-CUl27IrGlgBFY06%$uW1rp?Y|BW41KkK7}d
z>d#Xr@MkHS=8cX^lfyHvA!3$zgXqyoICR_Df|a)5ZRu5D7+*%k5pI#Y$OEBZJI1z9
zf?ODn<2KJmrh$97%9h-zt%4plf9;hqhB(d-W#fkl+mrT;OGTK(h#L|PSmashjyz9X
zdVP_i?q@J49bR9Wj}A0dc%miX2}ZVj9^5oGnr(pR6qQPMf(ZErnR;J97pxIR(PUn{
zW)@CWd0cc|16(<**0esjq`98Kw5kt1ovsZtI(`L2<8{>1j#QvRhZB%X@C_o#T4;3J
z7rx`d*uwHwbgX2c?uAX6ma5iPlVD(2TKwbD^@}7#o4M$^{l}TQla~Gu#dw?M7J~E^
zWM#X-?sVF?=a|(-q6Zh}xb9dBp3{~&xbPNaaBz{BvOP~aG~T??P)Q$+#Gqc?0#HHj
z;np8?e|iqtA4Qs`PW2EOwZEpP2A@Ox7fewLjP?UrMnVB9w}8MlS;=KCM@qZRsjw7_
zcLQ$qynRsTt)YwqO(%g3oLh^5Xx-7+<nIkV1ot3#W*Rhej<NetuIYzK>Wr7r_p^U}
z6sY`BDGGJaXS|@0^wlXXILp&S78Zz^3ZoC>7k794*qJ53QzE749=Ye``0;BBT)nfz
z7f%RA`|RfM9om{wM`Wpwr(N9wT8$W`h!Y<gXY%iSknzW+p`Wn@tM&3Tm=^MVReF#0
zw)B+^t-Ri)UD{Iod4N``#O}0!jiMNozP!elSK%lQHgox5E7qa}6DttNjUqFj3XQ6C
zvJheZHr7r1Z7j@M{EvLg(5bD^Tr;SQ`px<Uqx7B8+<mHyuJEKed8IJxTR>yhu@L>&
zzXAH)-wCmnK86o)?V~??jZ-t3!V~lA%NLU!Y<Tl?ZCSMyijE>ddLC4;&@g#|-S2MV
zUw#R@?%HW1v;E_+DtG=)&-{2+R7HLPKe-d|FWh1gV^z#AXfPW;jzZNkGTqzj2#(lM
zy=3RxP>xE3-}w#O(^I)Ggr*)6qW@KArNY+ms3;@H97X1if1B*W<To3923J1ym>2K<
zC0e&L1{)|J^nI{yUz%)|*A;#jUs;9jTvGqa+<RYs#f)|2m<c3A`RDS<2N556haa4q
zK%J+7bzb-*^`Cu+`58L`QvlN~prU3eH%|-Kpj((48OUq8ef-wPMj$O#v?Pa2EC=Ig
zH#xS9JUCjGXUez!z9lceXPs%l4R1ApzrI^08plQtPuzcxjFU7jaFX#aOF@j=Bf$dc
zag|;O`K6d*98tY(08^%}_hnAZ^Co44BW`gMw^B-4({!W-!WRq=5oJhVS<cT4Qk;8a
z<qwRL)8m@5#`;)9dP*X>TYFs+_DZFTvAy85LsvJxUzed~;ik+930%}xB-)7r+(!Hi
zye`QOp1j<;#K|NeudFyf&sf=yv0dul1b;un*@5CNvGpE%odou!qc|_?y3a17NR_-}
zW%72?Je?jpyx&;{%hoL*Y=Pa5<HjgP^O$zs$H{1Y<e-F2?~Q=*wC%Mh;ws8uhP>!X
z67&iWEEFFRuiUR6EkCo_tEa4q^V(<`QA%HAD9~|3d1qu9cO?bJUS)o8stg!3k#GHX
zV>D!2cV?HmWf;z9Vyrb&#?*5QSg$+8jEz|A4MTvP+^SlgG8K^6_nWDct~kf9$u~@S
z;aQ@t6glr}qHX0&de<e+eR8Jx@XLgd(+C`YPMrCh9@X6P<YF!wGI0Dh$J-OmIjLA<
ze1PgLU`K4v^NrfeageT8-q+~S_=QAQ=PJSc&u)Pf?IQ&+!`QHJH!X-qDj_1Kabssc
zqgzk77Z>|)(VT;nogUsvj9Yr1AGq_~Z4|uibm_3RT%Ub1&hLTIh8AL`Ka}*od!{;>
zkRUDYDBA#+)gZUrCKJoco(X)t;ktdWJ?DUrU%DK(yag~43AX@Diu{h+$G8YxFM9-s
zzYpAp(p|uIP&=i$jGt?KAw?r#KG$|qA1u1=?&$ATH?4YN`UWqf*42CNDAy+}2}RRi
z9Oz<J4dA7vXh=;VqS)Gf>y-aY!jLmA0WF@FM%+IX?X2(>!Q{_ScCoXmhd<M_v-MNb
zy%!$U9r&X^s@mGZ!Ofg5wJ4MQF8Gsha%(;b8XH{gkjDhZUNS1lnEK0aSm1Pd{{g&L
z?uhulBnANZA>ymW#btQ4mCfH>=J>Wfb+!l-Ecg&mEAde-47t~_C04>ysTSMN^j=k3
z`e%;N)RdhE-^r8yR-#UI%KB0>^G9Nli5ito*r$v|0;)Ji+nn&RB+OPJE{?HXfq6`g
zr7UUmx$Y5t|3zf))Vy^7lU(5Z%FrQmf8DhY#evyXm%HL=aP0}>&?6r;v;O@JCqFY&
zHS?Bt3$t0u=&q)Ke#0@;ncTW@<nG7tGx4cE%opruZM9>)r#@-TXvOE;VFF5xJr;%w
ze4(DndMJpu<y_L|=FU2jP`yG(n2Fx%D!0Ah(3*{q9wk<l%$pczVt@%^9{>x$rVg|{
zcZN(vLnKU=ToU3OyaZLRNPRx2*}IG5(2p<0CHda;);I9ZHh9GAix@%uv4UQ385rE7
zW{5wwsViYrPA<WZ!*dZg*0nDNozvFWSaRYjP^)-84wR)qC6$Juqz%&35;7M;nBKJ#
zS67lcIl2GNSW0ZjDc^%LD8AeS-2&L%Z<yLoat4Z}7Rbz~@F+={GtXN93wE_W7s;R?
z8*hp?N7IDGR?XJ+|Ja`PGlmAxTypbHj1Coi>XW!=nEm@#U2u9@21kEhX_lX0aa4mJ
z?fKvP9`Fnr*weuHTLAgi5szX(Pp840cx~-fx6f9+hNFzf@MescwqRD4Ml>eI6A4}2
zDq*tiIm79g9{ACSy6Dd9{H5)YC356v*yO9Fe2JGGT_4WSew^(njlmCM8>7HUC!IHa
zv~!&@Wfypji*F(!es0mDjWAQ~EG3;(#XhJFDt5@GsZPhy{wyyS+Hm2^uzXeAH8$8|
zLDiuX9e)Mn=4_#nBD&szZG1Lg4#L7a+JxX=fqH3cadEDZ`)lf@@k>18RbU>s8VRFA
zj2AR##2B42c}5e$`XwJtz0W?De^Jk$lr1~I@FH$KuU%&)*zAg1@W)TLmjea~DWu<)
zU-omSJnQzewNuvfjaK)vfUvbg7%bu$tnG@<B2S%%7tbemxvn*LWd3a4z6kpA)#m%<
z-TVZ3reXoOUwda5-{NBbd`bOccy`t$XR3YiRD6~dbaEnTBO}J(AbLb2`NGU4^*OXH
zGjM8RQ!KrSF04N>{}v$g)3Tr05hHCzSHl}NpI;dqRq&!z3rZVR0WTOS{-Ia+?z42t
zwsf>rtb*h}C1^9cPrJT>Q9t~JZvk&4!7EYN7oV4d^QM`H9!Y38Pkw(RtEL$sbZh|p
zCbV*)%;+02YVx!}q0>8Q6D3YE$7&(n{n_CT!_scl{JeGfa{cskS8`NQd@-Y%{gUFg
z{j|51<IWo@**<cm;X-Wx!9kB;SaA%V+?*Fixo;&$Lovf{4Xu8Q(626wf#GqXu(A-O
z`Dh5fmJmec`T>FMeC>|NjTiRW+R%yR@v?TRWrM^gWZ<b{H*r}8sKUCw;@&bg5fpFj
z{7p>vf?1IQj)1GG=tJq%<YuH!VfsY!S8})rftkb-*X7m8ma_P^Bgy>CnT({+ca61?
zl|+QU(;!D%N*Ds3*XtShe2LWEDI;^>`nl=B!DSOuMniNUf;4|rzjiaPzm|u->7sIy
zF1fg_zXo%jV3cVOk`ITkA^(FYE<_}`5(T3whMvMQ*`AM}#K6^l;*DG@NA@drK0(q2
zMjV_qetvl6g)k+ulcZ22h?)Z}wk+lpBaK+IKBJOfsga7Sa;{v^Z!qnsqv>zFSefj-
zc=R&TO^b_xgBl0lcFroF5QvEB5!}A08_w-^7hS4}Z*G=;om>KPtr;|ujjQ~qA)1jC
zc@ec&1_hNOK)J8-Xic7NA~@^e4KrJZa^6YxA|V`a2O-An@uxv+NxW~mVnpP$pfL|P
zazMEoS7Q6Qmk_&yy3fts>0Lrlv_x~+`q%GAXTm#O&V#pr_}@1I_EDEBDP-oUl!NF@
zjhEO8zTVVAG1m2{ZltEBokbOW3Qy`c9CI>GxM=n`0Dme`K!K|PwuscO+V4zv`s8`#
zZGgsX{-ym7p4C(t;5N9&_8kJGwL%U568t%|mzE*;Y45bAG0H}U&$&ESS)invWAR-;
zf<W`@Zo&-4LLw9<l4*_B_Xy<r3`}1To68VNwa)6^m`-gJGfv(raEJf12Iq0zCMi5Z
ztVxFCm)R&j(yx~3H`nau=9Ek~8`V6qZy&00(rKyZeW&zM_G#a2)xcVAa3)ot_FjSq
z`)Y+*t1Ysl$Ic_Zokh>kN4)Y6>%R~iAFKw9b)S2U9|;ABar=6{Ui!030mZh=l}g~P
zLI_xGXvi=C^HhGBc7FmfVQ6UmA{Q^I^)>{*f-3Wfdi2r#^K#Y&LM7>ZmhSx1#0~a^
zu!7HLP;04tJ!lx8@rJQ^W(b%qATdU09Se-i=v;j9$RBPe{<fw_YP)lY%Z8?cP=Y?7
z`O?9k*P?a<9DI7FSCVJ*dw#IbAAZ&Am6T|6yuKc=g`R9TeQoDs5x)N_jj)>LY;R1w
zcq1KWh5%`#_Enb#ku)3!%??|6qRckH&`IIKWe!Ws^W8)p?Z(~GEOmTYe5FA_Y<xUf
zz1{lqq2`CQL>>>sdLKB6r@`+uw`_jSFExfKTEcDK4aLoQUIgrQ0*luyXqet`a6Kb>
zAZ-<TS)L>mf_Ub3e0y~l^mCs4WpZOtEp!CQUly%Ci;HYA&$V3OU(k4a3+PGh3q|0b
z7fTYiJv#(b<Et<T$6`WSd}QTO#Q={PLA9#j?THPCe%fA8-+|T9VKdO2Z^re^rC`LZ
z9;$n;(mETT`FmOaa~#2~eSmYk?5D2G(M;TxI7H+MqTv^FRXgfJls&+TXM=7yu0OWI
z`rIFXb7yaS#>nm?l4%NwO`LLPlCCzigxYtdEsmUhLuM0vih=f3-Wz%biOr!cVJNrZ
zFaCpv=i5i>>#f)YPZOttqDCqVi%t1h-h7tt5qN{QZ8?td8(uC`qTf2_@M?D2jo{w9
zGx?e1EAMw<O02rZyAevdZ(E`HAlf@*j3~s$CoYsPEmW&PZJL3`*t}ly%RmW=)Yt@J
zY+fwU0>`tgc7LlxxV-v(#nO}M*7Eqk(l-1KVPmCKO-SGt>ASB0dFP)2`NvMh?UFI)
z1+Pf93)HdXZu#T2MeDcGZrG>`Oh|C`gDp$+Lnl@Rj&^kRt%amF+-KYJ@S?LCrU9Bx
zY?T-(3opI{Z&XPuXhPWc2}U(P0mch;1($s}GunXa#&a#w@sBce7f-pr@0efvg3Kf`
zf#*(fLr0A<rMX%I_Or#k;vCC;m6vdrXe~0MD?F=9Vn^fNx?reajTYV1mt?I<xbSO<
z)qpz6J<5-{G8U7wNlB+VR1EW{%G1a1<F{U#EpYv{^KE(IA+W1=1T(^~nIHo4oYM}w
z+g(T!EV2g*@^>%TrYkkSj%qo!%(~$;4t9M`J>~}m^ZS;X-^3E#SJGnD$SZO?#NCRi
z9(#RvjDo8R$Dq>RjOL?*k-OGItSVb0g=Ywmz>KUMBFg$q!z;T<%MsIcti+g^i(EoB
z0zE1l;cpifdtJORpNU5eYg)Y(EzfE=e45y^R<|65$#sElHE)3z+vqbf-VvOuS;lyS
z`<b~pruVP7eeI2uAfOJ4p^sWszcyM@>j+p_jA3G-2x&9;9PJ=0RjizzjiwVt+t`;n
zNhY`uNxaQSd1rdX?Ke@@(g9?{H^ujo#lYNVP$^u^E;#s7u(e*fG()Bn%@abV1D>nL
zVh?4H47M`zexo2xqHlb*`<Jp8G8*1Yn_1Y`Y<;NVO0^p4k*(Jfpy@oQGpV-BKu=GO
zE|pKgW=C^|1XNL-lYR%@g3I=6&WDC@Sy7eu&@ixD{QfQA_p*DXOuY**&gALiKI-jr
zDun!x+123_2$0#BxgN#bmWI+5U2dE)i9`=CD3WndSL<qw@X`%F{TJRxCDybIx?zdX
zI1nwlU=nA}UwB+b+#Ra*3$CDi-JyOY!(PGnSK@t3*2r(6R%Ulfe-vs*S7dGhVM-qB
zzT3{2+UlI<NkMA?C7Sd216gu7@xT^k(`Vg-$W&CjXi%s;o2<#}oGYPo`f$=XdU(b0
znQqcv*NQ@En_h!Tz_TX9C@=v3LXNWF>aBSJnNs#ubYH$WkCI)hb_mTU8|0#unsQ2B
z2Add6xmnp6ze*H?u*eH57k)kCRCSif(a*>7Rf3wWkB1Ma_GF?RJ^Bc=h|xJ3iq5{)
z#1}7C-kg~6(B&i>yD7UHI|SeKH$J=>SOJYX>H8=1xARb^yeai<PVN=ww0`&@&kof8
z1d1jV_VW32@qW9Un)+%0ikpq|@9=8%l<$$-zk(jzg+kt(gzHgGNw1{B3y$HPOS(+n
zfeB`Q?Da}nc6Gxc=jxS`8kx4qoIXYrH#uQKN=WInfl!F+=8SJ8j=(87%970^O^V~)
z;xX}#bxX$_E4qYIu;~y>|35wa2fvJat~CvO=9Y5Y9{!LF#d5Rn@c5@Qf&V@RYGD0^
zHxfzoV$*PQMj!INOrxpP;>M=~YrJokF8n0Qc8U_~Ud&Bn=Meo(&Oai2A8}?@+w%uK
z9a&!=o5gsX*2|M93eWLMb8dUZOL|XYn6ntxV|pv-qQ(29TvK6lJ!N?l58ihuI`+of
zfiL)5MhcAd^e3zQxNIyzMtan_CEK-M7Y?MCvFR6)q5WLa0eFordrG+eQNTlxqnxpz
zax6y<W-hspzrpTWX@EJ=guF2F41xDDS|-LhCb(ybv?zlP4@|jz5Ev5`!Pho+Q7nSH
z9G)br%WGjG>7%dyAaD_=Cv?}o5AIK)CJQ(80wc5Glu|DHPCJbmYVr=-nRs+%%zNaM
ztSa*Y?h!P54j1ngPn;h{HXA>-UAK{en=UKph5j*Vzw&)XuJf#T8F&2|y=$v_L?n3Y
z($w){T7-vBEj$<Z54$$Ah3cOR)u*}n1XrkQykZSEPY$(4QPC&gP3P9A@Mujh3tek{
z`eYnaGEM~gdp3-AHW;3k9R4&p^ntsQ*vm`$)O(@9)spNSKWl64Txn7FA&NY2*q;8A
zbYCy~{SqjjQTx^7S#3%6(0Xl2wR&y(w>IA-A3OM))u{Fw?pRh^7=f4TV<E*u=5pHg
z;=;;&S#D02dc^`S_}s4}cK&<Dd`Ai}y*y;sY!_y>5t~#J$LqY|+eraSERhaxLF}OL
z6GWlMWWV#*v!6Qo>%(!L?4<*v;tl8N^Nj>f0w><D_M+B$Qd8*Sl=>?YC^-n$O)RZ*
z{qaEB%U=tg9Vn1wExcE^qSVf>f&Ma}bwO97-nvB3hcF*Mep{$f@QGEEU{M8B%s!7V
zml!lMYLfZ*J+a+<_Tz~7GAH`AYDGg?_!q3I9lspr#_C2*2e{z5xa78#C7b|9&y8~}
zUszXh57zsBfEld+cJHGZ{Y^v;BPO+`ZIMgp!<)7A?LNyI<1>?slnA;K4g@dUelbAi
zsly`#oywu@O-UbKBy2HkGNt(C{-}n|Me<tBeBIqTByF$?%GL9@z~Ev7C{xwb!>%?U
zBr}9`sAKEYP{-N{kx5Fc*bRoC*tCsg7!Kz8BIRb$m5KYYrN>f>A-(I1eS%Db&L>0d
zZtZ`+qb4?Al(D%LU37}Q?ZHLvjeMLQ{K;gHpP6Yh(sHa4LhNJQzDzws_;HCnz6Bh8
zTvuokh&MIWR$3!;y=$sCsA`%-y!>2u%JA<JkJ%;!4Xj=t6w#71o}Mc{lQsBtM7deC
zr?^k+WMz+UzoP6Ey?7CP_{+j&ucJ-ii9ytl)2O9wj=<vn%J0}4?BTd-<6e!;+qQ;?
zJeoNuBxt`uT3V%ZyPdn}W$0V=lDG~b2PGlBaPodXB{5RE<o+-_^6tLBW;>@=I(wkY
z4Bsj!|8P<?tS2>7XNY4oXMfcnJf#;)O4-rNw&gQS4rZfgiDR%GOu&6jdtU7`JnYn!
z$XG!L{qpf=uRvGrALyP_rv8Q~D~4EW*&z7yaV!JZF4ZBY0YSLBsa<wpO>uod(uZH8
zO6xIcQ_lyjn4E{R8>(^_={ZbTG3YV~oc^)yJmt#7OcFDA=ZWH-4+VWC@Fhc$Kc5!y
zrrZLk@Ke3kbaQ^Yb>h7Z{4_nUDiZR?EQ*~KJ0(3599HTxH}je_@%Gi7yqg*+bC(nw
z%2kPUrSzw4leuuwe}+iLzJ3u^Z&f`|4SUAiqmXnO%}isF@yc-OK<iA%71>ii!Mw5Q
zp*NbsB!m(#FM_5{*_}S@$)9+&Pqwpl75?-ciYcQvH-z`dfv={t_v~{o4w<^S6zL%T
z3hOYN_5Gh5u-o)|!{6m%47Ht35}4(l?pbY_^mwg<P7{WU=YQ*Mh|%(z9{*$s`{jv|
zG?{U}8Su|!d8{?&<)$%zLvbiEyRy&RC{fvxsJ!?t=)qzcR7MN&Rwv}d*%y4tUUMQH
z_JAONKNvFMzC9Md$@#tirbqZma_SIExK=MrH*zLJkmPEp_*mM8i>Goez$Eeuq`5Ib
zo3F0c&t351OFK7#cJqc*G5fecm-42oPr@Mk+VEm|`|}eclfP42ZUL*Pg6x(PIrY}+
zMHZC{o4}k|G__`woc?%o32fgW(An&r<NADEH^jNPE?5Uk!DKXwdNPbGAmhfC#YHwq
z-2xt8|I|U-8J5AsOulE_>;#r>4QiOAx_+!Mae`4F1Sw@S<aHX1q4LX85yknnw!n4?
zkyuw+ebl&Hsglj>-M46h^MUkpm5jxG7wB6(-TjbBnV=%sXHEuBV-m@gXjsXSq~8V<
z*2%|&?}Fs84RZBpC3gnK<mtESDNm@_9<BTrQmB44r>*7qCc^G(2BB=*{{0R>!Oqqm
zr9uC!nzKEc%yP6>C6;9B083W!`^O*oeRgl>dU6}P>4skF8t81(edrTOuWqVu@L{X0
z(Q;fwH4IAd3!NVu__Q9l`}n*<RtOqn&MK(6P|U?|+-$+6owFSx{~|;LkZIn)>ZP)a
zN(Z5fX^bEdPlQCQ1}GPejFjcpo@xOD-ZOLz6F-bh4(Aj|JIn-v7rvVXcWLa;6?^Ac
z@ottR;uhBIEDL=K4WmrVZHvCUN2hLb<YgVI`FI>)X7fuQWu$T_CDY7^PLX(6mG45d
z;`N%MM!2ZeD*FfgK2#n{IX!(As+ZxQL@0QjGhkmk-@{RC^@#UbFxEsO&`vjU%f0O2
z#>se^rmEd=0!R|AETKeOD)>pV>kK6rzH7VbY<4A}nt_XKfaTYi%sqd<zR?w1k1Rm>
zCRnjh6k}L>X9e|d0p1+WWNSUs)@sQLCkUR%bFSC@galj}qrRyYvrc<;jt%D8H_>ob
z=I<mvw#K7hQE0N5I=e6Vi92o@2eYbfGX}+?+aqEY15Wenu?|P3*GsRKefv~KD9-ew
z9XcCIC4McUdXIjh9p`6vvAjn9$-VKJf)wQZT($!<VEMMXx-6x2!KyNvXbH%dFh28~
zf<3G^<j^rdX2?bhx3j&xy1{>C;oLe!I{cH`UXpzwuUZ9*I6j~Tr{deSzdvQtpJ8#Q
zL%+x}G_BpTL|D;I%VU8!xHQC$_fSr!Rjm?TQv)|zxCLz3h?!RE{EI!Q5Yr<j-2x)Q
z5OoB9#^(tRM}aqwlQMTX?V!@U?$UzDE~XlSmTyxBE-6W5eZ~J(+W6);yPG0xAJYQP
zJmKXP<h48EPn*E|Qu}m^&L?g*^{-UWb!R0IJ=ZjrbWl9*aP#fMecq{GYi>-uY?r#F
zPt|C&pr&2V`){;W9o+)O`u1OgDhtHVcuq_Q)`qfH-*oE*a$=Hyso}2rJa#e%JQ-3t
z&t60qEvDv}fb&_bynP{uT1N*`J2W)l5-11~_4~vXag*Y;>WZ8BBEnoK!luiRJQl|O
zzDF%eGgi6sq56Mj6`@3l72-qOoiU;jdZY0E0j|x_Yd`!|M`i0Wm6<unAd!kyi;Tli
zo%s_rc0;3Xt9yH+ixFee?lqn|nlqMJVy?>p%3P-AHvDUtd1^1@alt=-{@f6A5OUE(
z$(Fp;LmamLs#(JwAl4@1InB%Wi-GzD%z9ZI>UK9w;P1|RZKk2S`gy;hW(79QA(Jn1
zH*tBK<1+9y@T6CI_8fO%q1GX)aFezkwhmjWdG)fv>O@7>0ZP=fdJ+wU8C1uE`GE-b
zrDO2v$cAi^J|V%9qI~I;LpSmHNepv3BeK+Q+`QoZ-EZt^6?9t;aRb3qK-wA2ec;*J
z>5Ff_PNFMtg_s(%F!ZC0UHSy>8k>z2X>M%IE8JmSs+1`X=IIdk#Pv)V^&;xW?1$|5
zEH<yimWXw(jYidt#q3M!8|sX>1h(|L5mOHf9~)uaj^0vVdvHSYLk2s6oCf5~Ng#Ih
z#og{CeJ=M{eiFjNf6f@vEW#iLYv*lwBI5*q%*{KE?G-<AbodiVrmzm#m?YYYND1qv
zB{<dR?rw684G^i>GFI{@)k`HCoUg=TQ)jrR$gzO*!>(8+T#d+++LbU-;t`0ur$xEO
z0_wvhjV&$`Y8&yN?1V;>P9N(`xSdlT$mqY%t#|RbVRW7k5n~A2O~r7yCi05skm_}e
zN2`Dqo~K{BMq^6b-beZ!*KaScTc)8Oa~ZhpQ<T4Q`}he|fQ@lX1M#b1B^Jwfu|Cpg
zb#G+92b5Y*eXzhbEYKrC5zz8MZivftXB-Syi8n^<SROj)6@j3fyOMQWmE-695vVHk
zf!8en{dFI2zbi+x^n`YcpU;cgAP>f)(J3&4J<nIcE;(}!D%}D!B{m`jJ0S5v&&(O}
z8wF_(*&79JLNCMjbwfBn&&T(*7a{vm;>D02Rhmg`&e~$){&eKmfO2c6X3kQBx#%^s
zTR^tpE@zXK=a&23Nu9o-D|+W@O+6C<kL;VBXcShU7Ibr3uedxJ#Osi<)VuT3qjpcD
z-And$gKeY9vPY`6{kh!d)XmAY&oXfv&4KKEp6|yeu0J|xHPx)wzo3o+ZAhKRZ7SA0
zRn#_?6g^P<OJtPQ)i3FTSchR0``9{Y-w%{t6IH#@ATJyzRn>8H?UUd&PrIxs$1}Fl
z{LJ}x{HW>Z0OL>wD-?_3#KH2b5v9>}kM?U|?s~2ePi|mtk%p1d_^}E;)3719b1IcK
z!jr84?3!*eD6m?ZQX*q+n9ur8I2kqTv+uzF%1AHe;LPs5FP%PqLv;6z7B^)?f2jXt
z1$;6$;_3Rw-=Z}?UA~ZidZkXS*bDm7_##v9pQ3;O6;T~Zy-*v?%q4LND#fQVEE%i?
zl=SSh4@lYiVALf!I?iN@*(0)M>_;s>bq&qni_V<QbsTmAKFv`~pt5Ijpy(6FzCLL!
z*hoYr(U?6#N@d3fSE@Sz5(#BCAV$?B(ns3urzgg2OpeaIf4j(F+t>04a<u8AnVoO;
zsm<JW2I_KT6H?5A75v1tcYSWDM?eYK_VVm`C0N#;SanT#Ij#RyR(B@0Hdcj?E8!wT
zY%-(<Rg>qu2*du>rJ!o)-~cOq>@;ATI}w?Syq-*%UtV?u4Vj7bx<qF_c&ZD!E|vgI
zY<3@Wt<W_yL$nsDpM)P?8BU_!<~D$+jbU>f#F3C?zFx|b7Rv%2z4-0711Culdq;0w
z>j{1diLb%$Kz8nhIek*;Etse)wmbx4oUTuvEVj<0Ll64FwiV{dL8{{03#7l8eG``O
zBmIq?tB!%mlLZ5GeXm@Xh~(0tjEpfchMAgI>+DJjOo6*t$*NcoLOvaF+4z8-tSTbx
zwj7nT6UGo6J+#F>>@oHyLlLIWXPjbEzBX3xNz&jSJ;~js4%nF}Kqqwy@<uJerzsKJ
zjwV<l*W+#sOn7xGE5s(*;tjKDLvZ#gXEo?F{v?(d`>@M1L%Lup23$VvC^`0_j);DL
zbnHA$DdNy6|AH!2zagSP%Fu9Y8O(h!9rP-*^c?*_B^?)0K>qAi&Qlf895<GqB~4-#
zKM_F9Bk4&OFeh4IfLtR~-+3z8iIe60ngXGs*$q%`+3ajCe!YLU<tu^L1Bb1-_!T8J
z32&59D+W=(iuoN&n{6_}5|$cF=H=LmfcB0_zT31HO=Uv*x>0&X@?muLPwY>T)R!}x
zU}k6kVy6`2hz?L7MZ^q63(xs!-qnz4%HZmSs5f0C(JQ97%WCUuSXiiC)3^AEhiYMk
zd&hs|K{j`Mhgqv+{qJpMeq}#CkxBZqHjgHqqeJW+(ihFG>kZ{zP%UlHMmTsew$MEp
zTZW}Ia+b9;NIV3Wo)|?(mEQ%dDeiYyFAfraM&+1v^hB7mtFh50`gK&Quxo}033FGu
z-669|Z2$c2^Y4Bpb5&ccfDZuBF$JM~{WO#L4L;tojDa6_Onug@@{+m<z3zGZ+U8Fs
z#|KpGfBo~mA&##2e#w2#9x}FjqT$Nv5072<c>xGm<6xcm*0FP-9Zysl-p<GRH`*^Q
zu8~nn1(Cg>gp}?P8~bq&O%HE~y>I?Tm9`9Fq@@$}k#Uh*!&#M4A#<{n1lV(+fKuxz
zjrs8CvaH&1_#JHsB!G))GB)*q*QbiC58f+OZ3T|X#0=`;;?Jwt9+7!m$loL#IKd3l
z+caSDhITN!9Pnu2?lpM6%fi(yP_6ZcpU?RvXZ3i`>5XYHtZq_Cr1Sitour&`7S+At
zvE9|1j2%oTElZOJ8%FCOk9cH)$ynVQ3PBNkM01n2siLfOjvJ9<f*U(S5q~bn_+dtb
z16nedTWoS56rA<5md9SBGlu67+0Nc3znWDp?m^wSo4x~~zdxKy#}R6)p2C3cz^0EH
zg;z4NVz7p@<hv94M`u88w-LCIVFnV{!tr|7XWrE{PO_WPX%|;tRH_S!PhTN$+Yz|D
ze<vmA0y!#y96x5Gx~bofR<*we;8p#s|9zwWET#NuZJgATEG1z8UIX*+)Y|rG4PHHx
zFHAI(zJ#aU#K4<j8Yqg>CYt{lUN<@`n-0P1-UT5*m0dbd<#QH(fJM#mQ=729-_Iv;
z{Yp0d|2&@@J*50N_Hbn8LJxTOH?_wFF!RItuX_cffRFP4+}b&r&ETI(+Bq*?WB_c3
T0rw04@4R&Le?}#LJN^FvWh@Sj

literal 0
HcmV?d00001

diff --git a/common/lib/xmodule/xmodule/js/fixtures/video.html b/common/lib/xmodule/xmodule/js/fixtures/video.html
index e1fe11ae473..dabb3801b97 100644
--- a/common/lib/xmodule/xmodule/js/fixtures/video.html
+++ b/common/lib/xmodule/xmodule/js/fixtures/video.html
@@ -4,22 +4,7 @@
       <div
         id="video_id"
         class="video closed"
-        data-streams="0.5:7tqY6eQzVhE,1.0:cogebirgzzM,1.5:abcdefghijkl"
-        data-show-captions="true"
-        data-save-state-url="/save_user_state"
-        data-speed="1.5"
-        data-start=""
-        data-end=""
-        data-saved-video-position="0"
-        data-transcript-language="en"
-        data-transcript-languages='{"en": "English", "de": "Deutsch", "zh": "普通话"}'
-        data-transcript-translation-url="/transcript/translation"
-        data-transcript-available-translations-url="/transcript/available_translations"
-        data-autoplay="False"
-        data-yt-test-timeout="1500"
-        data-yt-api-url="www.youtube.com/iframe_api"
-        data-yt-test-url="gdata.youtube.com/feeds/api/videos/"
-        data-autohide-html5="True"
+        data-metadata='{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": "[]", "speed": "1.5", "startTime": "", "streams": "0.5:7tqY6eQzVhE,1.0:cogebirgzzM,1.5:abcdefghijkl", "sub": "Z5KLxerq05Y", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "ytTestUrl": "gdata.youtube.com/feeds/api/videos/"}'
       >
         <div class="focus_grabber first"></div>
 
@@ -35,35 +20,11 @@
             <section class="video-controls is-hidden">
               <div class="slider"></div>
               <div>
-                <ul class="vcr">
-                  <li><a class="video_control" href="#" title="Play" role="button" aria-disabled="false"></a></li>
-                  <li><div class="vidtime">0:00 / 0:00</div></li>
-                </ul>
-                <div class="secondary-controls">
-                  <div class="speeds">
-                    <a class="speed-button" href="#" title="Speeds" role="button" aria-disabled="false">
-                      <span class="label">Speed</span>
-                      <span class="value"></span>
-                    </a>
-                    <ol class="video-speeds"></ol>
-                  </div>
-                  <div class="volume">
-                    <a href="#" title="Volume" role="button" aria-disabled="false"></a>
-                    <div class="volume-slider-container">
-                      <div class="volume-slider"></div>
-                    </div>
-                  </div>
-                  <a href="#" class="add-fullscreen" title="Fill browser" role="button" aria-disabled="false">Fill Browser</a>
-                  <a href="#" class="quality-control is-hidden" title="HD off" role="button" aria-disabled="false">HD off</a>
-                  <div class="lang menu-container">
-                    <a href="#" class="hide-subtitles" title="Turn off captions" role="button" aria-disabled="false">Captions</a>
-                  </div>
-                </div>
+                <div class="vcr"><div class="vidtime">0:00 / 0:00</div></div>
+                <div class="secondary-controls"></div>
               </div>
             </section>
           </article>
-
-          <ol class="subtitles"><li></li></ol>
         </div>
 
         <div class="focus_grabber last"></div>
diff --git a/common/lib/xmodule/xmodule/js/fixtures/video_all.html b/common/lib/xmodule/xmodule/js/fixtures/video_all.html
index 1b0727d3d79..617d9583576 100644
--- a/common/lib/xmodule/xmodule/js/fixtures/video_all.html
+++ b/common/lib/xmodule/xmodule/js/fixtures/video_all.html
@@ -4,23 +4,7 @@
       <div
         id="video_id"
         class="video closed"
-        data-show-captions="true"
-        data-save-state-url="/save_user_state"
-        data-speed="1.5"
-        data-start=""
-        data-end=""
-        data-saved-video-position="0"
-        data-transcript-language="en"
-        data-transcript-languages='{"en": "English", "de": "Deutsch", "zh": "普通话"}'
-        data-transcript-translation-url="/transcript/translation"
-        data-transcript-available-translations-url="/transcript/available_translations"
-        data-sub="Z5KLxerq05Y"
-        data-sources='["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"]'
-        data-autoplay="False"
-        data-yt-test-timeout="1500"
-        data-yt-api-url="www.youtube.com/iframe_api"
-        data-yt-test-url="gdata.youtube.com/feeds/api/videos/"
-        data-autohide-html5="True"
+        data-metadata='{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": ["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"], "speed": "1.5", "startTime": "", "streams": "", "sub": "Z5KLxerq05Y", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "ytTestUrl": "gdata.youtube.com/feeds/api/videos/"}'
       >
         <div class="focus_grabber first"></div>
 
@@ -36,35 +20,11 @@
             <section class="video-controls is-hidden">
               <div class="slider"></div>
               <div>
-                <ul class="vcr">
-                  <li><a class="video_control" href="#" title="Play" role="button" aria-disabled="false"></a></li>
-                  <li><div class="vidtime">0:00 / 0:00</div></li>
-                </ul>
-                <div class="secondary-controls">
-                  <div class="speeds">
-                    <a class="speed-button" href="#" title="Speeds" role="button" aria-disabled="false">
-                      <span class="label">Speed</span>
-                      <span class="value"></span>
-                    </a>
-                    <ol class="video-speeds"></ol>
-                  </div>
-                  <div class="volume">
-                    <a href="#" title="Volume" role="button" aria-disabled="false"></a>
-                    <div class="volume-slider-container">
-                      <div class="volume-slider"></div>
-                    </div>
-                  </div>
-                  <a href="#" class="add-fullscreen" title="Fill browser" role="button" aria-disabled="false">Fill Browser</a>
-                  <a href="#" class="quality-control is-hidden" title="HD off" role="button" aria-disabled="false">HD off</a>
-                  <div class="lang menu-container">
-                    <a href="#" class="hide-subtitles" title="Turn off captions" role="button" aria-disabled="false">Captions</a>
-                  </div>
-                </div>
+                <div class="vcr"><div class="vidtime">0:00 / 0:00</div></div>
+                <div class="secondary-controls"></div>
               </div>
             </section>
           </article>
-
-          <ol class="subtitles"><li></li></ol>
         </div>
 
         <div class="focus_grabber last"></div>
diff --git a/common/lib/xmodule/xmodule/js/fixtures/video_html5.html b/common/lib/xmodule/xmodule/js/fixtures/video_html5.html
index 678803a90dd..47be4f04fcb 100644
--- a/common/lib/xmodule/xmodule/js/fixtures/video_html5.html
+++ b/common/lib/xmodule/xmodule/js/fixtures/video_html5.html
@@ -4,23 +4,7 @@
       <div
         id="video_id"
         class="video closed"
-        data-show-captions="true"
-        data-save-state-url="/save_user_state"
-        data-speed="1.5"
-        data-start=""
-        data-end=""
-        data-saved-video-position="0"
-        data-transcript-language="en"
-        data-transcript-languages='{"en": "English", "de": "Deutsch", "zh": "普通话"}'
-        data-transcript-translation-url="/transcript/translation"
-        data-transcript-available-translations-url="/transcript/available_translations"
-        data-sub="Z5KLxerq05Y"
-        data-sources='["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"]'
-        data-autoplay="False"
-        data-yt-test-timeout="1500"
-        data-yt-api-url="www.youtube.com/iframe_api"
-        data-yt-test-url="gdata.youtube.com/feeds/api/videos/"
-        data-autohide-html5="True"
+        data-metadata='{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": ["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"], "speed": "1.5", "startTime": "", "streams": "", "sub": "Z5KLxerq05Y", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "ytTestUrl": "gdata.youtube.com/feeds/api/videos/", "source": "", "html5_sources": ["http://youtu.be/3_yD_cEKoCk.mp4"]}'
       >
         <div class="focus_grabber first"></div>
 
@@ -33,8 +17,6 @@
             </section>
             <section class="video-controls is-hidden"></section>
           </article>
-
-          <ol class="subtitles"><li></li></ol>
         </div>
 
         <div class="focus_grabber last"></div>
diff --git a/common/lib/xmodule/xmodule/js/fixtures/video_no_captions.html b/common/lib/xmodule/xmodule/js/fixtures/video_no_captions.html
index a34df976bfb..77017d403dd 100644
--- a/common/lib/xmodule/xmodule/js/fixtures/video_no_captions.html
+++ b/common/lib/xmodule/xmodule/js/fixtures/video_no_captions.html
@@ -4,22 +4,7 @@
       <div
         id="video_id"
         class="video closed"
-        data-streams="0.5:7tqY6eQzVhE,1.0:cogebirgzzM,1.5:abcdefghijkl"
-        data-show-captions="false"
-        data-save-state-url="/save_user_state"
-        data-speed="1.5"
-        data-start=""
-        data-end=""
-        data-saved-video-position="0"
-        data-transcript-language="en"
-        data-transcript-languages='{"en": "English", "de": "Deutsch", "zh": "普通话"}'
-        data-transcript-translation-url="/transcript/translation"
-        data-transcript-available-translations-url="/transcript/available_translations"
-        data-autoplay="False"
-        data-yt-test-timeout="1500"
-        data-yt-api-url="www.youtube.com/iframe_api"
-        data-yt-test-url="gdata.youtube.com/feeds/api/videos/"
-        data-autohide-html5="True"
+        data-metadata='{"streams":"0.5:7tqY6eQzVhE,1.0:cogebirgzzM,1.5:abcdefghijkl", "showCaptions": false, "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "speed": "1.5", "startTime": "", "end": "", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytTestTimeout": "1500", "ytTestUrl": "gdata.youtube.com/feeds/api/videos/"}'
       >
         <div class="focus_grabber first"></div>
 
diff --git a/common/lib/xmodule/xmodule/js/fixtures/video_with_bumper.html b/common/lib/xmodule/xmodule/js/fixtures/video_with_bumper.html
new file mode 100644
index 00000000000..22bd2062689
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/fixtures/video_with_bumper.html
@@ -0,0 +1,36 @@
+<div class="course-content">
+  <div id="video_example">
+    <div id="example">
+      <div
+        id="video_id"
+        class="video closed"
+        data-metadata='{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": ["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"], "speed": "1.5", "startTime": "", "streams": "", "sub": "Z5KLxerq05Y", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "ytTestUrl": "gdata.youtube.com/feeds/api/videos/"}'
+        data-bumper-metadata='{"transcriptLanguage": "en", "showCaptions": "true", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "sources": ["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"], "transcriptTranslationUrl": "/transcript/translation/__lang__/?is_bumper=1", "transcriptAvailableTranslationsUrl": "/transcript/available_translations/?is_bumper=1", "streams": "", "saveStateUrl": "/save_user_state"}'
+        data-poster='{"url": "xmodule/include/fixtures/poster.jpg", "type": "youtube"}'
+      >
+        <div class="focus_grabber first"></div>
+
+        <div class="tc-wrapper">
+          <article class="video-wrapper">
+            <span tabindex="0" class="spinner" aria-hidden="false" aria-label="${_('Loading video player')}"></span>
+            <span tabindex="-1" class="btn-play is-hidden" aria-hidden="true" aria-label="${_('Play video')}"></span>
+            <div class="video-player-pre"></div>
+            <section class="video-player">
+              <iframe id="id"></iframe>
+            </section>
+            <div class="video-player-post"></div>
+            <section class="video-controls is-hidden">
+              <div class="slider"></div>
+              <div>
+                <div class="vcr"><div class="vidtime">0:00 / 0:00</div></div>
+                <div class="secondary-controls"></div>
+              </div>
+            </section>
+          </article>
+        </div>
+
+        <div class="focus_grabber last"></div>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/common/lib/xmodule/xmodule/js/fixtures/video_yt_multiple.html b/common/lib/xmodule/xmodule/js/fixtures/video_yt_multiple.html
index 8086c2b2691..8842b1e5926 100644
--- a/common/lib/xmodule/xmodule/js/fixtures/video_yt_multiple.html
+++ b/common/lib/xmodule/xmodule/js/fixtures/video_yt_multiple.html
@@ -4,22 +4,7 @@
       <div
         id="video_id1"
         class="video closed"
-        data-streams="0.5:7tqY6eQzVhE,1.0:cogebirgzzM,1.5:abcdefghijkl"
-        data-show-captions="true"
-        data-save-state-url="/save_user_state"
-        data-speed="1.5"
-        data-start=""
-        data-end=""
-        data-saved-video-position="0"
-        data-transcript-language="en"
-        data-transcript-languages='{"en": "English", "de": "Deutsch", "zh": "普通话"}'
-        data-transcript-translation-url="/transcript/translation"
-        data-transcript-available-translations-url="/transcript/available_translations"
-        data-autoplay="False"
-        data-yt-test-timeout="1500"
-        data-yt-api-url="www.youtube.com/iframe_api"
-        data-yt-test-url="gdata.youtube.com/feeds/api/videos/"
-        data-autohide-html5="True"
+        data-metadata='{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": ["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"], "speed": "1.5", "startTime": "", "streams": "0.5:7tqY6eQzVhE,1.0:cogebirgzzM,1.5:abcdefghijkl", "sub": "", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "ytTestUrl": "gdata.youtube.com/feeds/api/videos/"}'
       >
         <div class="focus_grabber first"></div>
 
@@ -35,35 +20,11 @@
             <section class="video-controls is-hidden">
               <div class="slider"></div>
               <div>
-                <ul class="vcr">
-                  <li><a class="video_control" href="#" title="Play" role="button" aria-disabled="false"></a></li>
-                  <li><div class="vidtime">0:00 / 0:00</div></li>
-                </ul>
-                <div class="secondary-controls">
-                  <div class="speeds">
-                    <a class="speed-button" href="#" title="Speeds" role="button" aria-disabled="false">
-                      <span class="label">Speed</span>
-                      <span class="value"></span>
-                    </a>
-                    <ol class="video-speeds"></ol>
-                  </div>
-                  <div class="volume">
-                    <a href="#" title="Volume" role="button" aria-disabled="false"></a>
-                    <div class="volume-slider-container">
-                      <div class="volume-slider"></div>
-                    </div>
-                  </div>
-                  <a href="#" class="add-fullscreen" title="Fill browser" role="button" aria-disabled="false">Fill Browser</a>
-                  <a href="#" class="quality-control is-hidden" title="HD off" role="button" aria-disabled="false">HD off</a>
-                  <div class="lang menu-container">
-                    <a href="#" class="hide-subtitles" title="Turn off captions" role="button" aria-disabled="false">Captions</a>
-                  </div>
-                </div>
+                <div class="vcr"><div class="vidtime">0:00 / 0:00</div></div>
+                <div class="secondary-controls"></div>
               </div>
             </section>
           </article>
-
-          <ol class="subtitles"><li></li></ol>
         </div>
 
         <div class="focus_grabber last"></div>
@@ -77,20 +38,7 @@
       <div
         id="video_id2"
         class="video"
-        data-streams="0.75:7tqY6eQzVhE,1.0:cogebirgzzM"
-        data-show-captions="true"
-        data-speed="1.0"
-        data-start=""
-        data-end=""
-        data-transcript-language="en"
-        data-transcript-languages='{"en": "English", "de": "Deutsch", "zh": "普通话"}'
-        data-transcript-translation-url="/transcript/translation"
-        data-transcript-available-translations-url="/transcript/available_translations"
-        data-autoplay="False"
-        data-yt-test-timeout="1500"
-        data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/"
-
-        data-autohide-html5="True"
+        data-metadata='{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": ["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"], "speed": "1.0", "startTime": "", "streams": "0.75:7tqY6eQzVhE,1.0:cogebirgzzM", "sub": "", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "ytTestUrl": "gdata.youtube.com/feeds/api/videos/"}'
       >
         <div class="tc-wrapper">
           <article class="video-wrapper">
@@ -102,30 +50,8 @@
             <section class="video-controls">
               <div class="slider"></div>
               <div>
-                <ul class="vcr">
-                  <li><a class="video_control" href="#" title="Play"></a></li>
-                  <li><div class="vidtime">0:00 / 0:00</div></li>
-                </ul>
-                <div class="secondary-controls">
-                  <div class="speeds">
-                    <a class="speed-button" href="#" title="Speeds" role="button" aria-disabled="false">
-                      <span class="label">Speed</span>
-                      <span class="value"></span>
-                    </a>
-                    <ol class="video-speeds"></ol>
-                  </div>
-                  <div class="volume">
-                    <a href="#"></a>
-                    <div class="volume-slider-container">
-                      <div class="volume-slider"></div>
-                    </div>
-                  </div>
-                  <a href="#" class="add-fullscreen" title="Fill browser">Fill Browser</a>
-                  <a href="#" class="quality-control is-hidden" title="HD">HD</a>
-                  <div class="lang menu-container">
-                    <a href="#" class="hide-subtitles" title="Turn off captions" role="button" aria-disabled="false">Captions</a>
-                  </div>
-                </div>
+                <div class="vcr"><div class="vidtime">0:00 / 0:00</div></div>
+                <div class="secondary-controls"></div>
               </div>
             </section>
           </article>
@@ -142,20 +68,7 @@
       <div
         id="video_id3"
         class="video"
-        data-streams="0.75:7tqY6eQzVhE,1.0:cogebirgzzM"
-        data-show-captions="true"
-        data-speed="1.0"
-        data-start=""
-        data-end=""
-        data-transcript-language="en"
-        data-transcript-languages='{"en": "English", "de": "Deutsch", "zh": "普通话"}'
-        data-transcript-translation-url="/transcript/translation"
-        data-transcript-available-translations-url="/transcript/available_translations"
-        data-autoplay="False"
-        data-yt-test-timeout="1500"
-        data-yt-test-url="https://gdata.youtube.com/feeds/api/videos/"
-
-        data-autohide-html5="True"
+        data-metadata='{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": ["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"], "speed": "1.0", "startTime": "", "streams": "0.75:7tqY6eQzVhE,1.0:cogebirgzzM", "sub": "", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "ytTestUrl": "gdata.youtube.com/feeds/api/videos/"}'
       >
         <div class="tc-wrapper">
           <article class="video-wrapper">
diff --git a/common/lib/xmodule/xmodule/js/spec/helper.js b/common/lib/xmodule/xmodule/js/spec/helper.js
index f188c9c639e..97d422d5d89 100644
--- a/common/lib/xmodule/xmodule/js/spec/helper.js
+++ b/common/lib/xmodule/xmodule/js/spec/helper.js
@@ -206,6 +206,9 @@
             },
             toBeInArray: function (array) {
                 return $.inArray(this.actual, array) > -1;
+            },
+            toBeFocused: function () {
+                return $(this.actual)[0] === $(this.actual)[0].ownerDocument.activeElement;
             }
         });
 
@@ -239,12 +242,11 @@
             loadFixtures('video_all.html');
         }
 
-        // If `params` is an object, assign it's properties as data attributes
+        // If `params` is an object, assign its properties as data attributes
         // to the main video DIV element.
         if (_.isObject(params)) {
-            $('#example')
-                .find('#video_id')
-                .data(params);
+            var metadata = _.extend($('#video_id').data('metadata'), params);
+            $('#video_id').data('metadata', metadata);
         }
 
         jasmine.stubRequests();
diff --git a/common/lib/xmodule/xmodule/js/spec/video/general_spec.js b/common/lib/xmodule/xmodule/js/spec/video/general_spec.js
index 72fc269fd8a..7cefab5e499 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/general_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/general_spec.js
@@ -1,6 +1,6 @@
 (function (undefined) {
     describe('Video', function () {
-        var oldOTBD;
+        var oldOTBD, state;
 
         beforeEach(function () {
             jasmine.stubRequests();
@@ -17,11 +17,12 @@
                 beforeEach(function () {
                     loadFixtures('video.html');
                     $.cookie.andReturn('0.50');
+                    this.state = jasmine.initializePlayerYouTube('video_html5.html');
                 });
 
                 describe('by default', function () {
-                    beforeEach(function () {
-                        this.state = new window.Video('#example');
+                    afterEach(function () {
+                        this.state.videoPlayer.destroy();
                     });
 
                     it('check videoType', function () {
@@ -54,19 +55,16 @@
                 var state;
 
                 beforeEach(function () {
-                    loadFixtures('video_html5.html');
                     $.cookie.andReturn('0.75');
+                    state = jasmine.initializePlayer('video_html5.html');
                 });
 
-                describe('by default', function () {
-                    beforeEach(function () {
-                        state = new window.Video('#example');
-                    });
-
-                    afterEach(function () {
-                        state = undefined;
-                    });
+                afterEach(function () {
+                    state.videoPlayer.destroy();
+                    state = undefined;
+                });
 
+                describe('by default', function () {
                     it('check videoType', function () {
                         expect(state.videoType).toEqual('html5');
                     });
@@ -95,14 +93,6 @@
                 // the stand alone HTML5 player object is already loaded, so no
                 // further testing in that case is required.
                 describe('HTML5 API is available', function () {
-                    beforeEach(function () {
-                        state = new Video('#example');
-                    });
-
-                    afterEach(function () {
-                        state = null;
-                    });
-
                     it('create the Video Player', function () {
                         expect(state.videoPlayer.player).not.toBeUndefined();
                     });
@@ -113,8 +103,11 @@
         describe('YouTube API is not loaded', function () {
             beforeEach(function () {
                 window.YT = undefined;
+                state = jasmine.initializePlayerYouTube();
+            })
 
-                state = jasmine.initializePlayerYouTube('video.html');
+            afterEach(function () {
+                state.videoPlayer.destroy();
             });
 
             it('callback, to be called after YouTube API loads, exists and is called', function () {
@@ -159,9 +152,8 @@
                 }
             ];
 
-            beforeEach(function () {
-                loadFixtures('video.html');
-
+            afterEach(function () {
+                state.videoPlayer.destroy();
             });
 
             $.each(miniTestSuite, function (index, test) {
@@ -172,13 +164,10 @@
 
             function itFabrique(itDescription, data, expectData) {
                 it(itDescription, function () {
-                    $('#example').find('.video')
-                        .data({
-                            'start': data.start,
-                            'end': data.end
-                        });
-
-                    state = new Video('#example');
+                    state = jasmine.initializePlayer('video.html', {
+                        'start': data.start,
+                        'end': data.end
+                    });
 
                     expect(state.config.startTime).toBe(expectData.start);
                     expect(state.config.endTime).toBe(expectData.end);
@@ -238,26 +227,5 @@
                 expect(numAjaxCalls).toBe(1);
             });
         });
-
-        describe('log', function () {
-            beforeEach(function () {
-                loadFixtures('video_html5.html');
-                state = new Video('#example');
-                spyOn(Logger, 'log');
-                state.videoPlayer.log('someEvent', {
-                    currentTime: 25,
-                    speed: '1.0'
-                });
-            });
-
-            it('call the logger with valid extra parameters', function () {
-                expect(Logger.log).toHaveBeenCalledWith('someEvent', {
-                    id: 'id',
-                    code: 'html5',
-                    currentTime: 25,
-                    speed: '1.0'
-                });
-            });
-        });
     });
 }).call(this);
diff --git a/common/lib/xmodule/xmodule/js/spec/video/html5_video_spec.js b/common/lib/xmodule/xmodule/js/spec/video/html5_video_spec.js
index baefb4dea1c..b22cdd375ef 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/html5_video_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/html5_video_spec.js
@@ -10,8 +10,8 @@
 
         afterEach(function () {
             state.storage.clear();
+            state.videoPlayer.destroy();
             $.fn.scrollTo.reset();
-            $('.subtitles').remove();
             $('source').remove();
             window.onTouchBasedDevice = oldOTBD;
         });
diff --git a/common/lib/xmodule/xmodule/js/spec/video/initialize_spec.js b/common/lib/xmodule/xmodule/js/spec/video/initialize_spec.js
index e08ba56a751..f3194b8bce3 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/initialize_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/initialize_spec.js
@@ -12,158 +12,6 @@ function (Initialize) {
             state = {};
         });
 
-        describe('saveState function', function () {
-            var videoPlayerCurrentTime, newCurrentTime, speed;
-
-            // We make sure that `currentTime` is a float. We need to test
-            // that Math.round() is called.
-            videoPlayerCurrentTime = 3.1242;
-
-            // We have two times, because one is  stored in
-            // `videoPlayer.currentTime`, and the other is passed directly to
-            // `saveState` in `data` object. In each case, there is different
-            // code that handles these times. They have to be different for
-            // test completeness sake. Also, make sure it is float, as is the
-            // time above.
-            newCurrentTime = 5.4;
-
-            speed = '0.75';
-
-            beforeEach(function () {
-                state = {
-                    videoPlayer: {
-                        currentTime: videoPlayerCurrentTime
-                    },
-                    storage: {
-                        setItem: jasmine.createSpy()
-                    },
-                    config: {
-                        saveStateUrl: 'http://example.com/save_user_state'
-                    }
-                };
-
-                spyOn($, 'ajax');
-                spyOn(Time, 'formatFull').andCallThrough();
-            });
-
-            it('data is not an object, async is true', function () {
-                itSpec({
-                    asyncVal: true,
-                    speedVal: undefined,
-                    positionVal: videoPlayerCurrentTime,
-                    data: undefined,
-                    ajaxData: {
-                        saved_video_position: Time.formatFull(Math.round(videoPlayerCurrentTime))
-                    }
-                });
-            });
-
-            it('data contains speed, async is false', function () {
-                itSpec({
-                    asyncVal: false,
-                    speedVal: speed,
-                    positionVal: undefined,
-                    data: {
-                        speed: speed
-                    },
-                    ajaxData: {
-                        speed: speed
-                    }
-                });
-            });
-
-            it('data contains float position, async is true', function () {
-                itSpec({
-                    asyncVal: true,
-                    speedVal: undefined,
-                    positionVal: newCurrentTime,
-                    data: {
-                        saved_video_position: newCurrentTime
-                    },
-                    ajaxData: {
-                        saved_video_position: Time.formatFull(Math.round(newCurrentTime))
-                    }
-                });
-            });
-
-            it('data contains speed and rounded position, async is false', function () {
-                itSpec({
-                    asyncVal: false,
-                    speedVal: speed,
-                    positionVal: Math.round(newCurrentTime),
-                    data: {
-                        speed: speed,
-                        saved_video_position: Math.round(newCurrentTime)
-                    },
-                    ajaxData: {
-                        speed: speed,
-                        saved_video_position: Time.formatFull(Math.round(newCurrentTime))
-                    }
-                });
-            });
-
-            it('data contains empty object, async is true', function () {
-                itSpec({
-                    asyncVal: true,
-                    speedVal: undefined,
-                    positionVal: undefined,
-                    data: {},
-                    ajaxData: {}
-                });
-            });
-
-            it('data contains position 0, async is true', function () {
-                itSpec({
-                    asyncVal: true,
-                    speedVal: undefined,
-                    positionVal: 0,
-                    data: {
-                        saved_video_position: 0
-                    },
-                    ajaxData: {
-                        saved_video_position: Time.formatFull(Math.round(0))
-                    }
-                });
-            });
-
-            return;
-
-            function itSpec(value) {
-                var asyncVal    = value.asyncVal,
-                    speedVal    = value.speedVal,
-                    positionVal = value.positionVal,
-                    data        = value.data,
-                    ajaxData    = value.ajaxData;
-
-                Initialize.prototype.saveState.call(state, asyncVal, data);
-
-                if (speedVal) {
-                    expect(state.storage.setItem).toHaveBeenCalledWith(
-                        'speed',
-                        speedVal,
-                        true
-                    );
-                }
-                if (positionVal) {
-                    expect(state.storage.setItem).toHaveBeenCalledWith(
-                        'savedVideoPosition',
-                        positionVal,
-                        true
-                    );
-                    expect(Time.formatFull).toHaveBeenCalledWith(
-                        positionVal
-                    );
-                }
-                expect($.ajax).toHaveBeenCalledWith({
-                    url: state.config.saveStateUrl,
-                    type: 'POST',
-                    async: asyncVal,
-                    dataType: 'json',
-                    data: ajaxData
-                });
-            }
-        });
-
         describe('getCurrentLanguage', function () {
             var msg;
 
@@ -356,20 +204,12 @@ function (Initialize) {
 
                 describe('when new speed is available', function () {
                     beforeEach(function () {
-                        Initialize.prototype.setSpeed.call(state, '0.75', true);
+                        Initialize.prototype.setSpeed.call(state, '0.75');
                     });
 
                     it('set new speed', function () {
                         expect(state.speed).toEqual('0.75');
                     });
-
-                    it('save setting for new speed', function () {
-                        expect(state.storage.setItem.calls[0].args)
-                            .toEqual(['speed', '0.75', true]);
-
-                        expect(state.storage.setItem.calls[1].args)
-                            .toEqual(['general_speed', '0.75']);
-                    });
                 });
 
                 describe('when new speed is not available', function () {
@@ -390,7 +230,7 @@ function (Initialize) {
                     };
 
                     $.each(map, function(key, expected) {
-                        Initialize.prototype.setSpeed.call(state, key, true);
+                        Initialize.prototype.setSpeed.call(state, key);
                         expect(state.speed).toBe(expected);
                     });
                 });
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_accessible_menu_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_accessible_menu_spec.js
index feb332122cf..a790f8ecaee 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/video_accessible_menu_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_accessible_menu_spec.js
@@ -5,6 +5,7 @@
         afterEach(function () {
             $('source').remove();
             state.storage.clear();
+            state.videoPlayer.destroy();
         });
 
         describe('constructor', function () {
@@ -56,24 +57,6 @@
                     });
                     */
                 });
-
-                it('add ARIA attributes to button, menu, and menu items links',
-                   function () {
-                    expect(button).toHaveAttrs({
-                        'role': 'button',
-                        'title': '.srt',
-                        'aria-disabled': 'false'
-                    });
-
-                    expect(menuList).toHaveAttr('role', 'menu');
-
-                    menuItemsLinks.each(function(){
-                        expect($(this)).toHaveAttrs({
-                            'role': 'menuitem',
-                            'aria-disabled': 'false'
-                        });
-                    });
-                });
             });
 
             describe('when running', function () {
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_bumper_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_bumper_spec.js
new file mode 100644
index 00000000000..0a355e918d2
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_bumper_spec.js
@@ -0,0 +1,109 @@
+(function (WAIT_TIMEOUT) {
+    'use strict';
+    describe('VideoBumper', function () {
+        var state, oldOTBD, waitForPlaying;
+
+        waitForPlaying = function (state) {
+            waitsFor(function () {
+                return state.el.hasClass('is-playing');
+            }, 'Player is not playing.', WAIT_TIMEOUT);
+        };
+
+        beforeEach(function () {
+            oldOTBD = window.onTouchBasedDevice;
+            window.onTouchBasedDevice = jasmine
+                .createSpy('onTouchBasedDevice').andReturn(null);
+            state = jasmine.initializePlayer('video_with_bumper.html');
+            $('.poster .btn-play').click();
+            jasmine.Clock.useMock();
+        });
+
+        afterEach(function () {
+            $('source').remove();
+            state.storage.clear();
+            if (state.bumperState && state.bumperState.videoPlayer) {
+                state.bumperState.videoPlayer.destroy();
+            }
+            if (state.videoPlayer) {
+                state.videoPlayer.destroy();
+            }
+            window.onTouchBasedDevice = oldOTBD;
+        });
+
+        it('can render the bumper video', function () {
+            expect($('.is-bumper')).toExist();
+        });
+
+        it('can show the main video on error', function () {
+            state.el.trigger('error');
+            jasmine.Clock.tick(20);
+            expect($('.is-bumper')).not.toExist();
+            waitForPlaying(state);
+        });
+
+        it('can show the main video once bumper ends', function () {
+            state.el.trigger('ended');
+            jasmine.Clock.tick(20);
+            expect($('.is-bumper')).not.toExist();
+            waitForPlaying(state);
+        });
+
+        it('can show the main video on skip', function () {
+            state.bumperState.videoBumper.skip();
+            jasmine.Clock.tick(20);
+            expect($('.is-bumper')).not.toExist();
+            waitForPlaying(state);
+        });
+
+        it('can stop the bumper video playing if it is too long', function () {
+            state.el.trigger('timeupdate', [state.bumperState.videoBumper.maxBumperDuration + 1]);
+            jasmine.Clock.tick(20);
+            expect($('.is-bumper')).not.toExist();
+            waitForPlaying(state);
+        });
+
+        it('can save appropriate states correctly on ended', function () {
+            var saveState = jasmine.createSpy('saveState');
+            state.bumperState.videoSaveStatePlugin.saveState = saveState;
+            state.el.trigger('ended');
+            jasmine.Clock.tick(20);
+            expect(saveState).toHaveBeenCalledWith(true, {
+                bumper_last_view_date: true});
+        });
+
+        it('can save appropriate states correctly on skip', function () {
+            var saveState = jasmine.createSpy('saveState');
+            state.bumperState.videoSaveStatePlugin.saveState = saveState;
+            state.bumperState.videoBumper.skip();
+            expect(state.storage.getItem('isBumperShown')).toBeTruthy();
+            jasmine.Clock.tick(20);
+            expect(saveState).toHaveBeenCalledWith(true, {
+                bumper_last_view_date: true});
+        });
+
+         it('can save appropriate states correctly on error', function () {
+            var saveState = jasmine.createSpy('saveState');
+            state.bumperState.videoSaveStatePlugin.saveState = saveState;
+            state.el.trigger('error');
+            expect(state.storage.getItem('isBumperShown')).toBeTruthy();
+            jasmine.Clock.tick(20);
+            expect(saveState).toHaveBeenCalledWith(true, {
+                bumper_last_view_date: true});
+        });
+
+        it('can save appropriate states correctly on skip and do not show again', function () {
+            var saveState = jasmine.createSpy('saveState');
+            state.bumperState.videoSaveStatePlugin.saveState = saveState;
+            state.bumperState.videoBumper.skipAndDoNotShowAgain();
+            expect(state.storage.getItem('isBumperShown')).toBeTruthy();
+            jasmine.Clock.tick(20);
+            expect(saveState).toHaveBeenCalledWith(true, {
+                bumper_last_view_date: true, bumper_do_not_show_again: true});
+        });
+
+        it('can destroy itself', function () {
+            state.bumperState.videoBumper.destroy();
+            expect(state.videoBumper).toBeUndefined();
+        });
+    });
+}).call(this, window.WAIT_TIMEOUT);
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_caption_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_caption_spec.js
index 39778d7ba6c..f269bde541a 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/video_caption_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_caption_spec.js
@@ -11,14 +11,13 @@
         });
 
         afterEach(function () {
-            $('.subtitles').remove();
-
             // `source` tags should be removed to avoid memory leak bug that we
             // had before. Removing of `source` tag, not `video` tag, stops
             // loading video source and clears the memory.
             $('source').remove();
             $.fn.scrollTo.reset();
             state.storage.clear();
+            state.videoPlayer.destroy();
 
             window.onTouchBasedDevice = oldOTBD;
         });
@@ -121,11 +120,6 @@
                     });
                 });
 
-                it('bind the hide caption button', function () {
-                    state = jasmine.initializePlayer();
-                    expect($('.hide-subtitles')).toHandle('click');
-                });
-
                 it('bind the mouse movement', function () {
                     state = jasmine.initializePlayer();
                     expect($('.subtitles')).toHandle('mouseover');
@@ -143,6 +137,27 @@
 
             });
 
+            it('can destroy itself', function () {
+                spyOn($, 'ajaxWithPrefix');
+                state = jasmine.initializePlayer();
+                var plugin = state.videoCaption;
+
+                spyOn($.fn, 'off').andCallThrough();
+                state.videoCaption.destroy();
+
+                expect(state.videoCaption).toBeUndefined();
+                expect($.fn.off).toHaveBeenCalledWith({
+                    'caption:fetch': plugin.fetchCaption,
+                    'caption:resize': plugin.onResize,
+                    'caption:update': plugin.onCaptionUpdate,
+                    'ended': plugin.pause,
+                    'fullscreen': plugin.onResize,
+                    'pause': plugin.pause,
+                    'play': plugin.play,
+                    'destroy': plugin.destroy
+                });
+            });
+
             describe('renderLanguageMenu', function () {
                 describe('is rendered', function () {
                     it('if languages more than 1', function () {
@@ -593,7 +608,7 @@
             it(msg, function () {
                 spyOn(Caption, 'fetchAvailableTranslations');
                 $.ajax.andCallFake(function (settings) {
-                    settings.error([]);
+                    _.result(settings, 'error');
                 });
 
                 state.config.transcriptLanguages = {};
@@ -612,7 +627,7 @@
             xit(msg, function () {
                 $.ajax
                     .andCallFake(function (settings) {
-                        settings.error([]);
+                        _.result(settings, 'error');
                     });
 
                 state.config.transcriptLanguages = {
@@ -690,7 +705,7 @@
             msg = 'on error: captions are hidden if there are no transcript';
             it(msg, function () {
                 $.ajax.andCallFake(function (settings) {
-                    settings.error();
+                    _.result(settings, 'error');
                 });
                 Caption.fetchAvailableTranslations();
 
@@ -907,8 +922,8 @@
                             $('.subtitles').css('maxHeight'), 10
                         );
                         videoWrapperHeight = $('.video-wrapper').height();
-                        progressSliderHeight = videoControl.sliderEl.height();
-                        controlHeight = videoControl.el.height();
+                        progressSliderHeight = state.el.find('.slider').height();
+                        controlHeight = state.el.find('.video-controls').height();
                         shouldBeHeight = videoWrapperHeight -
                             0.5 * progressSliderHeight -
                             controlHeight;
@@ -1043,7 +1058,6 @@
         describe('toggle', function () {
             beforeEach(function () {
                 state = jasmine.initializePlayer();
-                spyOn(state.videoPlayer, 'log');
                 $('.subtitles li[data-index=1]').addClass('current');
             });
 
@@ -1053,15 +1067,6 @@
                     state.videoCaption.toggle(jQuery.Event('click'));
                 });
 
-                it('log the hide_transcript event', function () {
-                    expect(state.videoPlayer.log).toHaveBeenCalledWith(
-                        'hide_transcript',
-                        {
-                            currentTime: state.videoPlayer.currentTime
-                        }
-                    );
-                });
-
                 it('hide the caption', function () {
                     expect(state.el).toHaveClass('closed');
                 });
@@ -1079,15 +1084,6 @@
                     jasmine.Clock.useMock();
                 });
 
-                it('log the show_transcript event', function () {
-                    expect(state.videoPlayer.log).toHaveBeenCalledWith(
-                        'show_transcript',
-                        {
-                            currentTime: state.videoPlayer.currentTime
-                        }
-                    );
-                });
-
                 it('show the caption', function () {
                     expect(state.el).not.toHaveClass('closed');
                 });
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_context_menu_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_context_menu_spec.js
index 269a75053c4..295b151a4fd 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/video_context_menu_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_context_menu_spec.js
@@ -68,6 +68,7 @@
             $('source').remove();
             _.result(state.storage, 'clear');
             _.result($('video').data('contextmenu'), 'destroy');
+            _.result(state.videoPlayer, 'destroy');
         });
 
         describe('constructor', function () {
@@ -219,12 +220,13 @@
 
             it('mouse left/right-clicking behaves as expected on play/pause menu item', function () {
                 var menuItem = menuItems.first();
+                spyOn(state.videoPlayer, 'isPlaying');
                 spyOn(state.videoPlayer, 'play').andCallFake(function () {
-                    state.videoControl.isPlaying = true;
+                    state.videoPlayer.isPlaying.andReturn(true);
                     state.el.trigger('play');
                 });
                 spyOn(state.videoPlayer, 'pause').andCallFake(function () {
-                    state.videoControl.isPlaying = false;
+                    state.videoPlayer.isPlaying.andReturn(false);
                     state.el.trigger('pause');
                 });
                 // Left-click on play
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_control_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_control_spec.js
index 98569620c22..85794dc2d5e 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/video_control_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_control_spec.js
@@ -13,12 +13,13 @@
         afterEach(function () {
             $('source').remove();
             state.storage.clear();
-            window.Video.previousState = null;
+            state.videoPlayer.destroy();
             window.onTouchBasedDevice = oldOTBD;
         });
 
         describe('constructor', function () {
             beforeEach(function () {
+                window.VideoState = {};
                 state = jasmine.initializePlayer();
             });
 
@@ -28,83 +29,13 @@
                         '.slider',
                         'ul.vcr',
                         'a.play',
-                        '.vidtime',
-                        '.add-fullscreen'
+                        '.vidtime'
                     ].join(',')
                 );
 
                 expect($('.video-controls').find('.vidtime'))
                     .toHaveText('0:00 / 0:00');
             });
-
-            it('add ARIA attributes to time control', function () {
-                var timeControl = $('div.slider > a');
-
-                expect(timeControl).toHaveAttrs({
-                    'role': 'slider',
-                    'title': 'Video position',
-                    'aria-disabled': 'false'
-                });
-
-                expect(timeControl).toHaveAttr('aria-valuetext');
-            });
-
-            it('add ARIA attributes to play control', function () {
-                var playControl = $('ul.vcr a');
-
-                expect(playControl).toHaveAttrs({
-                    'role': 'button',
-                    'title': 'Play',
-                    'aria-disabled': 'false'
-                });
-            });
-
-            it('add ARIA attributes to fullscreen control', function () {
-                var fullScreenControl = $('a.add-fullscreen');
-
-                expect(fullScreenControl).toHaveAttrs({
-                    'role': 'button',
-                    'title': 'Fill browser',
-                    'aria-disabled': 'false'
-                });
-            });
-
-            it('bind the playback button', function () {
-                expect($('.video_control')).toHandleWith(
-                    'click',
-                    state.videoControl.togglePlayback
-                );
-            });
-
-            describe('when on a non-touch based device', function () {
-                beforeEach(function () {
-                    state = jasmine.initializePlayer();
-                });
-
-                it('add the play class to video control', function () {
-                    expect($('.video_control')).toHaveClass('play');
-                    expect($('.video_control')).toHaveAttr(
-                        'title', 'Play'
-                    );
-                });
-            });
-
-            describe('when on a touch based device', function () {
-                beforeEach(function () {
-                    window.onTouchBasedDevice.andReturn(['iPad']);
-                    state = jasmine.initializePlayer();
-                });
-
-                it(
-                    'does not add the play class to video control',
-                    function ()
-                {
-                    expect($('.video_control')).toHaveClass('play');
-                    expect($('.video_control')).toHaveAttr(
-                        'title', 'Play'
-                    );
-                });
-            });
         });
 
         describe('constructor with start-time', function () {
@@ -115,6 +46,7 @@
                 var duration, sliderEl, expectedValue;
 
                 runs(function () {
+                    window.VideoState = {};
                     state = jasmine.initializePlayer({
                         start: 10,
                         savedVideoPosition: 0
@@ -147,6 +79,7 @@
                 var duration, sliderEl, expectedValue;
 
                 runs(function () {
+                    window.VideoState = {};
                     state = jasmine.initializePlayer({
                         start: 10,
                         savedVideoPosition: 15
@@ -181,6 +114,7 @@
                 var duration, sliderEl, expectedValue;
 
                 runs(function () {
+                    window.VideoState = {};
                     state = jasmine.initializePlayer({
                         start: 10,
                         savedVideoPosition: -15
@@ -215,6 +149,7 @@
                 var duration, sliderEl, expectedValue;
 
                 runs(function () {
+                    window.VideoState = {};
                     state = jasmine.initializePlayer({
                         start: 10,
                         savedVideoPosition: 'a'
@@ -249,6 +184,7 @@
                 var duration, sliderEl, expectedValue;
 
                 runs(function () {
+                    window.VideoState = {};
                     state = jasmine.initializePlayer({
                         start: 10,
                         savedVideoPosition: 10000
@@ -278,13 +214,14 @@
 
         describe('constructor with end-time', function () {
             it(
-                'saved position is 0, timer slider and VCR set to 0:00 ' + 
+                'saved position is 0, timer slider and VCR set to 0:00 ' +
                 'and ending at specified end-time',
                 function ()
             {
                 var duration, sliderEl, expectedValue;
 
                 runs(function () {
+                    window.VideoState = {};
                     state = jasmine.initializePlayer({
                         end: 20,
                         savedVideoPosition: 0
@@ -319,6 +256,7 @@
                 var duration, sliderEl, expectedValue;
 
                 runs(function () {
+                    window.VideoState = {};
                     state = jasmine.initializePlayer({
                         end: 20,
                         savedVideoPosition: 15
@@ -353,6 +291,7 @@
                 var duration, sliderEl, expectedValue;
 
                 runs(function () {
+                    window.VideoState = {};
                     state = jasmine.initializePlayer({
                         end: 20,
                         savedVideoPosition: -15
@@ -387,6 +326,7 @@
                 var duration, sliderEl, expectedValue;
 
                 runs(function () {
+                    window.VideoState = {};
                     state = jasmine.initializePlayer({
                         end: 20,
                         savedVideoPosition: 'a'
@@ -422,6 +362,7 @@
                 var duration, sliderEl, expectedValue;
 
                 runs(function () {
+                    window.VideoState = {};
                     state = jasmine.initializePlayer({
                         end: 20,
                         savedVideoPosition: 10000
@@ -457,6 +398,7 @@
                 var duration, sliderEl, expectedValue;
 
                 runs(function () {
+                    window.VideoState = {};
                     state = jasmine.initializePlayer({
                         start: 10,
                         end: 20,
@@ -492,6 +434,7 @@
                 var duration, sliderEl, expectedValue;
 
                 runs(function () {
+                    window.VideoState = {};
                     state = jasmine.initializePlayer({
                         start: 10,
                         end: 20,
@@ -527,6 +470,7 @@
                 var duration, sliderEl, expectedValue;
 
                 runs(function () {
+                    window.VideoState = {};
                     state = jasmine.initializePlayer({
                         start: 10,
                         end: 20,
@@ -562,6 +506,7 @@
                 var duration, sliderEl, expectedValue;
 
                 runs(function () {
+                    window.VideoState = {};
                     state = jasmine.initializePlayer({
                         start: 10,
                         end: 20,
@@ -597,6 +542,7 @@
                 var duration, sliderEl, expectedValue;
 
                 runs(function () {
+                    window.VideoState = {};
                     state = jasmine.initializePlayer({
                         start: 10,
                         end: 20,
@@ -625,217 +571,8 @@
             });
         });
 
-        it('Controls height is actual on switch to fullscreen', function () {
-            spyOn($.fn, 'height').andCallFake(function (val) {
-                return _.isUndefined(val) ? 100: this;
-            });
-
-            state = jasmine.initializePlayer();
-            $(state.el).trigger('fullscreen');
-
-            expect(state.videoControl.height).toBe(150);
-        });
-
-        describe('play', function () {
-            beforeEach(function () {
-                state = jasmine.initializePlayer();
-                state.videoControl.play();
-            });
-
-            it('switch playback button to play state', function () {
-                expect($('.video_control')).not.toHaveClass('play');
-                expect($('.video_control')).toHaveClass('pause');
-                expect($('.video_control')).toHaveAttr('title', 'Pause');
-            });
-        });
-
-        describe('pause', function () {
-            beforeEach(function () {
-                state = jasmine.initializePlayer();
-                state.videoControl.pause();
-            });
-
-            it('switch playback button to pause state', function () {
-                expect($('.video_control')).not.toHaveClass('pause');
-                expect($('.video_control')).toHaveClass('play');
-                expect($('.video_control')).toHaveAttr('title', 'Play');
-            });
-        });
-
-        describe('togglePlayback', function () {
-            beforeEach(function () {
-                state = jasmine.initializePlayer();
-            });
-
-            describe(
-                'when the control does not have play or pause class',
-                function ()
-            {
-                beforeEach(function () {
-                    $('.video_control').removeClass('play')
-                        .removeClass('pause');
-                });
-
-                describe('when the video is playing', function () {
-                    beforeEach(function () {
-                        $('.video_control').addClass('play');
-                        spyOnEvent(state.videoControl, 'pause');
-                        state.videoControl.togglePlayback(
-                            $.Event('click')
-                        );
-                    });
-
-                    it('does not trigger the pause event', function () {
-                        expect('pause').not
-                            .toHaveBeenTriggeredOn(state.videoControl);
-                    });
-                });
-
-                describe('when the video is paused', function () {
-                    beforeEach(function () {
-                        $('.video_control').addClass('pause');
-                        spyOnEvent(state.videoControl, 'play');
-                        state.videoControl.togglePlayback(
-                            $.Event('click')
-                        );
-                    });
-
-                    it('does not trigger the play event', function () {
-                        expect('play').not
-                            .toHaveBeenTriggeredOn(state.videoControl);
-                    });
-                });
-            });
-        });
-
-        describe('Play placeholder', function () {
-            var cases = [
-                {
-                    name: 'PC',
-                    isShown: false,
-                    isTouch: null
-                }, {
-                    name: 'iPad',
-                    isShown: true,
-                    isTouch: ['iPad']
-                }, {
-                    name: 'Android',
-                    isShown: true,
-                    isTouch: ['Android']
-                }, {
-                    name: 'iPhone',
-                    isShown: false,
-                    isTouch: ['iPhone']
-                }
-            ];
-
-            beforeEach(function () {
-                jasmine.stubRequests();
-
-                spyOn(window.YT, 'Player').andCallThrough();
-            });
-
-            it ('works correctly on calling proper methods', function () {
-                var btnPlay;
-
-                state = jasmine.initializePlayer();
-                btnPlay = state.el.find('.btn-play');
-
-                state.videoControl.showPlayPlaceholder();
-
-                expect(btnPlay).not.toHaveClass('is-hidden');
-                expect(btnPlay).toHaveAttrs({
-                    'aria-hidden': 'false',
-                    'tabindex': 0
-                });
-
-                state.videoControl.hidePlayPlaceholder();
-
-                expect(btnPlay).toHaveClass('is-hidden');
-                expect(btnPlay).toHaveAttrs({
-                    'aria-hidden': 'true',
-                    'tabindex': -1
-                });
-            });
-
-            $.each(cases, function (index, data) {
-                var message = [
-                    (data.isShown) ? 'is' : 'is not',
-                    ' shown on',
-                    data.name
-                ].join('');
-
-                it(message, function () {
-                    var btnPlay;
-
-                    window.onTouchBasedDevice.andReturn(data.isTouch);
-                    state = jasmine.initializePlayer();
-                    btnPlay = state.el.find('.btn-play');
-
-                    if (data.isShown) {
-                        expect(btnPlay).not.toHaveClass('is-hidden');
-                    } else {
-                        expect(btnPlay).toHaveClass('is-hidden');
-                    }
-                });
-            });
-
-            $.each(['iPad', 'Android'], function (index, device) {
-                it(
-                    'is shown on paused video on ' + device +
-                    ' in HTML5 player',
-                    function ()
-                {
-                    var btnPlay;
-
-                    window.onTouchBasedDevice.andReturn([device]);
-                    state = jasmine.initializePlayer();
-                    btnPlay = state.el.find('.btn-play');
-
-                    state.videoControl.play();
-                    state.videoControl.pause();
-
-                    expect(btnPlay).not.toHaveClass('is-hidden');
-                });
-
-                it(
-                    'is hidden on playing video on ' + device +
-                    ' in HTML5 player',
-                    function ()
-                {
-                    var btnPlay;
-
-                    window.onTouchBasedDevice.andReturn([device]);
-                    state = jasmine.initializePlayer();
-                    btnPlay = state.el.find('.btn-play');
-
-                    state.videoControl.play();
-
-                    expect(btnPlay).toHaveClass('is-hidden');
-                });
-
-                it(
-                    'is hidden on paused video on ' + device +
-                    ' in YouTube player',
-                    function ()
-                {
-                    var btnPlay;
-
-                    window.onTouchBasedDevice.andReturn([device]);
-                    state = jasmine.initializePlayerYouTube();
-                    btnPlay = state.el.find('.btn-play');
-
-                    state.videoControl.play();
-                    state.videoControl.pause();
-
-                    expect(btnPlay).toHaveClass('is-hidden');
-                });
-            });
-        });
-
         it('show', function () {
             var controls;
-
             state = jasmine.initializePlayer();
             controls = state.el.find('.video-controls');
             controls.addClass('is-hidden');
@@ -843,5 +580,23 @@
             state.videoControl.show();
             expect(controls).not.toHaveClass('is-hidden');
         });
+
+        it('can destroy itself', function () {
+            state = jasmine.initializePlayer();
+            state.videoControl.destroy();
+            expect(state.videoControl).toBeUndefined();
+        });
+
+        it('can focus the first control', function () {
+            var btnPlay;
+            state = jasmine.initializePlayer({focusFirstControl: true});
+            btnPlay = state.el.find('.video-controls .play');
+            waitsFor(function () {
+                return state.el.hasClass('is-initialized');
+            }, 'Player is not initialized', WAIT_TIMEOUT);
+            runs(function () {
+                expect(btnPlay).toBeFocused();
+            });
+        });
     });
 }).call(this, window.WAIT_TIMEOUT);
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_events_bumper_plugin_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_events_bumper_plugin_spec.js
new file mode 100644
index 00000000000..e41b40e782e
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_events_bumper_plugin_spec.js
@@ -0,0 +1,157 @@
+(function (undefined) {
+    'use strict';
+    describe('VideoPlayer Events Bumper plugin', function () {
+        var state, oldOTBD;
+
+        beforeEach(function () {
+            oldOTBD = window.onTouchBasedDevice;
+            window.onTouchBasedDevice = jasmine
+                .createSpy('onTouchBasedDevice')
+                .andReturn(null);
+
+            jasmine.stubRequests();
+            state = jasmine.initializePlayer('video_with_bumper.html');
+            spyOn(Logger, 'log');
+            $('.poster .btn-play').click();
+            spyOn(state.bumperState.videoEventsBumperPlugin, 'getCurrentTime').andReturn(10);
+            spyOn(state.bumperState.videoEventsBumperPlugin, 'getDuration').andReturn(20);
+        });
+
+        afterEach(function () {
+            $('source').remove();
+            window.onTouchBasedDevice = oldOTBD;
+            state.storage.clear();
+            if (state.bumperState && state.bumperState.videoPlayer) {
+                state.bumperState.videoPlayer.destroy();
+            }
+            if (state.videoPlayer) {
+                state.videoPlayer.destroy();
+            }
+        });
+
+        it('can emit "edx.video.bumper.loaded" event', function () {
+            state.el.trigger('ready');
+            expect(Logger.log).toHaveBeenCalledWith('edx.video.bumper.loaded', {
+                host_component_id: 'id',
+                bumper_id: 'xmodule/include/fixtures/test.mp4',
+                code: 'html5',
+                duration: 20
+            });
+        });
+
+        it('can emit "edx.video.bumper.played" event', function () {
+            state.el.trigger('play');
+            expect(Logger.log).toHaveBeenCalledWith('edx.video.bumper.played', {
+                host_component_id: 'id',
+                bumper_id: 'xmodule/include/fixtures/test.mp4',
+                code: 'html5',
+                currentTime: 10,
+                duration: 20
+            });
+        });
+
+        it('can emit "edx.video.bumper.stopped" event', function () {
+            state.el.trigger('ended');
+            expect(Logger.log).toHaveBeenCalledWith('edx.video.bumper.stopped', {
+                host_component_id: 'id',
+                bumper_id: 'xmodule/include/fixtures/test.mp4',
+                code: 'html5',
+                currentTime: 10,
+                duration: 20
+            });
+
+            Logger.log.reset();
+            state.el.trigger('stop');
+            expect(Logger.log).toHaveBeenCalledWith('edx.video.bumper.stopped', {
+                host_component_id: 'id',
+                bumper_id: 'xmodule/include/fixtures/test.mp4',
+                code: 'html5',
+                currentTime: 10,
+                duration: 20
+            });
+        });
+
+        it('can emit "edx.video.bumper.skipped" event', function () {
+            state.el.trigger('skip', [false]);
+            expect(Logger.log).toHaveBeenCalledWith('edx.video.bumper.skipped', {
+                host_component_id: 'id',
+                bumper_id: 'xmodule/include/fixtures/test.mp4',
+                code: 'html5',
+                currentTime: 10,
+                duration: 20
+            });
+        });
+
+        it('can emit "edx.video.bumper.dismissed" event', function () {
+            state.el.trigger('skip', [true]);
+            expect(Logger.log).toHaveBeenCalledWith('edx.video.bumper.dismissed', {
+                host_component_id: 'id',
+                bumper_id: 'xmodule/include/fixtures/test.mp4',
+                code: 'html5',
+                currentTime: 10,
+                duration: 20
+            });
+        });
+
+        it('can emit "edx.video.bumper.transcript.menu.shown" event', function () {
+            state.el.trigger('language_menu:show');
+            expect(Logger.log).toHaveBeenCalledWith('edx.video.bumper.transcript.menu.shown', {
+                host_component_id: 'id',
+                bumper_id: 'xmodule/include/fixtures/test.mp4',
+                code: 'html5',
+                duration: 20
+            });
+        });
+
+        it('can emit "edx.video.bumper.transcript.menu.hidden" event', function () {
+            state.el.trigger('language_menu:hide');
+            expect(Logger.log).toHaveBeenCalledWith('edx.video.bumper.transcript.menu.hidden', {
+                host_component_id: 'id',
+                bumper_id: 'xmodule/include/fixtures/test.mp4',
+                code: 'html5',
+                duration: 20
+            });
+        });
+
+        it('can emit "edx.video.bumper.transcript.shown" event', function () {
+            state.el.trigger('captions:show');
+            expect(Logger.log).toHaveBeenCalledWith('edx.video.bumper.transcript.shown', {
+                host_component_id: 'id',
+                bumper_id: 'xmodule/include/fixtures/test.mp4',
+                code: 'html5',
+                currentTime: 10,
+                duration: 20
+            });
+        });
+
+        it('can emit "edx.video.bumper.transcript.hidden" event', function () {
+            state.el.trigger('captions:hide');
+            expect(Logger.log).toHaveBeenCalledWith('edx.video.bumper.transcript.hidden', {
+                host_component_id: 'id',
+                bumper_id: 'xmodule/include/fixtures/test.mp4',
+                code: 'html5',
+                currentTime: 10,
+                duration: 20
+            });
+        });
+
+        it('can destroy itself', function () {
+            var plugin = state.bumperState.videoEventsBumperPlugin;
+            spyOn($.fn, 'off').andCallThrough();
+            plugin.destroy();
+            expect(state.bumperState.videoEventsBumperPlugin).toBeUndefined();
+            expect($.fn.off).toHaveBeenCalledWith({
+                'ready': plugin.onReady,
+                'play': plugin.onPlay,
+                'ended stop': plugin.onEnded,
+                'skip': plugin.onSkip,
+                'language_menu:show': plugin.onShowLanguageMenu,
+                'language_menu:hide': plugin.onHideLanguageMenu,
+                'captions:show': plugin.onShowCaptions,
+                'captions:hide': plugin.onHideCaptions,
+                'destroy': plugin.destroy
+            });
+        });
+    });
+
+}).call(this);
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_events_plugin_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_events_plugin_spec.js
new file mode 100644
index 00000000000..78352e575f1
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_events_plugin_spec.js
@@ -0,0 +1,166 @@
+(function (undefined) {
+    'use strict';
+    describe('VideoPlayer Events plugin', function () {
+        var state, oldOTBD;
+
+        beforeEach(function () {
+            oldOTBD = window.onTouchBasedDevice;
+            window.onTouchBasedDevice = jasmine
+                .createSpy('onTouchBasedDevice')
+                .andReturn(null);
+
+            jasmine.stubRequests();
+            state = jasmine.initializePlayer();
+            spyOn(Logger, 'log');
+            spyOn(state.videoEventsPlugin, 'getCurrentTime').andReturn(10);
+        });
+
+        afterEach(function () {
+            $('source').remove();
+            window.onTouchBasedDevice = oldOTBD;
+            state.storage.clear();
+            if (state.videoPlayer) {
+                state.videoPlayer.destroy();
+            }
+        });
+
+        it('can emit "load_video" event', function () {
+            state.el.trigger('ready');
+            expect(Logger.log).toHaveBeenCalledWith('load_video', {
+                id: 'id',
+                code: 'html5'
+            });
+        });
+
+        it('can emit "play_video" event', function () {
+            state.el.trigger('play');
+            expect(Logger.log).toHaveBeenCalledWith('play_video', {
+                id: 'id',
+                code: 'html5',
+                currentTime: 10
+            });
+        });
+
+        it('can emit "pause_video" event', function () {
+            state.el.trigger('pause');
+            expect(Logger.log).toHaveBeenCalledWith('pause_video', {
+                id: 'id',
+                code: 'html5',
+                currentTime: 10
+            });
+        });
+
+        it('can emit "speed_change_video" event', function () {
+            state.el.trigger('speedchange', ['2.0', '1.0']);
+            expect(Logger.log).toHaveBeenCalledWith('speed_change_video', {
+                id: 'id',
+                code: 'html5',
+                current_time: 10,
+                old_speed: '1.0',
+                new_speed: '2.0'
+            });
+        });
+
+        it('can emit "seek_video" event', function () {
+            state.el.trigger('seek', [1, 0, 'any']);
+            expect(Logger.log).toHaveBeenCalledWith('seek_video', {
+                id: 'id',
+                code: 'html5',
+                old_time: 0,
+                new_time: 1,
+                type: 'any'
+            });
+        });
+
+        it('can emit "stop_video" event', function () {
+            state.el.trigger('ended');
+            expect(Logger.log).toHaveBeenCalledWith('stop_video', {
+                id: 'id',
+                code: 'html5',
+                currentTime: 10
+            });
+
+            Logger.log.reset();
+            state.el.trigger('stop');
+            expect(Logger.log).toHaveBeenCalledWith('stop_video', {
+                id: 'id',
+                code: 'html5',
+                currentTime: 10
+            });
+        });
+
+        it('can emit "skip_video" event', function () {
+            state.el.trigger('skip', [false]);
+            expect(Logger.log).toHaveBeenCalledWith('skip_video', {
+                id: 'id',
+                code: 'html5',
+                currentTime: 10
+            });
+        });
+
+        it('can emit "do_not_show_again_video" event', function () {
+            state.el.trigger('skip', [true]);
+            expect(Logger.log).toHaveBeenCalledWith('do_not_show_again_video', {
+                id: 'id',
+                code: 'html5',
+                currentTime: 10
+            });
+        });
+
+        it('can emit "video_show_cc_menu" event', function () {
+            state.el.trigger('language_menu:show');
+            expect(Logger.log).toHaveBeenCalledWith('video_show_cc_menu', {
+                id: 'id',
+                code: 'html5'
+            });
+        });
+
+        it('can emit "video_hide_cc_menu" event', function () {
+            state.el.trigger('language_menu:hide');
+            expect(Logger.log).toHaveBeenCalledWith('video_hide_cc_menu', {
+                id: 'id',
+                code: 'html5'
+            });
+        });
+
+        it('can emit "show_transcript" event', function () {
+            state.el.trigger('captions:show');
+            expect(Logger.log).toHaveBeenCalledWith('show_transcript', {
+                id: 'id',
+                code: 'html5',
+                current_time: 10
+            });
+        });
+
+        it('can emit "hide_transcript" event', function () {
+            state.el.trigger('captions:hide');
+            expect(Logger.log).toHaveBeenCalledWith('hide_transcript', {
+                id: 'id',
+                code: 'html5',
+                current_time: 10
+            });
+        });
+
+        it('can destroy itself', function () {
+            var plugin = state.videoEventsPlugin;
+            spyOn($.fn, 'off').andCallThrough();
+            state.videoEventsPlugin.destroy();
+            expect(state.videoEventsPlugin).toBeUndefined();
+            expect($.fn.off).toHaveBeenCalledWith({
+                'ready': plugin.onReady,
+                'play': plugin.onPlay,
+                'pause': plugin.onPause,
+                'ended stop': plugin.onEnded,
+                'seek': plugin.onSeek,
+                'skip': plugin.onSkip,
+                'speedchange': plugin.onSpeedChange,
+                'language_menu:show': plugin.onShowLanguageMenu,
+                'language_menu:hide': plugin.onHideLanguageMenu,
+                'captions:show': plugin.onShowCaptions,
+                'captions:hide': plugin.onHideCaptions,
+                'destroy': plugin.destroy
+            });
+        });
+    });
+
+}).call(this);
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_focus_grabber_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_focus_grabber_spec.js
index ab3c12df6f5..5f69d2c7c78 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/video_focus_grabber_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_focus_grabber_spec.js
@@ -26,6 +26,7 @@
         afterEach(function () {
             // Turn jQuery animations back on.
             jQuery.fx.off = true;
+            state.videoPlayer.destroy();
         });
 
         it(
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_full_screen_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_full_screen_spec.js
new file mode 100644
index 00000000000..215b891f41f
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_full_screen_spec.js
@@ -0,0 +1,102 @@
+(function () {
+    'use strict';
+    describe('VideoFullScreen', function () {
+        var state, oldOTBD;
+
+        beforeEach(function () {
+            oldOTBD = window.onTouchBasedDevice;
+            window.onTouchBasedDevice = jasmine
+                .createSpy('onTouchBasedDevice').andReturn(null);
+        });
+
+        afterEach(function () {
+            $('source').remove();
+            state.storage.clear();
+            state.videoPlayer.destroy();
+            window.onTouchBasedDevice = oldOTBD;
+        });
+
+        describe('constructor', function () {
+            beforeEach(function () {
+                state = jasmine.initializePlayer();
+            });
+
+            it('renders the fullscreen control', function () {
+                expect($('.add-fullscreen')).toExist();
+                expect(state.videoFullScreen.fullScreenState).toBe(false);
+            });
+
+            it('correctly adds ARIA attributes to fullscreen control', function () {
+                var fullScreenControl = $('.add-fullscreen');
+
+                expect(fullScreenControl).toHaveAttrs({
+                    'role': 'button',
+                    'title': 'Fill browser',
+                    'aria-disabled': 'false'
+                });
+            });
+
+            it('correctly triggers the event handler to toggle fullscreen mode', function () {
+                spyOn(state.videoFullScreen, 'exit');
+                spyOn(state.videoFullScreen, 'enter');
+
+                state.videoFullScreen.fullScreenState = false;
+                state.videoFullScreen.toggle();
+                expect(state.videoFullScreen.enter).toHaveBeenCalled();
+
+                state.videoFullScreen.fullScreenState = true;
+                state.videoFullScreen.toggle();
+                expect(state.videoFullScreen.exit).toHaveBeenCalled();
+            });
+
+            it('correctly updates ARIA on state change', function () {
+                var fullScreenControl = $('.add-fullscreen');
+                fullScreenControl.click();
+                expect(fullScreenControl).toHaveAttrs({
+                    'role': 'button',
+                    'title': 'Exit full browser',
+                    'aria-disabled': 'false'
+                });
+                fullScreenControl.click();
+                expect(fullScreenControl).toHaveAttrs({
+                    'role': 'button',
+                    'title': 'Fill browser',
+                    'aria-disabled': 'false'
+                });
+            });
+
+            it('correctly can out of fullscreen by pressing esc', function () {
+                spyOn(state.videoCommands, 'execute');
+                var esc = $.Event('keyup');
+                esc.keyCode = 27;
+                state.isFullScreen = true;
+                $(document).trigger(esc);
+                expect(state.videoCommands.execute).toHaveBeenCalledWith('toggleFullScreen');
+            });
+
+            it('can update video dimensions on state change', function () {
+                state.el.trigger('fullscreen', [true]);
+                expect(state.resizer.setMode).toHaveBeenCalledWith('both');
+                state.el.trigger('fullscreen', [false]);
+                expect(state.resizer.setMode).toHaveBeenCalledWith('width');
+            });
+
+            it('can destroy itself', function () {
+                state.videoFullScreen.destroy();
+                expect($('.add-fullscreen')).not.toExist();
+                expect(state.videoFullScreen).toBeUndefined();
+            });
+        });
+
+        it('Controls height is actual on switch to fullscreen', function () {
+            spyOn($.fn, 'height').andCallFake(function (val) {
+                return _.isUndefined(val) ? 100: this;
+            });
+
+            state = jasmine.initializePlayer();
+            $(state.el).trigger('fullscreen');
+
+            expect(state.videoFullScreen.height).toBe(150);
+        });
+    });
+}).call(this);
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_play_pause_control_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_play_pause_control_spec.js
new file mode 100644
index 00000000000..877dc9861e2
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_play_pause_control_spec.js
@@ -0,0 +1,68 @@
+(function () {
+    'use strict';
+    describe('VideoPlayPauseControl', function () {
+        var state, oldOTBD;
+
+        beforeEach(function () {
+            oldOTBD = window.onTouchBasedDevice;
+            window.onTouchBasedDevice = jasmine
+                .createSpy('onTouchBasedDevice').andReturn(null);
+            state = jasmine.initializePlayer();
+            spyOn(state.videoCommands, 'execute');
+            spyOn(state.videoSaveStatePlugin, 'saveState');
+        });
+
+        afterEach(function () {
+            $('source').remove();
+            state.storage.clear();
+            state.videoPlayer.destroy();
+            window.onTouchBasedDevice = oldOTBD;
+        });
+
+        it('can render the control', function () {
+            expect($('.video_control.play')).toExist();
+        });
+
+        it('add ARIA attributes to play control', function () {
+            expect($('.video_control.play')).toHaveAttrs({
+                'role': 'button',
+                'title': 'Play',
+                'aria-disabled': 'false'
+            });
+        });
+
+        it('can update ARIA state on play', function () {
+            state.el.trigger('play');
+            expect($('.video_control.pause')).toHaveAttrs({
+                'role': 'button',
+                'title': 'Pause',
+                'aria-disabled': 'false'
+            });
+        });
+
+        it('can update ARIA state on video ends', function () {
+            state.el.trigger('play');
+            state.el.trigger('ended');
+            expect($('.video_control.play')).toHaveAttrs({
+                'role': 'button',
+                'title': 'Play',
+                'aria-disabled': 'false'
+            });
+        });
+
+        it('can update state on pause', function () {
+            state.el.trigger('pause');
+            expect(state.videoSaveStatePlugin.saveState).toHaveBeenCalledWith(true);
+        });
+
+        it('can start video playing on click', function () {
+            $('.video_control.play').click();
+            expect(state.videoCommands.execute).toHaveBeenCalledWith('togglePlayback');
+        });
+
+        it('can destroy itself', function () {
+            state.videoPlayPauseControl.destroy();
+            expect(state.videoPlayPauseControl).toBeUndefined();
+        });
+    });
+}).call(this);
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_play_placeholder_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_play_placeholder_spec.js
new file mode 100644
index 00000000000..d99c12e24cc
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_play_placeholder_spec.js
@@ -0,0 +1,151 @@
+(function () {
+    'use strict';
+    describe('VideoPlayPlaceholder', function () {
+        var state, oldOTBD;
+
+        beforeEach(function () {
+            oldOTBD = window.onTouchBasedDevice;
+            window.onTouchBasedDevice = jasmine
+                .createSpy('onTouchBasedDevice').andReturn(['iPad']);
+
+            state = jasmine.initializePlayer();
+            spyOn(state.videoCommands, 'execute');
+        });
+
+        afterEach(function () {
+            $('source').remove();
+            state.storage.clear();
+            state.videoPlayer.destroy();
+            window.onTouchBasedDevice = oldOTBD;
+        });
+
+        var cases = [
+            {
+                name: 'PC',
+                isShown: false,
+                isTouch: null
+            }, {
+                name: 'iPad',
+                isShown: true,
+                isTouch: ['iPad']
+            }, {
+                name: 'Android',
+                isShown: true,
+                isTouch: ['Android']
+            }, {
+                name: 'iPhone',
+                isShown: false,
+                isTouch: ['iPhone']
+            }
+        ];
+
+        beforeEach(function () {
+            jasmine.stubRequests();
+            spyOn(window.YT, 'Player').andCallThrough();
+        });
+
+        it ('works correctly on calling proper methods', function () {
+            var btnPlay;
+
+            state = jasmine.initializePlayer();
+            btnPlay = state.el.find('.btn-play');
+
+            state.videoPlayPlaceholder.show();
+
+            expect(btnPlay).not.toHaveClass('is-hidden');
+            expect(btnPlay).toHaveAttrs({
+                'aria-hidden': 'false',
+                'tabindex': 0
+            });
+
+            state.videoPlayPlaceholder.hide();
+
+            expect(btnPlay).toHaveClass('is-hidden');
+            expect(btnPlay).toHaveAttrs({
+                'aria-hidden': 'true',
+                'tabindex': -1
+            });
+        });
+
+        $.each(cases, function (index, data) {
+            var message = [
+                (data.isShown) ? 'is' : 'is not',
+                ' shown on',
+                data.name
+            ].join('');
+
+            it(message, function () {
+                var btnPlay;
+
+                window.onTouchBasedDevice.andReturn(data.isTouch);
+                state = jasmine.initializePlayer();
+                btnPlay = state.el.find('.btn-play');
+
+                if (data.isShown) {
+                    expect(btnPlay).not.toHaveClass('is-hidden');
+                } else {
+                    expect(btnPlay).toHaveClass('is-hidden');
+                }
+            });
+        });
+
+        $.each(['iPad', 'Android'], function (index, device) {
+            it(
+                'is shown on paused video on ' + device +
+                ' in HTML5 player',
+                function ()
+            {
+                var btnPlay;
+
+                window.onTouchBasedDevice.andReturn([device]);
+                state = jasmine.initializePlayer();
+                btnPlay = state.el.find('.btn-play');
+
+                state.el.trigger('play');
+                state.el.trigger('pause');
+                expect(btnPlay).not.toHaveClass('is-hidden');
+            });
+
+            it(
+                'is hidden on playing video on ' + device +
+                ' in HTML5 player',
+                function ()
+            {
+                var btnPlay;
+
+                window.onTouchBasedDevice.andReturn([device]);
+                state = jasmine.initializePlayer();
+                btnPlay = state.el.find('.btn-play');
+
+                state.el.trigger('play');
+                expect(btnPlay).toHaveClass('is-hidden');
+            });
+
+            it(
+                'is hidden on paused video on ' + device +
+                ' in YouTube player',
+                function ()
+            {
+                var btnPlay;
+
+                window.onTouchBasedDevice.andReturn([device]);
+                state = jasmine.initializePlayerYouTube();
+                btnPlay = state.el.find('.btn-play');
+
+                state.el.trigger('play');
+                state.el.trigger('pause');
+                expect(btnPlay).toHaveClass('is-hidden');
+            });
+        });
+
+        it('starts play the video on click', function () {
+            $('.btn-play').click();
+            expect(state.videoCommands.execute).toHaveBeenCalledWith('play');
+        });
+
+        it('can destroy itself', function () {
+            state.videoPlayPlaceholder.destroy();
+            expect(state.videoPlayPlaceholder).toBeUndefined();
+        });
+    });
+}).call(this);
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_play_skip_control_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_play_skip_control_spec.js
new file mode 100644
index 00000000000..9ccea6a0ab9
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_play_skip_control_spec.js
@@ -0,0 +1,64 @@
+(function () {
+    'use strict';
+    describe('VideoPlaySkipControl', function () {
+        var state, oldOTBD;
+
+        beforeEach(function () {
+            oldOTBD = window.onTouchBasedDevice;
+            window.onTouchBasedDevice = jasmine
+                .createSpy('onTouchBasedDevice').andReturn(null);
+            state = jasmine.initializePlayer('video_with_bumper.html');
+            $('.poster .btn-play').click();
+            spyOn(state.bumperState.videoCommands, 'execute');
+        });
+
+        afterEach(function () {
+            $('source').remove();
+            state.storage.clear();
+            if (state.bumperState && state.bumperState.videoPlayer) {
+                state.bumperState.videoPlayer.destroy();
+            }
+            window.onTouchBasedDevice = oldOTBD;
+        });
+
+        it('can render the control', function () {
+            expect($('.video_control.play')).toExist();
+        });
+
+        it('add ARIA attributes to play control', function () {
+            expect($('.video_control.play')).toHaveAttrs({
+                'role': 'button',
+                'title': 'Play',
+                'aria-disabled': 'false'
+            });
+        });
+
+        it('can update state on play', function () {
+            state.el.trigger('play');
+            expect($('.video_control.play')).not.toExist();
+            expect($('.video_control.skip')).toExist();
+        });
+
+        it('can start video playing on click', function () {
+            $('.video_control.play').click();
+            expect(state.bumperState.videoCommands.execute).toHaveBeenCalledWith('play');
+        });
+
+        it('can skip the video on click', function () {
+            state.el.trigger('play');
+            spyOn(state.bumperState.videoPlayer, 'isPlaying').andReturn(true);
+            $('.video_control.skip').first().click();
+            expect(state.bumperState.videoCommands.execute).toHaveBeenCalledWith('skip');
+        });
+
+        it('can destroy itself', function () {
+            var plugin = state.bumperState.videoPlaySkipControl,
+                el = plugin.el;
+            spyOn($.fn, 'off').andCallThrough();
+            plugin.destroy();
+            expect(state.bumperState.videoPlaySkipControl).toBeUndefined();
+            expect(el).not.toExist();
+            expect($.fn.off).toHaveBeenCalledWith('destroy', plugin.destroy);
+        });
+    });
+}).call(this);
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js
index da910d7bb0f..fc74ef92293 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js
@@ -1,5 +1,4 @@
 (function (requirejs, require, define, undefined) {
-
 'use strict';
 
 require(
@@ -21,6 +20,9 @@ function (VideoPlayer) {
             if (state.storage) {
                 state.storage.clear();
             }
+            if (state.videoPlayer) {
+                _.result(state.videoPlayer, 'destroy');
+            }
         });
 
         describe('constructor', function () {
@@ -47,7 +49,7 @@ function (VideoPlayer) {
                     expect(state.videoCaption).toBeDefined();
                     expect(state.speed).toEqual('1.50');
                     expect(state.config.transcriptTranslationUrl)
-                        .toEqual('/transcript/translation');
+                        .toEqual('/transcript/translation/__lang__');
                 });
 
                 it('create video speed control', function () {
@@ -71,18 +73,15 @@ function (VideoPlayer) {
                 var events;
 
                 jasmine.stubRequests();
-
                 spyOn(window.YT, 'Player').andCallThrough();
-
                 state = jasmine.initializePlayerYouTube();
-
                 state.videoEl = $('video, iframe');
 
                 events = {
                     onReady:                 state.videoPlayer.onReady,
                     onStateChange:           state.videoPlayer.onStateChange,
-                    onPlaybackQualityChange: state.videoPlayer
-                        .onPlaybackQualityChange
+                    onPlaybackQualityChange: state.videoPlayer.onPlaybackQualityChange,
+                    onError:                 state.videoPlayer.onError
                 };
 
                 expect(YT.Player).toHaveBeenCalledWith('id', {
@@ -156,7 +155,7 @@ function (VideoPlayer) {
                 });
 
                 it('controls are in paused state', function () {
-                    expect(state.videoControl.isPlaying).toBe(false);
+                    expect(state.videoPlayer.isPlaying()).toBe(false);
                 });
             });
         });
@@ -166,16 +165,10 @@ function (VideoPlayer) {
                 state = jasmine.initializePlayer();
 
                 state.videoEl = $('video, iframe');
-
-                spyOn(state.videoPlayer, 'log').andCallThrough();
                 spyOn(state.videoPlayer, 'play').andCallThrough();
                 state.videoPlayer.onReady();
             });
 
-            it('log the load_video event', function () {
-                expect(state.videoPlayer.log).toHaveBeenCalledWith('load_video');
-            });
-
             it('autoplay the first video', function () {
                 expect(state.videoPlayer.play).not.toHaveBeenCalled();
             });
@@ -197,9 +190,7 @@ function (VideoPlayer) {
                 var playbackRates = state.videoPlayer.player.getAvailablePlaybackRates();
 
                 state.currentPlayerMode = 'flash';
-
                 state.videoPlayer.onReady();
-
                 expect(playbackRates.length).toBe(4);
                 expect(state.currentPlayerMode).toBe('html5');
             });
@@ -209,10 +200,7 @@ function (VideoPlayer) {
             describe('when the video is unstarted', function () {
                 beforeEach(function () {
                     state = jasmine.initializePlayer();
-
                     state.videoEl = $('video, iframe');
-
-                    spyOn(state.videoControl, 'pause').andCallThrough();
                     spyOn($.fn, 'trigger').andCallThrough();
 
                     state.videoPlayer.onStateChange({
@@ -221,7 +209,7 @@ function (VideoPlayer) {
                 });
 
                 it('pause the video control', function () {
-                    expect(state.videoControl.pause).toHaveBeenCalled();
+                    expect($('.video_control')).toHaveClass('play');
                 });
 
                 it('pause the video caption', function () {
@@ -244,9 +232,7 @@ function (VideoPlayer) {
 
                     state.videoEl = $('video, iframe');
 
-                    spyOn(state.videoPlayer, 'log').andCallThrough();
                     spyOn(window, 'setInterval').andReturn(100);
-                    spyOn(state.videoControl, 'play');
                     spyOn($.fn, 'trigger').andCallThrough();
 
                     state.videoPlayer.onStateChange({
@@ -254,23 +240,6 @@ function (VideoPlayer) {
                     });
                 });
 
-                it('speed_change_video event is not logged when speed not change', function () {
-                    expect(state.videoPlayer.log).not.toHaveBeenCalledWith(
-                        'speed_change_video',
-                        {
-                            current_time: state.videoPlayer.currentTime,
-                            old_speed: state.speed,
-                            new_speed: state.speed
-                        }
-                    );
-                });
-
-                it('log the play_video event', function () {
-                    expect(state.videoPlayer.log).toHaveBeenCalledWith(
-                        'play_video', { currentTime: 0 }
-                    );
-                });
-
                 it('set update interval', function () {
                     expect(window.setInterval).toHaveBeenCalledWith(
                         state.videoPlayer.update, 200
@@ -279,7 +248,7 @@ function (VideoPlayer) {
                 });
 
                 it('play the video control', function () {
-                    expect(state.videoControl.play).toHaveBeenCalled();
+                    expect($('.video_control')).toHaveClass('pause');
                 });
 
                 it('play the video caption', function () {
@@ -295,10 +264,7 @@ function (VideoPlayer) {
 
                     state.videoEl = $('video, iframe');
 
-                    spyOn(state.videoPlayer, 'log').andCallThrough();
-                    spyOn(state.videoControl, 'pause').andCallThrough();
                     spyOn($.fn, 'trigger').andCallThrough();
-
                     state.videoPlayer.onStateChange({
                         data: YT.PlayerState.PLAYING
                     });
@@ -310,18 +276,12 @@ function (VideoPlayer) {
                     });
                 });
 
-                it('log the pause_video event', function () {
-                    expect(state.videoPlayer.log).toHaveBeenCalledWith(
-                        'pause_video', { currentTime: 0 }
-                    );
-                });
-
                 it('clear update interval', function () {
                     expect(state.videoPlayer.updateInterval).toBeUndefined();
                 });
 
                 it('pause the video control', function () {
-                    expect(state.videoControl.pause).toHaveBeenCalled();
+                    expect($('.video_control')).toHaveClass('play');
                 });
 
                 it('pause the video caption', function () {
@@ -334,32 +294,19 @@ function (VideoPlayer) {
                     state = jasmine.initializePlayer();
 
                     state.videoEl = $('video, iframe');
-
-                    spyOn(state.videoPlayer, 'log').andCallThrough();
-                    spyOn(state.videoControl, 'pause').andCallThrough();
                     spyOn($.fn, 'trigger').andCallThrough();
-
                     state.videoPlayer.onStateChange({
                         data: YT.PlayerState.ENDED
                     });
                 });
 
                 it('pause the video control', function () {
-                    expect(state.videoControl.pause).toHaveBeenCalled();
+                    expect($('.video_control')).toHaveClass('play');
                 });
 
                 it('pause the video caption', function () {
                     expect($.fn.trigger).toHaveBeenCalledWith('ended', {});
                 });
-
-                it('log stop_video event', function () {
-                    expect(state.videoPlayer.log).toHaveBeenCalledWith(
-                        'stop_video',
-                        {
-                            currentTime: state.videoPlayer.currentTime
-                        }
-                    );
-                });
             });
         });
 
@@ -397,25 +344,6 @@ function (VideoPlayer) {
                     });
                 });
 
-                it('slider event causes log update', function () {
-                    runs(function () {
-                        spyOn(state.videoPlayer, 'log');
-                        state.videoProgressSlider.onSlide(
-                            jQuery.Event('slide'), { value: 2 }
-                        );
-                        // Video player uses _.debounce (with a wait time in 300 ms) for seeking.
-                        // That's why we have to do this tick(300).
-                        jasmine.Clock.tick(300);
-                        expect(state.videoPlayer.currentTime).toBe(2);
-
-                        expect(state.videoPlayer.log).toHaveBeenCalledWith('seek_video', {
-                            old_time: jasmine.any(Number),
-                            new_time: 2,
-                            type: 'onSlideSeek'
-                        });
-                    });
-                });
-
                 it('seek the player', function () {
                     runs(function () {
                         spyOn(state.videoPlayer.player, 'seekTo').andCallThrough();
@@ -469,24 +397,6 @@ function (VideoPlayer) {
                         .andCallThrough();
                 });
 
-                it('slider event causes log update', function () {
-                    spyOn(state.videoPlayer, 'log');
-                    state.videoProgressSlider.onSlide(
-                        jQuery.Event('slide'), { value: 2 }
-                    );
-                    // Video player uses _.debounce (with a wait time in 300 ms) for seeking.
-                    // That's why we have to do this tick(300).
-                    jasmine.Clock.tick(300);
-                    expect(state.videoPlayer.currentTime).toBe(2);
-                    expect(state.videoPlayer.log).toHaveBeenCalledWith(
-                        'seek_video', {
-                            old_time: 0,
-                            new_time: 2,
-                            type: 'onSlideSeek'
-                        }
-                    );
-                });
-
                 it('video has a correct speed', function () {
                     state.speed = '2.0';
                     state.videoPlayer.onPlay();
@@ -785,7 +695,7 @@ function (VideoPlayer) {
                     state = jasmine.initializePlayer();
                     state.videoEl = $('video, iframe');
                     spyOn($.fn, 'trigger').andCallThrough();
-                    state.videoControl.toggleFullScreen(jQuery.Event('click'));
+                    $('.add-fullscreen').click();
                 });
 
                 it('replace the full screen button tooltip', function () {
@@ -810,11 +720,10 @@ function (VideoPlayer) {
                     state.videoEl = $('video, iframe');
                     spyOn($.fn, 'trigger').andCallThrough();
                     state.el.addClass('video-fullscreen');
-                    state.videoControl.fullScreenState = true;
-                    state.videoControl.isFullScreen = true;
-                    state.videoControl.fullScreenEl.attr('title', 'Exit-fullscreen');
-
-                    state.videoControl.toggleFullScreen(jQuery.Event('click'));
+                    state.videoFullScreen.fullScreenState = true;
+                    state.videoFullScreen.isFullScreen = true;
+                    state.videoFullScreen.fullScreenEl.attr('title', 'Exit-fullscreen');
+                    $('.add-fullscreen').click();
                 });
 
                 it('replace the full screen button tooltip', function () {
@@ -835,83 +744,6 @@ function (VideoPlayer) {
             });
         });
 
-        describe('play', function () {
-            beforeEach(function () {
-                state = jasmine.initializePlayer();
-
-                state.videoEl = $('video, iframe');
-
-                spyOn(state.videoPlayer.player, 'playVideo').andCallThrough();
-            });
-
-            describe('when the player is not ready', function () {
-                beforeEach(function () {
-                    state.videoPlayer.player.playVideo = void 0;
-                    state.videoPlayer.play();
-                });
-
-                it('does nothing', function () {
-                    expect(state.videoPlayer.player.playVideo).toBeUndefined();
-                });
-            });
-
-            describe('when the player is ready', function () {
-                beforeEach(function () {
-                    state.videoPlayer.player.playVideo.andReturn(true);
-                    state.videoPlayer.play();
-                });
-
-                it('delegate to the player', function () {
-                    expect(state.videoPlayer.player.playVideo).toHaveBeenCalled();
-                });
-            });
-        });
-
-        describe('isPlaying', function () {
-            beforeEach(function () {
-                state = jasmine.initializePlayer();
-
-                state.videoEl = $('video, iframe');
-
-                spyOn(state.videoPlayer.player, 'getPlayerState').andCallThrough();
-            });
-
-            describe('when the video is playing', function () {
-                beforeEach(function () {
-                    state.videoPlayer.player.getPlayerState.andReturn(YT.PlayerState.PLAYING);
-                });
-
-                it('return true', function () {
-                    expect(state.videoPlayer.isPlaying()).toBeTruthy();
-                });
-            });
-
-            describe('when the video is not playing', function () {
-                beforeEach(function () {
-                    state.videoPlayer.player.getPlayerState.andReturn(YT.PlayerState.PAUSED);
-                });
-
-                it('return false', function () {
-                    expect(state.videoPlayer.isPlaying()).toBeFalsy();
-                });
-            });
-        });
-
-        describe('pause', function () {
-            beforeEach(function () {
-                state = jasmine.initializePlayer();
-
-                state.videoEl = $('video, iframe');
-
-                spyOn(state.videoPlayer.player, 'pauseVideo').andCallThrough();
-                state.videoPlayer.pause();
-            });
-
-            it('delegate to the player', function () {
-                expect(state.videoPlayer.player.pauseVideo).toHaveBeenCalled();
-            });
-        });
-
         describe('duration', function () {
             beforeEach(function () {
                 state = jasmine.initializePlayer();
@@ -1016,9 +848,7 @@ function (VideoPlayer) {
 
                     runs(function () {
                         state = jasmine.initializePlayer();
-
                         state.videoEl = $('video, iframe');
-
                         controls = state.el.find('.video-controls');
                     });
 
@@ -1053,7 +883,6 @@ function (VideoPlayer) {
                     saveState: jasmine.createSpy(),
                     videoPlayer: {
                         currentTime: 60,
-                        log: jasmine.createSpy(),
                         updatePlayTime: jasmine.createSpy(),
                         setPlaybackRate: jasmine.createSpy(),
                         player: jasmine.createSpyObj('player', ['setPlaybackRate'])
@@ -1063,18 +892,6 @@ function (VideoPlayer) {
             });
 
             describe('always', function () {
-                it('check if speed_change_video is logged', function () {
-                    VideoPlayer.prototype.onSpeedChange.call(state, '0.75', false);
-                    expect(state.videoPlayer.log).toHaveBeenCalledWith(
-                        'speed_change_video',
-                        {
-                            current_time: state.videoPlayer.currentTime,
-                            old_speed: '1.50',
-                            new_speed: '0.75'
-                        }
-                    );
-                });
-
                 it('convert the current time to the new speed', function () {
                     state.isFlashMode.andReturn(true);
                     VideoPlayer.prototype.onSpeedChange.call(state, '0.75', false);
@@ -1083,10 +900,7 @@ function (VideoPlayer) {
 
                 it('set video speed to the new speed', function () {
                     VideoPlayer.prototype.onSpeedChange.call(state, '0.75', false);
-                    expect(state.setSpeed).toHaveBeenCalledWith('0.75', true);
-                    expect(state.saveState).toHaveBeenCalledWith(true, {
-                        speed: '0.75'
-                    });
+                    expect(state.setSpeed).toHaveBeenCalledWith('0.75');
                     expect(state.videoPlayer.setPlaybackRate)
                         .toHaveBeenCalledWith('0.75');
                 });
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_poster_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_poster_spec.js
new file mode 100644
index 00000000000..18a6f6874c8
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_poster_spec.js
@@ -0,0 +1,42 @@
+(function (WAIT_TIMEOUT) {
+    'use strict';
+    describe('VideoPoster', function () {
+        var state, oldOTBD;
+
+        beforeEach(function () {
+            oldOTBD = window.onTouchBasedDevice;
+            window.onTouchBasedDevice = jasmine
+                .createSpy('onTouchBasedDevice').andReturn(null);
+            state = jasmine.initializePlayer('video_with_bumper.html');
+        });
+
+        afterEach(function () {
+            $('source').remove();
+            state.storage.clear();
+            if (state.bumperState && state.bumperState.videoPlayer) {
+                state.bumperState.videoPlayer.destroy();
+            }
+            if (state.videoPlayer) {
+                state.videoPlayer.destroy();
+            }
+            window.onTouchBasedDevice = oldOTBD;
+        });
+
+        it('can render the poster', function () {
+            expect($('.poster')).toExist();
+            expect($('.btn-play')).toExist();
+        });
+
+        it('can start playing the video on click', function () {
+            $('.btn-play').click();
+            waitsFor(function () {
+                return state.el.hasClass('is-playing');
+            }, 'Player is not playing.', WAIT_TIMEOUT);
+        });
+
+        it('destroy itself on "play" event', function () {
+            $('.btn-play').click();
+            expect($('.poster')).not.toExist();
+        });
+    });
+}).call(this, window.WAIT_TIMEOUT);
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_progress_slider_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_progress_slider_spec.js
index d7a56857190..491e98fae72 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/video_progress_slider_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_progress_slider_spec.js
@@ -12,6 +12,7 @@
             $('source').remove();
             window.onTouchBasedDevice = oldOTBD;
             state.storage.clear();
+            state.videoPlayer.destroy();
         });
 
         describe('constructor', function () {
@@ -38,6 +39,18 @@
                     expect(state.videoProgressSlider.handle)
                         .toBe('.slider .ui-slider-handle');
                 });
+
+                it('add ARIA attributes to time control', function () {
+                    var timeControl = $('div.slider > a');
+
+                    expect(timeControl).toHaveAttrs({
+                        'role': 'slider',
+                        'title': 'Video position',
+                        'aria-disabled': 'false'
+                    });
+
+                    expect(timeControl).toHaveAttr('aria-valuetext');
+                });
             });
 
             describe('on a touch-based device', function () {
@@ -304,6 +317,13 @@
             });
         });
 
+        it('can destroy itself', function () {
+            state = jasmine.initializePlayer();
+            state.videoProgressSlider.destroy();
+            expect(state.videoProgressSlider).toBeUndefined();
+            expect($('.slider')).toBeEmpty();
+        });
+
     });
 
 }).call(this);
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_quality_control_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_quality_control_spec.js
index 1ade3cb9ced..0bf3722a4c4 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/video_quality_control_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_quality_control_spec.js
@@ -1,12 +1,13 @@
 (function (undefined) {
     describe('VideoQualityControl', function () {
-        var state, qualityControl, qualityControlEl, videoPlayer, player;
+        var state, qualityControl, videoPlayer, player;
 
         afterEach(function () {
             $('source').remove();
             if (state.storage) {
                 state.storage.clear();
             }
+            state.videoPlayer.destroy();
         });
 
         describe('constructor, YouTube mode', function () {
@@ -105,6 +106,11 @@
                  expect(qualityControl.el).toHaveClass('active');
             });
 
+            it('can destroy itself', function () {
+                state.videoQualityControl.destroy();
+                expect(state.videoQualityControl).toBeUndefined();
+                expect($('.quality-control')).not.toExist();
+            });
         });
 
         describe('constructor, HTML5 mode', function () {
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_save_state_plugin_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_save_state_plugin_spec.js
new file mode 100644
index 00000000000..7c101cdb324
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_save_state_plugin_spec.js
@@ -0,0 +1,230 @@
+(function (undefined) {
+    'use strict';
+    describe('VideoPlayer Save State plugin', function () {
+        var state, oldOTBD;
+
+        beforeEach(function () {
+            oldOTBD = window.onTouchBasedDevice;
+            window.onTouchBasedDevice = jasmine
+                .createSpy('onTouchBasedDevice')
+                .andReturn(null);
+
+            jasmine.stubRequests();
+            state = jasmine.initializePlayer();
+            spyOn(state.storage, 'setItem');
+        });
+
+        afterEach(function () {
+            $('source').remove();
+            window.onTouchBasedDevice = oldOTBD;
+            state.storage.clear();
+            if (state.videoPlayer) {
+                state.videoPlayer.destroy();
+            }
+        });
+
+        describe('saveState function', function () {
+            var videoPlayerCurrentTime, newCurrentTime, speed;
+
+            // We make sure that `currentTime` is a float. We need to test
+            // that Math.round() is called.
+            videoPlayerCurrentTime = 3.1242;
+
+            // We have two times, because one is  stored in
+            // `videoPlayer.currentTime`, and the other is passed directly to
+            // `saveState` in `data` object. In each case, there is different
+            // code that handles these times. They have to be different for
+            // test completeness sake. Also, make sure it is float, as is the
+            // time above.
+            newCurrentTime = 5.4;
+            speed = '0.75';
+
+            beforeEach(function () {
+                state.videoPlayer.currentTime = videoPlayerCurrentTime;
+                spyOn(Time, 'formatFull').andCallThrough();
+            });
+
+            it('data is not an object, async is true', function () {
+                itSpec({
+                    asyncVal: true,
+                    speedVal: undefined,
+                    positionVal: videoPlayerCurrentTime,
+                    data: undefined,
+                    ajaxData: {
+                        saved_video_position: Time.formatFull(Math.round(videoPlayerCurrentTime))
+                    }
+                });
+            });
+
+            it('data contains speed, async is false', function () {
+                itSpec({
+                    asyncVal: false,
+                    speedVal: speed,
+                    positionVal: undefined,
+                    data: {
+                        speed: speed
+                    },
+                    ajaxData: {
+                        speed: speed
+                    }
+                });
+            });
+
+            it('data contains float position, async is true', function () {
+                itSpec({
+                    asyncVal: true,
+                    speedVal: undefined,
+                    positionVal: newCurrentTime,
+                    data: {
+                        saved_video_position: newCurrentTime
+                    },
+                    ajaxData: {
+                        saved_video_position: Time.formatFull(Math.round(newCurrentTime))
+                    }
+                });
+            });
+
+            it('data contains speed and rounded position, async is false', function () {
+                itSpec({
+                    asyncVal: false,
+                    speedVal: speed,
+                    positionVal: Math.round(newCurrentTime),
+                    data: {
+                        speed: speed,
+                        saved_video_position: Math.round(newCurrentTime)
+                    },
+                    ajaxData: {
+                        speed: speed,
+                        saved_video_position: Time.formatFull(Math.round(newCurrentTime))
+                    }
+                });
+            });
+
+            it('data contains empty object, async is true', function () {
+                itSpec({
+                    asyncVal: true,
+                    speedVal: undefined,
+                    positionVal: undefined,
+                    data: {},
+                    ajaxData: {}
+                });
+            });
+
+            it('data contains position 0, async is true', function () {
+                itSpec({
+                    asyncVal: true,
+                    speedVal: undefined,
+                    positionVal: 0,
+                    data: {
+                        saved_video_position: 0
+                    },
+                    ajaxData: {
+                        saved_video_position: Time.formatFull(Math.round(0))
+                    }
+                });
+            });
+
+            function itSpec(value) {
+                var asyncVal    = value.asyncVal,
+                    speedVal    = value.speedVal,
+                    positionVal = value.positionVal,
+                    data        = value.data,
+                    ajaxData    = value.ajaxData;
+
+                state.videoSaveStatePlugin.saveState(asyncVal, data);
+
+                if (speedVal) {
+                    expect(state.storage.setItem).toHaveBeenCalledWith(
+                        'speed',
+                        speedVal,
+                        true
+                    );
+                }
+                if (positionVal) {
+                    expect(state.storage.setItem).toHaveBeenCalledWith(
+                        'savedVideoPosition',
+                        positionVal,
+                        true
+                    );
+                    expect(Time.formatFull).toHaveBeenCalledWith(
+                        positionVal
+                    );
+                }
+                expect($.ajax).toHaveBeenCalledWith({
+                    url: state.config.saveStateUrl,
+                    type: 'POST',
+                    async: asyncVal,
+                    dataType: 'json',
+                    data: ajaxData
+                });
+            }
+        });
+
+        it('can save state on speed change', function () {
+            state.el.trigger('speedchange', ['2.0']);
+            expect($.ajax).toHaveBeenCalledWith({
+                url: state.config.saveStateUrl,
+                type: 'POST',
+                async: true,
+                dataType: 'json',
+                data: {speed: '2.0'}
+            });
+        });
+
+        it('can save state on page unload', function () {
+            $.ajax.reset();
+            state.videoSaveStatePlugin.onUnload();
+            expect($.ajax).toHaveBeenCalledWith({
+                url: state.config.saveStateUrl,
+                type: 'POST',
+                async: false,
+                dataType: 'json',
+                data: {saved_video_position: '00:00:00'}
+            });
+        });
+
+        it('can save state on pause', function () {
+            state.el.trigger('pause');
+            expect($.ajax).toHaveBeenCalledWith({
+                url: state.config.saveStateUrl,
+                type: 'POST',
+                async: true,
+                dataType: 'json',
+                data: {saved_video_position: '00:00:00'}
+            });
+        });
+
+        it('can save state on language change', function () {
+            state.el.trigger('language_menu:change', ['ua']);
+            expect(state.storage.setItem).toHaveBeenCalledWith('language', 'ua');
+        });
+
+        it('can save information about youtube availability', function () {
+            state.el.trigger('youtube_availability', [true]);
+            expect($.ajax).toHaveBeenCalledWith({
+                url: state.config.saveStateUrl,
+                type: 'POST',
+                async: true,
+                dataType: 'json',
+                data: {youtube_is_available: true}
+            });
+        });
+
+        it('can destroy itself', function () {
+            var plugin = state.videoSaveStatePlugin;
+            spyOn($.fn, 'off').andCallThrough();
+            state.videoSaveStatePlugin.destroy();
+            expect(state.videoSaveStatePlugin).toBeUndefined();
+            expect($.fn.off).toHaveBeenCalledWith({
+                'speedchange': plugin.onSpeedChange,
+                'play': plugin.bindUnloadHandler,
+                'pause destroy': plugin.saveStateHandler,
+                'language_menu:change': plugin.onLanguageChange,
+                'youtube_availability': plugin.onYoutubeAvailability
+            });
+            expect($.fn.off).toHaveBeenCalledWith('destroy', plugin.destroy);
+            expect($.fn.off).toHaveBeenCalledWith('unload', plugin.onUnload);
+        });
+    });
+
+}).call(this);
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_skip_control_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_skip_control_spec.js
new file mode 100644
index 00000000000..da3a87845b0
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_skip_control_spec.js
@@ -0,0 +1,55 @@
+(function () {
+    'use strict';
+    describe('VideoSkipControl', function () {
+        var state, oldOTBD;
+
+        beforeEach(function () {
+            oldOTBD = window.onTouchBasedDevice;
+            window.onTouchBasedDevice = jasmine
+                .createSpy('onTouchBasedDevice').andReturn(null);
+            state = jasmine.initializePlayer('video_with_bumper.html');
+            $('.poster .btn-play').click();
+            spyOn(state.bumperState.videoCommands, 'execute').andCallThrough();
+        });
+
+        afterEach(function () {
+            $('source').remove();
+            state.storage.clear();
+            if (state.bumperState && state.bumperState.videoPlayer) {
+                state.bumperState.videoPlayer.destroy();
+            }
+            if (state.videoPlayer) {
+                state.videoPlayer.destroy();
+            }
+            window.onTouchBasedDevice = oldOTBD;
+        });
+
+        it('can render the control when video starts playing', function () {
+            expect($('.skip-control')).not.toExist();
+            state.el.trigger('play');
+            expect($('.skip-control')).toExist();
+        });
+
+        it('add ARIA attributes to play control', function () {
+            state.el.trigger('play');
+            expect($('.skip-control')).toHaveAttrs({
+                'role': 'button',
+                'title': 'Do not show again',
+                'aria-disabled': 'false'
+            });
+        });
+
+        it('can skip the video on click', function () {
+            spyOn(state.bumperState.videoBumper, 'skipAndDoNotShowAgain');
+            state.el.trigger('play');
+            $('.skip-control').click();
+            expect(state.bumperState.videoCommands.execute).toHaveBeenCalledWith('skip', true);
+            expect(state.bumperState.videoBumper.skipAndDoNotShowAgain).toHaveBeenCalled();
+        });
+
+        it('can destroy itself', function () {
+            state.bumperState.videoPlaySkipControl.destroy();
+            expect(state.bumperState.videoPlaySkipControl).toBeUndefined();
+        });
+    });
+}).call(this);
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_speed_control_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_speed_control_spec.js
index 45db124f0a0..d5b14e6b2d7 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/video_speed_control_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_speed_control_spec.js
@@ -12,6 +12,7 @@
             $('source').remove();
             window.onTouchBasedDevice = oldOTBD;
             state.storage.clear();
+            state.videoPlayer.destroy();
         });
 
         describe('constructor', function () {
@@ -247,5 +248,13 @@
                 expect($('.speeds .value')).toHaveHtml('0.75x');
             });
         });
+
+        it('can destroy itself', function () {
+            state = jasmine.initializePlayer();
+            state.videoSpeedControl.destroy();
+            expect(state.videoSpeedControl).toBeUndefined();
+            expect($('.video-speeds')).not.toExist();
+            expect($('.speed-button')).not.toExist();
+        });
     });
 }).call(this);
diff --git a/common/lib/xmodule/xmodule/js/spec/video/video_volume_control_spec.js b/common/lib/xmodule/xmodule/js/spec/video/video_volume_control_spec.js
index 7ff313f956f..e1edb571d3f 100644
--- a/common/lib/xmodule/xmodule/js/spec/video/video_volume_control_spec.js
+++ b/common/lib/xmodule/xmodule/js/spec/video/video_volume_control_spec.js
@@ -13,6 +13,7 @@ describe('VideoVolumeControl', function () {
         $('source').remove();
         window.onTouchBasedDevice = oldOTBD;
         state.storage.clear();
+        state.videoPlayer.destroy();
     });
 
     it('Volume level has correct value even if cookie is broken', function () {
@@ -35,8 +36,7 @@ describe('VideoVolumeControl', function () {
         });
 
         it('render the volume control', function () {
-            expect(state.videoControl.secondaryControlsEl.html())
-                .toContain('<div class="volume">\n');
+            expect($('.volume')).toExist();
         });
 
         it('create the slider', function () {
@@ -292,7 +292,7 @@ describe('VideoVolumeControl', function () {
                 shiftKey: true
             });
         });
-    })
+    });
 
     describe('keyDownButtonHandler', function () {
         beforeEach(function () {
@@ -308,6 +308,6 @@ describe('VideoVolumeControl', function () {
             }));
             expect(volumeControl.getMuteStatus()).toEqual(isMuted);
         });
-    })
+    });
 });
 }).call(this);
diff --git a/common/lib/xmodule/xmodule/js/src/video/00_resizer.js b/common/lib/xmodule/xmodule/js/src/video/00_resizer.js
index cbf7df47eec..f0c1debcd0a 100644
--- a/common/lib/xmodule/xmodule/js/src/video/00_resizer.js
+++ b/common/lib/xmodule/xmodule/js/src/video/00_resizer.js
@@ -177,9 +177,8 @@ function () {
             }
         };
 
-        var cleanDelta = function () {
-            delta['height'] = 0;
-            delta['width'] = 0;
+        var resetDelta = function () {
+            delta['height'] = delta['width'] = 0;
 
             return module;
         };
@@ -200,12 +199,23 @@ function () {
             return module;
         };
 
+        var destroy = function () {
+            var data = getData();
+            data.element.css({
+                'height': '', 'width': '', 'top': '', 'left': ''
+            });
+            removeCallbacks();
+            resetDelta();
+            mode = null;
+        };
+
         initialize.apply(module, arguments);
 
         return $.extend(true, module, {
             align: align,
             alignByWidthOnly: alignByWidthOnly,
             alignByHeightOnly: alignByHeightOnly,
+            destroy: destroy,
             setParams: initialize,
             setMode: setMode,
             setElement: setElement,
@@ -218,7 +228,7 @@ function () {
             delta: {
                 add: addDelta,
                 substract: substractDelta,
-                reset: cleanDelta
+                reset: resetDelta
             }
         });
     };
diff --git a/common/lib/xmodule/xmodule/js/src/video/01_initialize.js b/common/lib/xmodule/xmodule/js/src/video/01_initialize.js
index 05654966f5b..ad7edc8b566 100644
--- a/common/lib/xmodule/xmodule/js/src/video/01_initialize.js
+++ b/common/lib/xmodule/xmodule/js/src/video/01_initialize.js
@@ -14,8 +14,8 @@
 
 define(
 'video/01_initialize.js',
-['video/03_video_player.js', 'video/00_video_storage.js', 'video/00_i18n.js'],
-function (VideoPlayer, VideoStorage, i18n) {
+['video/03_video_player.js', 'video/00_i18n.js'],
+function (VideoPlayer, i18n) {
     /**
      * @function
      *
@@ -71,7 +71,6 @@ function (VideoPlayer, VideoStorage, i18n) {
         isYoutubeType: isYoutubeType,
         parseSpeed: parseSpeed,
         parseYoutubeStreams: parseYoutubeStreams,
-        saveState: saveState,
         setPlayerMode: setPlayerMode,
         setSpeed: setSpeed,
         speedToString: speedToString,
@@ -145,9 +144,7 @@ function (VideoPlayer, VideoStorage, i18n) {
                     _youtubeApiDeferred.resolve();
                 }
 
-                window.YT.ready(function () {
-                    onYTApiReady();
-                });
+                window.YT.ready(onYTApiReady);
             } else {
                 // There is only one global variable window.onYouTubeIframeAPIReady which
                 // is supposed to be a function that will be called by the YouTube API
@@ -191,9 +188,7 @@ function (VideoPlayer, VideoStorage, i18n) {
                 // Attach a callback to our Deferred object to be called once the
                 // YouTube API loads.
                 window.onYouTubeIframeAPIReady.done(function () {
-                    window.YT.ready(function () {
-                        onYTApiReady();
-                    });
+                    window.YT.ready(onYTApiReady);
                 });
             }
         } else {
@@ -212,20 +207,15 @@ function (VideoPlayer, VideoStorage, i18n) {
             // callback, which will set `state.youtubeApiAvailable` to `true`.
             // If something goes wrong at this stage, `state.youtubeApiAvailable` is
             // `false`.
-            _reportToServer(state, state.youtubeApiAvailable);
+            if (!state.youtubeIsAvailable) {
+                console.log('[Video info]: YouTube API is not available.');
+            }
+            state.el.trigger('youtube_availability', [state.youtubeIsAvailable]);
         }, state.config.ytTestTimeout);
 
         $.getScript(document.location.protocol + '//' + state.config.ytApiUrl);
     }
 
-    function _reportToServer(state, youtubeIsAvailable) {
-        if (!youtubeIsAvailable) {
-            console.log('[Video info]: YouTube API is not available.');
-        }
-
-        state.saveState(true, { youtube_is_available: youtubeIsAvailable });
-    }
-
     // function _configureCaptions(state)
     //     Configure displaying of captions.
     //
@@ -296,8 +286,7 @@ function (VideoPlayer, VideoStorage, i18n) {
 
         state.videoType = 'html5';
 
-        if (!state.config.sub || !state.config.sub.length) {
-            state.config.sub = '';
+        if (!_.keys(state.config.transcriptLanguages).length) {
             state.config.showCaptions = false;
         }
         state.setSpeed(state.speed);
@@ -328,8 +317,9 @@ function (VideoPlayer, VideoStorage, i18n) {
     function _initializeModules(state, i18n) {
         var dfd = $.Deferred(),
             modulesList = $.map(state.modules, function(module) {
-                if ($.isFunction(module)) {
-                    return module(state, i18n);
+                var options = state.options[module.moduleName] || {};
+                if (_.isFunction(module)) {
+                    return module(state, i18n, options);
                 } else if ($.isPlainObject(module)) {
                     return module;
                 }
@@ -388,7 +378,6 @@ function (VideoPlayer, VideoStorage, i18n) {
                     },
                     'startTime': function (value) {
                         value = parseInt(value, 10);
-
                         if (!isFinite(value) || value < 0) {
                             return 0;
                         }
@@ -407,6 +396,13 @@ function (VideoPlayer, VideoStorage, i18n) {
                 },
                 config = {};
 
+            data = _.extend({
+                startTime: 0,
+                endTime: null,
+                sub: '',
+                streams: ''
+            }, data);
+
             $.each(data, function(option, value) {
                 // Extract option that is in `extractKeys`.
                 if ($.inArray(option, extractKeys) !== -1) {
@@ -420,7 +416,7 @@ function (VideoPlayer, VideoStorage, i18n) {
 
                 // Pre-process data.
                 if (conversions[option]) {
-                    if ($.isFunction(conversions[option])) {
+                    if (_.isFunction(conversions[option])) {
                         value = conversions[option].call(this, value);
                     } else {
                         throw new TypeError(option + ' is not a function.');
@@ -463,12 +459,11 @@ function (VideoPlayer, VideoStorage, i18n) {
 
     function initialize(element) {
         var self = this,
-            el = $(element).find('.video'),
+            el = this.el,
+            id = this.id,
             container = el.find('.video-wrapper'),
-            id = el.attr('id').replace(/video_/, ''),
             __dfd__ = $.Deferred(),
-            isTouch = onTouchBasedDevice() || '',
-            storage = VideoStorage('VideoState', id);
+            isTouch = onTouchBasedDevice() || '';
 
         if (isTouch) {
             el.addClass('is-touch');
@@ -476,23 +471,18 @@ function (VideoPlayer, VideoStorage, i18n) {
 
         $.extend(this, {
             __dfd__: __dfd__,
-            el: el,
             container: container,
-            id: id,
             isFullScreen: false,
-            isTouch: isTouch,
-            storage: storage
+            isTouch: isTouch
         });
 
-        console.log(
-            '[Video info]: Initializing video with id "' + id + '".'
-        );
+        console.log('[Video info]: Initializing video with id "%s".', id);
 
         // We store all settings passed to us by the server in one place. These
         // are "read only", so don't modify them. All variable content lives in
         // 'state' object.
         // jQuery .data() return object with keys in lower camelCase format.
-        this.config = $.extend({}, _getConfiguration(el.data(), storage), {
+        this.config = $.extend({}, _getConfiguration(this.metadata, this.storage), {
             element: element,
             fadeOutTimeout:     1400,
             captionsFreezeTime: 10000,
@@ -602,26 +592,18 @@ function (VideoPlayer, VideoStorage, i18n) {
     //         true: Parsing of YouTube video IDs went OK, and we can proceed
     //             onwards to play YouTube videos.
     function parseYoutubeStreams(youtubeStreams) {
-        var _this;
-
-        if (
-            typeof youtubeStreams === 'undefined' ||
-            youtubeStreams.length === 0
-        ) {
+        if (_.isUndefined(youtubeStreams) || !youtubeStreams.length) {
             return false;
         }
 
-        _this = this;
         this.videos = {};
 
-        $.each(youtubeStreams.split(/,/), function (index, video) {
+        _.each(youtubeStreams.split(/,/), function (video) {
             var speed;
-
             video = video.split(/:/);
-            speed = _this.speedToString(video[0]);
-
-            _this.videos[speed] = video[1];
-        });
+            speed = this.speedToString(video[0]);
+            this.videos[speed] = video[1];
+        }, this);
 
         return _.isString(this.videos['1.0']);
     }
@@ -633,23 +615,21 @@ function (VideoPlayer, VideoStorage, i18n) {
     //     example the length of the video can be determined from the meta
     //     data.
     function fetchMetadata() {
-        var _this = this,
+        var self = this,
             metadataXHRs = [];
 
         this.metadata = {};
 
-        $.each(this.videos, function (speed, url) {
-            var xhr = _this.getVideoMetadata(url, function (data) {
+        metadataXHRs = _.map(this.videos, function (url, speed) {
+            return self.getVideoMetadata(url, function (data) {
                 if (data.data) {
-                    _this.metadata[data.data.id] = data.data;
+                    self.metadata[data.data.id] = data.data;
                 }
             });
-
-            metadataXHRs.push(xhr);
         });
 
         $.when.apply(this, metadataXHRs).done(function () {
-            _this.el.trigger('metadata_received');
+            self.el.trigger('metadata_received');
 
             // Not only do we trigger the "metadata_received" event, we also
             // set a flag to notify that metadata has been received. This
@@ -657,7 +637,7 @@ function (VideoPlayer, VideoStorage, i18n) {
             // to know that metadata has been received. This is important in
             // cases when some code will subscribe to the "metadata_received"
             // event after it has been triggered.
-            _this.youtubeMetadataReceived = true;
+            self.youtubeMetadataReceived = true;
 
         });
     }
@@ -666,23 +646,21 @@ function (VideoPlayer, VideoStorage, i18n) {
     //
     //     Create a separate array of available speeds.
     function parseSpeed() {
-        this.speeds = ($.map(this.videos, function (url, speed) {
-            return speed;
-        })).sort();
+        this.speeds = _.keys(this.videos).sort();
     }
 
-    function setSpeed(newSpeed, updateStorage) {
+    function setSpeed(newSpeed) {
         // Possible speeds for each player type.
         // HTML5 =          [0.75, 1, 1.25, 1.5]
         // Youtube Flash =  [0.75, 1, 1.25, 1.5]
         // Youtube HTML5 =  [0.25, 0.5, 1, 1.5, 2]
         var map = {
-                '0.25': '0.75', // Youtube HTML5 -> HTML5 or Youtube Flash
-                '0.50': '0.75', // Youtube HTML5 -> HTML5 or Youtube Flash
-                '0.75': '0.50', // HTML5 or Youtube Flash -> Youtube HTML5
-                '1.25': '1.50', // HTML5 or Youtube Flash -> Youtube HTML5
-                '2.0': '1.50'   // Youtube HTML5 -> HTML5 or Youtube Flash
-            };
+            '0.25': '0.75', // Youtube HTML5 -> HTML5 or Youtube Flash
+            '0.50': '0.75', // Youtube HTML5 -> HTML5 or Youtube Flash
+            '0.75': '0.50', // HTML5 or Youtube Flash -> Youtube HTML5
+            '1.25': '1.50', // HTML5 or Youtube Flash -> Youtube HTML5
+            '2.0': '1.50'   // Youtube HTML5 -> HTML5 or Youtube Flash
+        };
 
         if (_.contains(this.speeds, newSpeed)) {
             this.speed = newSpeed;
@@ -690,57 +668,21 @@ function (VideoPlayer, VideoStorage, i18n) {
             newSpeed = map[newSpeed];
             this.speed = _.contains(this.speeds, newSpeed) ? newSpeed : '1.0';
         }
-
-        if (updateStorage) {
-            this.storage.setItem('speed', this.speed, true);
-            this.storage.setItem('general_speed', this.speed);
-        }
     }
 
     function getVideoMetadata(url, callback) {
-        var successHandler, xhr;
-
-        if (typeof url !== 'string') {
+        if (!(_.isString(url))) {
             url = this.videos['1.0'] || '';
         }
-        successHandler = ($.isFunction(callback)) ? callback : null;
-        xhr = $.ajax({
+
+        return $.ajax({
             url: [
                 document.location.protocol, '//', this.config.ytTestUrl, url,
                 '?v=2&alt=jsonc'
             ].join(''),
             dataType: 'jsonp',
             timeout: this.config.ytTestTimeout,
-            success: successHandler
-        });
-
-        return xhr;
-    }
-
-    function saveState(async, data) {
-
-        if (!($.isPlainObject(data))) {
-            data = {
-                saved_video_position: this.videoPlayer.currentTime
-            };
-        }
-
-        if (data.speed) {
-            this.storage.setItem('speed', data.speed, true);
-        }
-
-        if (data.hasOwnProperty('saved_video_position')) {
-            this.storage.setItem('savedVideoPosition', data.saved_video_position, true);
-
-            data.saved_video_position = Time.formatFull(data.saved_video_position);
-        }
-
-        $.ajax({
-            url: this.config.saveStateUrl,
-            type: 'POST',
-            async: async ? true : false,
-            dataType: 'json',
-            data: data,
+            success: _.isFunction(callback) ? callback : null
         });
     }
 
diff --git a/common/lib/xmodule/xmodule/js/src/video/02_html5_video.js b/common/lib/xmodule/xmodule/js/src/video/02_html5_video.js
index d055b85d62c..dc3fd7974b8 100644
--- a/common/lib/xmodule/xmodule/js/src/video/02_html5_video.js
+++ b/common/lib/xmodule/xmodule/js/src/video/02_html5_video.js
@@ -110,6 +110,54 @@ function () {
                     });
         };
 
+        Player.prototype.onError = function (event) {
+            if ($.isFunction(this.config.events.onError)) {
+                this.config.events.onError();
+            }
+        };
+
+        Player.prototype.destroy = function () {
+            this.video.removeEventListener('loadedmetadata', this.onLoadedMetadata, false);
+            this.video.removeEventListener('play', this.onPlay, false);
+            this.video.removeEventListener('playing', this.onPlaying, false);
+            this.video.removeEventListener('pause', this.onPause, false);
+            this.video.removeEventListener('ended', this.onEnded, false);
+            this.el
+                .find('.video-player div').removeClass('hidden')
+                .end()
+                .find('.video-player h3').addClass('hidden')
+                .end().removeClass('is-initialized')
+                .find('.spinner').attr({'aria-hidden': 'false'});
+            this.videoEl.remove();
+        };
+
+        Player.prototype.onLoadedMetadata = function () {
+            this.playerState = HTML5Video.PlayerState.PAUSED;
+            if ($.isFunction(this.config.events.onReady)) {
+                this.config.events.onReady(null);
+            }
+        };
+
+        Player.prototype.onPlay = function () {
+            this.playerState = HTML5Video.PlayerState.BUFFERING;
+            this.callStateChangeCallback();
+        };
+
+        Player.prototype.onPlaying = function () {
+            this.playerState = HTML5Video.PlayerState.PLAYING;
+            this.callStateChangeCallback();
+        };
+
+        Player.prototype.onPause = function () {
+            this.playerState = HTML5Video.PlayerState.PAUSED;
+            this.callStateChangeCallback();
+        };
+
+        Player.prototype.onEnded = function () {
+            this.playerState = HTML5Video.PlayerState.ENDED;
+            this.callStateChangeCallback();
+        };
+
         return Player;
 
         /*
@@ -152,6 +200,7 @@ function () {
             var isTouch = onTouchBasedDevice() || '',
                 sourceList, _this, errorMessage, lastSource;
 
+            _.bindAll(this, 'onLoadedMetadata', 'onPlay', 'onPlaying', 'onPause', 'onEnded');
             this.logs = [];
             // Initially we assume that el is a DOM element. If jQuery selector
             // fails to select something, we assume that el is an ID of a DOM
@@ -226,6 +275,8 @@ function () {
 
             lastSource = this.videoEl.find('source').last();
             lastSource.on('error', this.showErrorMessage.bind(this));
+            lastSource.on('error', this.onError.bind(this));
+            this.videoEl.on('error', this.onError.bind(this));
 
             if (/iP(hone|od)/i.test(isTouch[0])) {
                 this.videoEl.prop('controls', true);
@@ -280,35 +331,11 @@ function () {
             // When the <video> tag has been processed by the browser, and it
             // is ready for playback, notify other parts of the VideoPlayer,
             // and initially pause the video.
-            this.video.addEventListener('loadedmetadata', function () {
-                _this.playerState = HTML5Video.PlayerState.PAUSED;
-                if ($.isFunction(_this.config.events.onReady)) {
-                    _this.config.events.onReady(null);
-                }
-            }, false);
-
-            // Register the 'play' event.
-            this.video.addEventListener('play', function () {
-                _this.playerState = HTML5Video.PlayerState.BUFFERING;
-                _this.callStateChangeCallback();
-            }, false);
-
-            this.video.addEventListener('playing', function () {
-                _this.playerState = HTML5Video.PlayerState.PLAYING;
-                _this.callStateChangeCallback();
-            }, false);
-
-            // Register the 'pause' event.
-            this.video.addEventListener('pause', function () {
-                _this.playerState = HTML5Video.PlayerState.PAUSED;
-                _this.callStateChangeCallback();
-            }, false);
-
-            // Register the 'ended' event.
-            this.video.addEventListener('ended', function () {
-                _this.playerState = HTML5Video.PlayerState.ENDED;
-                _this.callStateChangeCallback();
-            }, false);
+            this.video.addEventListener('loadedmetadata', this.onLoadedMetadata, false);
+            this.video.addEventListener('play', this.onPlay, false);
+            this.video.addEventListener('playing', this.onPlaying, false);
+            this.video.addEventListener('pause', this.onPause, false);
+            this.video.addEventListener('ended', this.onEnded, false);
 
             // Place the <video> element on the page.
             this.videoEl.appendTo(this.el.find('.video-player div'));
diff --git a/common/lib/xmodule/xmodule/js/src/video/035_video_accessible_menu.js b/common/lib/xmodule/xmodule/js/src/video/035_video_accessible_menu.js
index 53e54f817cd..e1c2ce142a9 100644
--- a/common/lib/xmodule/xmodule/js/src/video/035_video_accessible_menu.js
+++ b/common/lib/xmodule/xmodule/js/src/video/035_video_accessible_menu.js
@@ -1,308 +1,241 @@
-(function (requirejs, require, define) {
-
+(function(define) {
+'use strict';
 // VideoAccessibleMenu module.
 define(
-'video/035_video_accessible_menu.js',
-[],
-function () {
-
-    // VideoAccessibleMenu() function - what this module "exports".
-    return function (state) {
-        var dfd = $.Deferred();
-
-        if (state.el.find('li.video-tracks') === 0) {
-            dfd.resolve();
-            return dfd.promise();
+'video/035_video_accessible_menu.js', [],
+function() {
+    /**
+     * Video Download Transcript control module.
+     * @exports video/035_video_accessible_menu.js
+     * @constructor
+     * @param {jquery Element} element
+     * @param {Object} options
+     */
+    var VideoAccessibleMenu = function(element, options) {
+        if (!(this instanceof VideoAccessibleMenu)) {
+            return new VideoAccessibleMenu(element, options);
         }
 
-        state.videoAccessibleMenu = {
-            value: state.storage.getItem('transcript_download_format')
-        };
-
-        _initialize(state);
-        dfd.resolve();
-        return dfd.promise();
-    };
-
-    // ***************************************************************
-    // Private functions start here.
-    // ***************************************************************
+        _.bindAll(this, 'openMenu', 'openMenuHandler', 'closeMenu', 'closeMenuHandler', 'toggleMenuHandler',
+            'clickHandler', 'keyDownHandler', 'render', 'menuItemsLinksFocused', 'changeFileType', 'setValue'
+        );
 
-    function _initialize(state) {
-        _makeFunctionsPublic(state);
-        _renderElements(state);
-        _addAriaAttributes(state);
-        _bindHandlers(state);
-    }
+        this.container = element;
+        this.options = options || {};
 
-    // function _makeFunctionsPublic(state)
-    //
-    //     Functions which will be accessible via 'state' object. When called,
-    //     these functions will get the 'state' object as a context.
-    function _makeFunctionsPublic(state) {
-        var methodsDict = {
-            changeFileType: changeFileType,
-            setValue: setValue
-        };
-
-        state.bindTo(methodsDict, state.videoAccessibleMenu, state);
-    }
-
-    // function _renderElements(state)
-    //
-    //     Create any necessary DOM elements, attach them, and set their
-    //     initial configuration. Also make the created DOM elements available
-    //     via the 'state' object. Much easier to work this way - you don't
-    //     have to do repeated jQuery element selects.
-    function _renderElements(state) {
+        if (this.container.find('.video-tracks')) {
+            this.initialize();
+        }
+    };
 
-        // For the  time being, we assume that the menu structure is present in
-        // the template HTML. In the future accessible menu plugin, everything
-        // inside <div class='menu-container'></div> will be generated in this
-        // file.
-        var container = state.el.find('li.video-tracks>div.a11y-menu-container'),
-            button = container.children('a.a11y-menu-button'),
-            menuList = container.children('ol.a11y-menu-list'),
-            menuItems = menuList.children('li.a11y-menu-item'),
-            menuItemsLinks = menuItems.children('a.a11y-menu-item-link'),
+    VideoAccessibleMenu.prototype = {
+        /** Initializes the module. */
+        initialize: function() {
+            this.value = this.options.storage.getItem('transcript_download_format');
+            this.el = this.container.find('.video-tracks .a11y-menu-container');
+            this.render();
+            this.bindHandlers();
+        },
+
+        /**
+         * Creates any necessary DOM elements, attach them, and set their,
+         * initial configuration.
+         */
+        render: function() {
+            var value, msg;
+            // For the  time being, we assume that the menu structure is present in
+            // the template HTML. In the future accessible menu plugin, everything
+            // inside <div class='menu-container'></div> will be generated in this
+            // file.
+            this.button = this.el.children('.a11y-menu-button');
+            this.menuList = this.el.children('.a11y-menu-list');
+            this.menuItems = this.menuList.children('.a11y-menu-item');
+            this.menuItemsLinks = this.menuItems.children('.a11y-menu-item-link');
             value = (function (val, activeElement) {
                 return val || activeElement.find('a').data('value') || 'srt';
-            }(state.videoAccessibleMenu.value, menuItems.filter('.active'))),
+            }(this.value, this.menuItems.filter('.active')));
             msg = '.' + value;
 
-        $.extend(state.videoAccessibleMenu, {
-            container: container,
-            button: button,
-            menuList: menuList,
-            menuItems: menuItems,
-            menuItemsLinks: menuItemsLinks
-        });
-
-        if (value) {
-            state.videoAccessibleMenu.setValue(value);
-            button.text(gettext(msg));
-        }
-    }
-
-    function _addAriaAttributes(state) {
-        var menu = state.videoAccessibleMenu;
-
-        menu.button.attr({
-            'role': 'button',
-            'aria-disabled': 'false'
-        });
-
-        menu.menuList.attr('role', 'menu');
-
-        menu.menuItemsLinks.each(function(){
-            $(this).attr({
-                'role': 'menuitem',
-                'aria-disabled': 'false'
+            if (value) {
+                this.setValue(value);
+                this.button.text(gettext(msg));
+            }
+        },
+
+        /** Bind any necessary function callbacks to DOM events. */
+        bindHandlers: function() {
+            // Attach various events handlers to menu container.
+            this.el.on({
+                'mouseenter': this.openMenuHandler,
+                'mouseleave': this.closeMenuHandler,
+                'click': this.toggleMenuHandler,
+                'keydown': this.keyDownHandler
             });
-        });
-    }
-
-    // Get previous element in array or cyles back to the last if it is the
-    // first.
-    function _previousMenuItemLink(links, index) {
-        return $(links.eq(index < 1 ? links.length - 1 : index - 1));
-    }
-
-    // Get next element in array or cyles back to the first if it is the last.
-    function _nextMenuItemLink(links, index) {
-        return $(links.eq(index >= links.length - 1 ? 0 : index + 1));
-    }
-
-    function _menuItemsLinksFocused(menu) {
-        return menu.menuItemsLinks.is(':focus');
-    }
-
-    function _openMenu(menu, without_handler) {
-        // When menu items have focus, the menu stays open on
-        // mouseleave. A _closeMenuHandler is added to the window
-        // element to have clicks close the menu when they happen
-        // outside of it. We namespace the click event to easily remove it (and
-        // only it) in _closeMenu.
-        menu.container.addClass('open');
-        menu.button.text('...');
-        if (!without_handler) {
-            $(window).on('click.currentMenu', _closeMenuHandler.bind(menu));
-        }
-
-        // @TODO: onOpen callback
-    }
-
-    function _closeMenu(menu, without_handler) {
-        // Remove the previously added clickHandler from window element.
-        var msg = '.' + menu.value;
-
-        menu.container.removeClass('open');
-        menu.button.text(gettext(msg));
-        if (!without_handler) {
-            $(window).off('click.currentMenu');
-        }
-
-        // @TODO: onClose callback
-    }
-
-    function _openMenuHandler(event) {
-        _openMenu(this, true);
 
-        return false;
-    }
-
-    function _closeMenuHandler(event) {
-        // Only close the menu if no menu item link has focus or `click` event.
-        if (!_menuItemsLinksFocused(this) || event.type == 'click') {
-            _closeMenu(this, true);
-        }
-
-        return false;
-    }
-
-    function _toggleMenuHandler(event) {
-        if (this.container.hasClass('open')) {
-            _closeMenu(this, true);
-        } else {
-            _openMenu(this, true);
-        }
-
-        return false;
-    }
-
-    // Various event handlers. They all return false to stop propagation and
-    // prevent default behavior.
-    function _clickHandler(event) {
-        var target = $(event.currentTarget);
-
-        this.changeFileType.call(this, event);
-        _closeMenu(this, true);
+            // Attach click and keydown event handlers to individual menu items.
+            this.menuItems
+                .on('click', 'a.a11y-menu-item-link', this.clickHandler)
+                .on('keydown', 'a.a11y-menu-item-link', this.keyDownHandler);
+        },
+
+        // Get previous element in array or cyles back to the last if it is the
+        // first.
+        previousMenuItemLink: function(links, index) {
+            return index < 1 ? links.last() : links.eq(index - 1);
+        },
+
+        // Get next element in array or cyles back to the first if it is the last.
+        nextMenuItemLink: function(links, index) {
+            return index >= links.length - 1 ? links.first() : links.eq(index + 1);
+        },
+
+        menuItemsLinksFocused: function() {
+            return this.menuItemsLinks.is(':focus');
+        },
+
+        openMenu: function(withoutHandler) {
+            // When menu items have focus, the menu stays open on
+            // mouseleave. A closeMenuHandler is added to the window
+            // element to have clicks close the menu when they happen
+            // outside of it. We namespace the click event to easily remove it (and
+            // only it) in closeMenu.
+            this.el.addClass('open');
+            this.button.text('...');
+            if (!withoutHandler) {
+                $(window).on('click.currentMenu', this.closeMenuHandler);
+            }
+            // @TODO: onOpen callback
+        },
 
-        return false;
-    }
+        closeMenu: function(withoutHandler) {
+            // Remove the previously added clickHandler from window element.
+            var msg = '.' + this.value;
 
-    function _keyDownHandler(event) {
-        var KEY = $.ui.keyCode,
-            keyCode = event.keyCode,
-            target = $(event.currentTarget),
-            index;
+            this.el.removeClass('open');
+            this.button.text(gettext(msg));
+            if (!withoutHandler) {
+                $(window).off('click.currentMenu');
+            }
+            // @TODO: onClose callback
+        },
 
-        if (target.is('a.a11y-menu-item-link')) {
+        openMenuHandler: function() {
+            this.openMenu(true);
+            return false;
+        },
 
-            index = target.parent().index();
+        closeMenuHandler: function(event) {
+            // Only close the menu if no menu item link has focus or `click` event.
+            if (!this.menuItemsLinksFocused() || event.type === 'click') {
+                this.closeMenu(true);
+            }
+            return false;
+        },
 
-            switch (keyCode) {
-                // Scroll up menu, wrapping at the top. Keep menu open.
-                case KEY.UP:
-                    _previousMenuItemLink(this.menuItemsLinks, index).focus();
-                    break;
-                // Scroll down  menu, wrapping at the bottom. Keep menu
-                // open.
-                case KEY.DOWN:
-                    _nextMenuItemLink(this.menuItemsLinks, index).focus();
-                    break;
-                // Close menu.
-                case KEY.TAB:
-                    _closeMenu(this);
-                    // TODO
-                    // What has to happen here? In speed menu, tabbing backward
-                    // will give focus to Play/Pause button and tabbing
-                    // forward to Volume button.
-                    break;
-                // Close menu, give focus to button and change
-                // file type.
-                case KEY.ENTER:
-                case KEY.SPACE:
-                    this.button.focus();
-                    this.changeFileType.call(this, event);
-                    _closeMenu(this);
-                    break;
-                // Close menu and give focus to speed control.
-                case KEY.ESCAPE:
-                    _closeMenu(this);
-                    this.button.focus();
-                    break;
+        toggleMenuHandler: function() {
+            if (this.el.hasClass('open')) {
+                this.closeMenu(true);
+            } else {
+                this.openMenu(true);
             }
             return false;
-        }
-        else {
-            switch(keyCode) {
-                // Open menu and focus on last element of list above it.
-                case KEY.ENTER:
-                case KEY.SPACE:
-                case KEY.UP:
-                    _openMenu(this);
-                    this.menuItemsLinks.last().focus();
-                    break;
-                // Close menu.
-                case KEY.ESCAPE:
-                    _closeMenu(this);
-                    break;
+        },
+
+        // Various event handlers. They all return false to stop propagation and
+        // prevent default behavior.
+        clickHandler: function(event) {
+            this.changeFileType.call(this, event);
+            this.closeMenu(true);
+            return false;
+        },
+
+        keyDownHandler: function(event) {
+            var KEY = $.ui.keyCode,
+                keyCode = event.keyCode,
+                target = $(event.currentTarget),
+                index;
+
+            if (target.is('a.a11y-menu-item-link')) {
+                index = target.parent().index();
+                switch (keyCode) {
+                    // Scroll up menu, wrapping at the top. Keep menu open.
+                    case KEY.UP:
+                        this.previousMenuItemLink(this.menuItemsLinks, index).focus();
+                        break;
+                    // Scroll down  menu, wrapping at the bottom. Keep menu
+                    // open.
+                    case KEY.DOWN:
+                        this.nextMenuItemLink(this.menuItemsLinks, index).focus();
+                        break;
+                    // Close menu.
+                    case KEY.TAB:
+                        this.closeMenu();
+                        // TODO
+                        // What has to happen here? In speed menu, tabbing backward
+                        // will give focus to Play/Pause button and tabbing
+                        // forward to Volume button.
+                        break;
+                    // Close menu, give focus to button and change
+                    // file type.
+                    case KEY.ENTER:
+                    case KEY.SPACE:
+                        this.button.focus();
+                        this.changeFileType.call(this, event);
+                        this.closeMenu();
+                        break;
+                    // Close menu and give focus to speed control.
+                    case KEY.ESCAPE:
+                        this.closeMenu();
+                        this.button.focus();
+                        break;
+                }
+                return false;
+            }
+            else {
+                switch(keyCode) {
+                    // Open menu and focus on last element of list above it.
+                    case KEY.ENTER:
+                    case KEY.SPACE:
+                    case KEY.UP:
+                        this.openMenu();
+                        this.menuItemsLinks.last().focus();
+                        break;
+                    // Close menu.
+                    case KEY.ESCAPE:
+                        this.closeMenu();
+                        break;
+                }
+                // We do not stop propagation and default behavior on a TAB
+                // keypress.
+                return event.keyCode === KEY.TAB;
             }
-            // We do not stop propagation and default behavior on a TAB
-            // keypress.
-            return event.keyCode === KEY.TAB;
+        },
+
+        setValue: function(value) {
+            this.value = value;
+            this.menuItems
+                .removeClass('active')
+                .find("a[data-value='" + value + "']")
+                .parent()
+                .addClass('active');
+        },
+
+        changeFileType: function(event) {
+            var fileType = $(event.currentTarget).data('value'),
+                data = {'transcript_download_format': fileType};
+
+            this.setValue(fileType);
+            this.options.storage.setItem('transcript_download_format', fileType);
+
+            $.ajax({
+                url: this.options.saveStateUrl,
+                type: 'POST',
+                dataType: 'json',
+                data: data
+            });
         }
-    }
-
-    /**
-     * @desc Bind any necessary function callbacks to DOM events (click,
-     *     mousemove, etc.).
-     *
-     * @type {function}
-     * @access private
-     *
-     * @param {object} state The object containg the state of the video player.
-     *     All other modules, their parameters, public variables, etc. are
-     *     available via this object.
-     *
-     * @this {object} The global window object.
-     *
-     * @returns {undefined}
-     */
-    function _bindHandlers(state) {
-        var menu = state.videoAccessibleMenu;
-
-        // Attach various events handlers to menu container.
-        menu.container.on({
-            'mouseenter': _openMenuHandler.bind(menu),
-            'mouseleave': _closeMenuHandler.bind(menu),
-            'click': _toggleMenuHandler.bind(menu),
-            'keydown': _keyDownHandler.bind(menu)
-        });
-
-        // Attach click and keydown event handlers to individual menu items.
-        menu.menuItems
-            .on('click', 'a.a11y-menu-item-link', _clickHandler.bind(menu))
-            .on('keydown', 'a.a11y-menu-item-link', _keyDownHandler.bind(menu));
-    }
-
-    function setValue(value) {
-        var menu = this.videoAccessibleMenu;
-
-        menu.value = value;
-        menu.menuItems
-            .removeClass('active')
-            .find("a[data-value='" + value + "']")
-            .parent()
-            .addClass('active');
-    }
-
-    // ***************************************************************
-    // Public functions start here.
-    // These are available via the 'state' object. Their context ('this'
-    // keyword) is the 'state' object. The magic private function that makes
-    // them available and sets up their context is makeFunctionsPublic().
-    // ***************************************************************
-
-    function changeFileType(event) {
-        var fileType = $(event.currentTarget).data('value');
-
-        this.videoAccessibleMenu.setValue(fileType);
-        this.saveState(true, {'transcript_download_format': fileType});
-        this.storage.setItem('transcript_download_format', fileType);
-    }
+    };
 
+    return VideoAccessibleMenu;
 });
-
-}(RequireJS.requirejs, RequireJS.require, RequireJS.define));
+}(RequireJS.define));
diff --git a/common/lib/xmodule/xmodule/js/src/video/03_video_player.js b/common/lib/xmodule/xmodule/js/src/video/03_video_player.js
index a4c268cfa7c..c2307941e91 100644
--- a/common/lib/xmodule/xmodule/js/src/video/03_video_player.js
+++ b/common/lib/xmodule/xmodule/js/src/video/03_video_player.js
@@ -15,6 +15,7 @@ function (HTML5Video, Resizer) {
             return dfd.promise();
         },
         methodsDict = {
+            destroy: destroy,
             duration: duration,
             handlePlaybackQualityChange: handlePlaybackQualityChange,
 
@@ -28,13 +29,14 @@ function (HTML5Video, Resizer) {
             isEnded: isEnded,
             isPlaying: isPlaying,
             isUnstarted: isUnstarted,
-            log: log,
             onCaptionSeek: onSeek,
             onEnded: onEnded,
+            onError: onError,
             onPause: onPause,
             onPlay: onPlay,
             runTimer: runTimer,
             stopTimer: stopTimer,
+            onLoadMetadataHtml5: onLoadMetadataHtml5,
             onPlaybackQualityChange: onPlaybackQualityChange,
             onReady: onReady,
             onSlideSeek: onSeek,
@@ -49,8 +51,7 @@ function (HTML5Video, Resizer) {
             update: update,
             figureOutStartEndTime: figureOutStartEndTime,
             figureOutStartingTime: figureOutStartingTime,
-            updatePlayTime: updatePlayTime,
-            logStopVideo:logStopVideo
+            updatePlayTime: updatePlayTime
         };
 
     VideoPlayer.prototype = methodsDict;
@@ -80,6 +81,17 @@ function (HTML5Video, Resizer) {
         state.videoPlayer.onCaptionSeek = debouncedF;
     }
 
+    // Updates players state, once metadata is loaded for html5 player.
+    function onLoadMetadataHtml5() {
+        var player = this.videoPlayer.player.videoEl,
+            videoWidth = player[0].videoWidth || player.width(),
+            videoHeight = player[0].videoHeight || player.height();
+
+        _resize(this, videoWidth, videoHeight);
+        _updateVcrAndRegion(this);
+    }
+
+
     // function _initialize(state)
     //
     //     Create any necessary DOM elements, attach them, and set their
@@ -94,8 +106,6 @@ function (HTML5Video, Resizer) {
         // metadata is loaded, which normally happens just after the video
         // starts playing. Just after that configurations can be applied.
         state.videoPlayer.ready = _.once(function () {
-            $(window).on('unload', state.saveState);
-
             if (!state.isFlashMode() && state.speed != '1.0') {
 
                 // Work around a bug in the Youtube API that causes videos to
@@ -150,20 +160,13 @@ function (HTML5Video, Resizer) {
                 videoSources: state.config.sources,
                 events: {
                     onReady:       state.videoPlayer.onReady,
-                    onStateChange: state.videoPlayer.onStateChange
+                    onStateChange: state.videoPlayer.onStateChange,
+                    onError: state.videoPlayer.onError
                 }
             });
 
             player = state.videoEl = state.videoPlayer.player.videoEl;
-
-            player[0].addEventListener('loadedmetadata', function () {
-                var videoWidth = player[0].videoWidth || player.width(),
-                    videoHeight = player[0].videoHeight || player.height();
-
-                _resize(state, videoWidth, videoHeight);
-
-                _updateVcrAndRegion(state);
-            }, false);
+            player[0].addEventListener('loadedmetadata', state.videoPlayer.onLoadMetadataHtml5, false);
 
         } else {
             youTubeId = state.youtubeId();
@@ -174,8 +177,8 @@ function (HTML5Video, Resizer) {
                 events: {
                     onReady: state.videoPlayer.onReady,
                     onStateChange: state.videoPlayer.onStateChange,
-                    onPlaybackQualityChange: state.videoPlayer
-                        .onPlaybackQualityChange
+                    onPlaybackQualityChange: state.videoPlayer.onPlaybackQualityChange,
+                    onError: state.videoPlayer.onError
                 }
             });
 
@@ -261,8 +264,8 @@ function (HTML5Video, Resizer) {
             });
         }
 
-        $(window).on('resize', _.debounce(function () {
-            state.trigger('videoControl.updateControlsHeight', null);
+        $(window).on('resize.video', _.debounce(function () {
+            state.trigger('videoFullScreen.updateControlsHeight', null);
             state.el.trigger('caption:resize');
             state.resizer.align();
         }, 100));
@@ -292,8 +295,8 @@ function (HTML5Video, Resizer) {
             events: {
                 onReady: state.videoPlayer.onReady,
                 onStateChange: state.videoPlayer.onStateChange,
-                onPlaybackQualityChange: state.videoPlayer
-                    .onPlaybackQualityChange
+                onPlaybackQualityChange: state.videoPlayer.onPlaybackQualityChange,
+                onError: state.videoPlayer.onError
             }
         });
 
@@ -309,6 +312,28 @@ function (HTML5Video, Resizer) {
     // them available and sets up their context is makeFunctionsPublic().
     // ***************************************************************
 
+    function destroy() {
+        var player = this.videoPlayer.player;
+        this.el.removeClass([
+            'is-unstarted', 'is-playing', 'is-paused', 'is-buffered',
+            'is-ended', 'is-cued'
+        ].join(' '));
+        $(window).off('.video');
+        this.el.trigger('destroy');
+        this.el.off();
+        this.videoPlayer.stopTimer();
+        if (this.resizer && this.resizer.destroy) {
+            this.resizer.destroy();
+        }
+        if (player && player.video) {
+            player.video.removeEventListener('loadedmetadata', this.videoPlayer.onLoadMetadataHtml5, false);
+        }
+        if (player && _.isFunction(player.destroy)) {
+            player.destroy();
+        }
+        delete this.videoPlayer;
+    }
+
     function pause() {
         if (this.videoPlayer.player.pauseVideo) {
             this.videoPlayer.player.pauseVideo();
@@ -349,9 +374,10 @@ function (HTML5Video, Resizer) {
                 this.trigger('videoProgressSlider.notifyThroughHandleEnd', {
                     end: true
                 });
-                // Emit `stop_video` event
-                this.videoPlayer.logStopVideo();
+
+                this.el.trigger('stop');
             }
+            this.el.trigger('timeupdate', [this.videoPlayer.currentTime]);
         }
     }
 
@@ -436,19 +462,8 @@ function (HTML5Video, Resizer) {
         }
 
         newSpeed = parseFloat(newSpeed).toFixed(2).replace(/\.00$/, '.0');
-
-        this.videoPlayer.log(
-            'speed_change_video',
-            {
-                current_time: time,
-                old_speed: this.speed,
-                new_speed: newSpeed
-            }
-        );
-
-        this.setSpeed(newSpeed, true);
+        this.setSpeed(newSpeed);
         this.videoPlayer.setPlaybackRate(newSpeed);
-        this.saveState(true, { speed: newSpeed });
     }
 
     // Every 200 ms, if the video is playing, we call the function update, via
@@ -459,20 +474,12 @@ function (HTML5Video, Resizer) {
         var time = params.time,
             type = params.type,
             oldTime = this.videoPlayer.currentTime;
-
         // After the user seeks, the video will start playing from
         // the sought point, and stop playing at the end.
         this.videoPlayer.goToStartTime = false;
 
         this.videoPlayer.seekTo(time);
-        this.videoPlayer.log(
-            'seek_video',
-            {
-                old_time: oldTime,
-                new_time: time,
-                type: type
-            }
-        );
+        this.el.trigger('seek', [time, oldTime, type]);
     }
 
     function seekTo(time) {
@@ -509,7 +516,6 @@ function (HTML5Video, Resizer) {
         }
 
         this.videoPlayer.updatePlayTime(time, true);
-        this.el.trigger('seek', arguments);
 
         // the timer is stopped above; restart it.
         if (this.videoPlayer.isPlaying()) {
@@ -534,9 +540,8 @@ function (HTML5Video, Resizer) {
 
     function onEnded() {
         var time = this.videoPlayer.duration();
-        this.videoPlayer.logStopVideo();
 
-        this.trigger('videoControl.pause', null);
+
         this.trigger('videoProgressSlider.notifyThroughHandleEnd', {
             end: true
         });
@@ -544,40 +549,20 @@ function (HTML5Video, Resizer) {
         if (this.videoPlayer.skipOnEndedStartEndReset) {
             this.videoPlayer.skipOnEndedStartEndReset = undefined;
         }
-
         // Sometimes `onEnded` events fires when `currentTime` not equal
         // `duration`. In this case, slider doesn't reach the end point of
         // timeline.
         this.videoPlayer.updatePlayTime(time);
-
         this.el.trigger('ended', arguments);
     }
 
     function onPause() {
-        this.videoPlayer.log(
-            'pause_video',
-            {
-                currentTime: this.videoPlayer.currentTime
-            }
-        );
-
         this.videoPlayer.stopTimer();
-
-        this.trigger('videoControl.pause', null);
-        this.saveState(true);
         this.el.trigger('pause', arguments);
     }
 
     function onPlay() {
-        this.videoPlayer.log(
-            'play_video',
-            {
-                currentTime: this.videoPlayer.currentTime
-            }
-        );
-
         this.videoPlayer.runTimer();
-        this.trigger('videoControl.play', null);
         this.trigger('videoProgressSlider.notifyThroughHandleEnd', {
             end: false
         });
@@ -591,22 +576,12 @@ function (HTML5Video, Resizer) {
         this.videoPlayer.player.setPlaybackQuality(value);
     }
 
-    function logStopVideo(){
-        this.videoPlayer.log(
-            'stop_video',
-            {
-                currentTime: this.videoPlayer.currentTime
-            }
-        );
-    }
-
     function onPlaybackQualityChange() {
         var quality;
 
         quality = this.videoPlayer.player.getPlaybackQuality();
 
         this.trigger('videoQualityControl.onQualityChange', quality);
-
         this.el.trigger('qualitychange', arguments);
     }
 
@@ -625,8 +600,6 @@ function (HTML5Video, Resizer) {
             _this.videoPlayer.onVolumeChange(volume);
         });
 
-        this.videoPlayer.log('load_video');
-
         availablePlaybackRates = this.videoPlayer.player
                                     .getAvailablePlaybackRates();
 
@@ -717,6 +690,10 @@ function (HTML5Video, Resizer) {
         }
 
         this.el.trigger('ready', arguments);
+
+        if (this.config.autoplay) {
+            this.videoPlayer.play();
+        }
     }
 
     function onStateChange(event) {
@@ -755,6 +732,10 @@ function (HTML5Video, Resizer) {
         }
     }
 
+    function onError (code) {
+        this.el.trigger('error', [code]);
+    }
+
     function figureOutStartEndTime(duration) {
         var videoPlayer = this.videoPlayer;
 
@@ -937,30 +918,6 @@ function (HTML5Video, Resizer) {
         return Math.floor(dur);
     }
 
-    function log(eventName, data) {
-        var logInfo;
-
-        // Default parameters that always get logged.
-        logInfo = {
-            id:   this.id
-        };
-
-        // If extra parameters were passed to the log.
-        if (data) {
-            $.each(data, function (paramName, value) {
-                logInfo[paramName] = value;
-            });
-        }
-
-        if (this.isYoutubeType()) {
-            logInfo.code = this.youtubeId();
-        } else {
-            logInfo.code = 'html5';
-        }
-
-        Logger.log(eventName, logInfo);
-    }
-
     function onVolumeChange(volume) {
         this.videoPlayer.player.setVolume(volume);
     }
diff --git a/common/lib/xmodule/xmodule/js/src/video/04_video_control.js b/common/lib/xmodule/xmodule/js/src/video/04_video_control.js
index 561c8c02d65..ce67eb18ad1 100644
--- a/common/lib/xmodule/xmodule/js/src/video/04_video_control.js
+++ b/common/lib/xmodule/xmodule/js/src/video/04_video_control.js
@@ -1,5 +1,4 @@
 (function (requirejs, require, define) {
-
 // VideoControl module.
 define(
 'video/04_video_control.js',
@@ -30,24 +29,29 @@ function () {
     //     get the 'state' object as a context.
     function _makeFunctionsPublic(state) {
         var methodsDict = {
-            exitFullScreenHandler: exitFullScreenHandler,
+            destroy: destroy,
             hideControls: hideControls,
-            hidePlayPlaceholder: hidePlayPlaceholder,
-            pause: pause,
-            play: play,
             show: show,
             showControls: showControls,
-            showPlayPlaceholder: showPlayPlaceholder,
-            toggleFullScreen: toggleFullScreen,
-            toggleFullScreenHandler: toggleFullScreenHandler,
-            togglePlayback: togglePlayback,
-            updateControlsHeight: updateControlsHeight,
+            focusFirst: focusFirst,
             updateVcrVidTime: updateVcrVidTime
         };
 
         state.bindTo(methodsDict, state.videoControl, state);
     }
 
+    function destroy() {
+        this.el.off({
+            'mousemove': this.videoControl.showControls,
+            'keydown': this.videoControl.showControls,
+            'destroy': this.videoControl.destroy,
+            'initialize': this.videoControl.focusFirst
+        });
+
+        this.el.off('controls:show');
+        delete this.videoControl;
+    }
+
     // function _renderElements(state)
     //
     //     Create any necessary DOM elements, attach them, and set their initial configuration. Also
@@ -55,21 +59,7 @@ function () {
     //     way - you don't have to do repeated jQuery element selects.
     function _renderElements(state) {
         state.videoControl.el = state.el.find('.video-controls');
-        // state.videoControl.el.append(el);
-
-        state.videoControl.sliderEl            = state.videoControl.el.find('.slider');
-        state.videoControl.playPauseEl         = state.videoControl.el.find('.video_control');
-        state.videoControl.playPlaceholder     = state.el.find('.btn-play');
-        state.videoControl.secondaryControlsEl = state.videoControl.el.find('.secondary-controls');
-        state.videoControl.fullScreenEl        = state.videoControl.el.find('.add-fullscreen');
-        state.videoControl.vidTimeEl           = state.videoControl.el.find('.vidtime');
-
-        state.videoControl.fullScreenState = false;
-        state.videoControl.pause();
-
-        if (state.isTouch && state.videoType === 'html5') {
-            state.videoControl.showPlayPlaceholder();
-        }
+        state.videoControl.vidTimeEl = state.videoControl.el.find('.vidtime');
 
         if ((state.videoType === 'html5') && (state.config.autohideHtml5)) {
             state.videoControl.fadeOutTimeout = state.config.fadeOutTimeout;
@@ -77,62 +67,23 @@ function () {
             state.videoControl.el.addClass('html5');
             state.controlHideTimeout = setTimeout(state.videoControl.hideControls, state.videoControl.fadeOutTimeout);
         }
-
-        // ARIA
-        // Let screen readers know that this anchor, representing the slider
-        // handle, behaves as a slider named 'video slider'.
-        state.videoControl.sliderEl.find('.ui-slider-handle').attr({
-            'role': 'slider',
-            'title': gettext('Video slider')
-        });
-
-        state.videoControl.updateControlsHeight();
     }
 
     // function _bindHandlers(state)
     //
     //     Bind any necessary function callbacks to DOM events (click, mousemove, etc.).
     function _bindHandlers(state) {
-        state.videoControl.playPauseEl.on('click', state.videoControl.togglePlayback);
-        state.videoControl.fullScreenEl.on('click', state.videoControl.toggleFullScreenHandler);
-        state.el.on('fullscreen', function (event, isFullScreen) {
-            var height = state.videoControl.updateControlsHeight();
-
-            if (isFullScreen) {
-                state.resizer
-                    .delta
-                    .substract(height, 'height')
-                    .setMode('both');
-
-            } else {
-                state.resizer
-                    .delta
-                    .reset()
-                    .setMode('width');
-            }
-        });
-
-        $(document).on('keyup', state.videoControl.exitFullScreenHandler);
-
         if ((state.videoType === 'html5') && (state.config.autohideHtml5)) {
-            state.el.on('mousemove', state.videoControl.showControls);
-            state.el.on('keydown', state.videoControl.showControls);
+            state.el.on({
+                'mousemove': state.videoControl.showControls,
+                'keydown': state.videoControl.showControls
+            });
         }
-        // The state.previousFocus is used in video_speed_control to track
-        // the element that had the focus before it.
-        state.videoControl.playPauseEl.on('blur', function () {
-            state.previousFocus = 'playPause';
-        });
 
-        if (/iPad|Android/i.test(state.isTouch[0])) {
-            state.videoControl.playPlaceholder
-                .on('click', function () {
-                    state.trigger('videoPlayer.play', null);
-                });
+        if (state.config.focusFirstControl) {
+            state.el.on('initialize', state.videoControl.focusFirst);
         }
-    }
-    function _getControlsHeight(control) {
-        return control.el.height() + 0.5 * control.sliderEl.height();
+        state.el.on('destroy', state.videoControl.destroy);
     }
 
     // ***************************************************************
@@ -141,10 +92,8 @@ function () {
     // The magic private function that makes them available and sets up their context is makeFunctionsPublic().
     // ***************************************************************
 
-    function updateControlsHeight () {
-        this.videoControl.height = _getControlsHeight(this.videoControl);
-
-        return this.videoControl.height;
+    function focusFirst() {
+        this.videoControl.el.find('.vcr a, .vcr button').first().focus();
     }
 
     function show() {
@@ -171,13 +120,12 @@ function () {
             }
 
             this.controlHideTimeout = setTimeout(this.videoControl.hideControls, this.videoControl.fadeOutTimeout);
-
             this.controlShowLock = false;
         }
     }
 
     function hideControls() {
-        var _this;
+        var _this = this;
 
         this.controlHideTimeout = null;
 
@@ -186,12 +134,8 @@ function () {
         }
 
         this.controlState = 'hiding';
-
-        _this = this;
-
         this.videoControl.el.fadeOut(this.videoControl.fadeOutTimeout, function () {
             _this.controlState = 'invisible';
-
             // If the focus was on the video control or the volume control,
             // then we must make sure to close these dialogs. Otherwise, after
             // next autofocus, these dialogs will be open, but the focus will
@@ -203,98 +147,6 @@ function () {
         });
     }
 
-    function showPlayPlaceholder(event) {
-        this.videoControl.playPlaceholder
-            .removeClass('is-hidden')
-            .attr({
-                'aria-hidden': 'false',
-                'tabindex': 0
-            });
-    }
-
-    function hidePlayPlaceholder(event) {
-        this.videoControl.playPlaceholder
-            .addClass('is-hidden')
-            .attr({
-                'aria-hidden': 'true',
-                'tabindex': -1
-            });
-    }
-
-    function play() {
-        this.videoControl.isPlaying = true;
-        this.videoControl.playPauseEl
-            .removeClass('play')
-            .addClass('pause')
-            .attr('title', gettext('Pause'));
-
-        if (/iPad|Android/i.test(this.isTouch[0]) && this.videoType === 'html5') {
-            this.videoControl.hidePlayPlaceholder();
-        }
-    }
-
-    function pause() {
-        this.videoControl.isPlaying = false;
-        this.videoControl.playPauseEl
-            .removeClass('pause')
-            .addClass('play')
-            .attr('title', gettext('Play'));
-
-        if (/iPad|Android/i.test(this.isTouch[0]) && this.videoType === 'html5') {
-            this.videoControl.showPlayPlaceholder();
-        }
-    }
-
-    function togglePlayback(event) {
-        event.preventDefault();
-        this.videoCommands.execute('togglePlayback');
-    }
-
-    /**
-     * Event handler to toggle fullscreen mode.
-     * @param {jquery Event} event
-     */
-    function toggleFullScreenHandler(event) {
-        event.preventDefault();
-        this.videoCommands.execute('toggleFullScreen');
-    }
-
-    /** Toggle fullscreen mode. */
-    function toggleFullScreen() {
-        var fullScreenClassNameEl = this.el.add(document.documentElement),
-            win = $(window), text;
-
-        if (this.videoControl.fullScreenState) {
-            this.videoControl.fullScreenState = this.isFullScreen = false;
-            fullScreenClassNameEl.removeClass('video-fullscreen');
-            text = gettext('Fill browser');
-            win.scrollTop(this.scrollPos);
-        } else {
-            this.scrollPos = win.scrollTop();
-            win.scrollTop(0);
-            this.videoControl.fullScreenState = this.isFullScreen = true;
-            fullScreenClassNameEl.addClass('video-fullscreen');
-            text = gettext('Exit full browser');
-        }
-
-        this.videoControl.fullScreenEl
-            .attr('title', text)
-            .text(text);
-
-        this.el.trigger('fullscreen', [this.isFullScreen]);
-    }
-
-    /**
-     * Event handler to exit from fullscreen mode.
-     * @param {jquery Event} event
-     */
-    function exitFullScreenHandler(event) {
-        if ((this.isFullScreen) && (event.keyCode === 27)) {
-            event.preventDefault();
-            this.videoCommands.execute('toggleFullScreen');
-        }
-    }
-
     function updateVcrVidTime(params) {
         var endTime = (this.config.endTime !== null) ? this.config.endTime : params.duration;
         // in case endTime is accidentally specified as being greater than the video
diff --git a/common/lib/xmodule/xmodule/js/src/video/04_video_full_screen.js b/common/lib/xmodule/xmodule/js/src/video/04_video_full_screen.js
new file mode 100644
index 00000000000..e5618520579
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/src/video/04_video_full_screen.js
@@ -0,0 +1,175 @@
+(function (define) {
+'use strict';
+define('video/04_video_full_screen.js', [], function () {
+    var template = [
+        '<a href="#" class="add-fullscreen" title="',
+            gettext('Fill browser'), '" role="button" aria-disabled="false">',
+            gettext('Fill browser'),
+        '</a>'
+    ].join('');
+
+    // VideoControl() function - what this module "exports".
+    return function (state) {
+        var dfd = $.Deferred();
+
+        state.videoFullScreen = {};
+
+        _makeFunctionsPublic(state);
+        _renderElements(state);
+        _bindHandlers(state);
+
+        dfd.resolve();
+        return dfd.promise();
+    };
+
+    // ***************************************************************
+    // Private functions start here.
+    // ***************************************************************
+
+    // function _makeFunctionsPublic(state)
+    //
+    //     Functions which will be accessible via 'state' object. When called, these functions will
+    //     get the 'state' object as a context.
+    function _makeFunctionsPublic(state) {
+        var methodsDict = {
+            destroy: destroy,
+            enter: enter,
+            exitHandler: exitHandler,
+            exit: exit,
+            onFullscreenChange: onFullscreenChange,
+            toggle: toggle,
+            toggleHandler: toggleHandler,
+            updateControlsHeight: updateControlsHeight
+        };
+
+        state.bindTo(methodsDict, state.videoFullScreen, state);
+    }
+
+    function destroy() {
+        $(document).off('keyup', this.videoFullScreen.exitHandler);
+        this.videoFullScreen.fullScreenEl.remove();
+        this.el.off({
+            'fullscreen': this.videoFullScreen.onFullscreenChange,
+            'destroy': this.videoFullScreen.destroy
+        });
+        if (this.isFullScreen) {
+            this.videoFullScreen.exit();
+        }
+        delete this.videoFullScreen;
+    }
+
+    // function _renderElements(state)
+    //
+    //     Create any necessary DOM elements, attach them, and set their initial configuration. Also
+    //     make the created DOM elements available via the 'state' object. Much easier to work this
+    //     way - you don't have to do repeated jQuery element selects.
+    function _renderElements(state) {
+        state.videoFullScreen.fullScreenEl = $(template);
+        state.videoFullScreen.sliderEl = state.el.find('.slider');
+        state.videoFullScreen.fullScreenState = false;
+        state.el.find('.secondary-controls').append(state.videoFullScreen.fullScreenEl);
+        state.videoFullScreen.updateControlsHeight();
+    }
+
+    // function _bindHandlers(state)
+    //
+    //     Bind any necessary function callbacks to DOM events (click, mousemove, etc.).
+    function _bindHandlers(state) {
+        state.videoFullScreen.fullScreenEl.on('click', state.videoFullScreen.toggleHandler);
+        state.el.on({
+            'fullscreen': state.videoFullScreen.onFullscreenChange,
+            'destroy': state.videoFullScreen.destroy
+        });
+        $(document).on('keyup', state.videoFullScreen.exitHandler);
+    }
+
+    function _getControlsHeight(controls, slider) {
+        return controls.height() + 0.5 * slider.height();
+    }
+
+    // ***************************************************************
+    // Public functions start here.
+    // These are available via the 'state' object. Their context ('this' keyword) is the 'state' object.
+    // The magic private function that makes them available and sets up their context is makeFunctionsPublic().
+    // ***************************************************************
+
+    function onFullscreenChange (event, isFullScreen) {
+        var height = this.videoFullScreen.updateControlsHeight();
+
+        if (isFullScreen) {
+            this.resizer
+                .delta
+                .substract(height, 'height')
+                .setMode('both');
+
+        } else {
+            this.resizer
+                .delta
+                .reset()
+                .setMode('width');
+        }
+    }
+
+    function updateControlsHeight() {
+        var controls = this.el.find('.video-controls'),
+            slider = this.videoFullScreen.sliderEl;
+        this.videoFullScreen.height = _getControlsHeight(controls, slider);
+        return this.videoFullScreen.height;
+    }
+
+    /**
+     * Event handler to toggle fullscreen mode.
+     * @param {jquery Event} event
+     */
+    function toggleHandler(event) {
+        event.preventDefault();
+        this.videoCommands.execute('toggleFullScreen');
+    }
+
+    function exit() {
+        var fullScreenClassNameEl = this.el.add(document.documentElement);
+
+        this.videoFullScreen.fullScreenState = this.isFullScreen = false;
+        fullScreenClassNameEl.removeClass('video-fullscreen');
+        $(window).scrollTop(this.scrollPos);
+        this.videoFullScreen.fullScreenEl
+            .attr('title', gettext('Fill browser'))
+            .text(gettext('Fill browser'));
+        this.el.trigger('fullscreen', [this.isFullScreen]);
+    }
+
+    function enter() {
+        var fullScreenClassNameEl = this.el.add(document.documentElement);
+
+        this.scrollPos = $(window).scrollTop();
+        $(window).scrollTop(0);
+        this.videoFullScreen.fullScreenState = this.isFullScreen = true;
+        fullScreenClassNameEl.addClass('video-fullscreen');
+        this.videoFullScreen.fullScreenEl
+            .attr('title', gettext('Exit full browser'))
+            .text(gettext('Exit full browser'));
+        this.el.trigger('fullscreen', [this.isFullScreen]);
+    }
+
+    /** Toggle fullscreen mode. */
+    function toggle() {
+        if (this.videoFullScreen.fullScreenState) {
+            this.videoFullScreen.exit();
+        } else {
+            this.videoFullScreen.enter();
+        }
+    }
+
+    /**
+     * Event handler to exit from fullscreen mode.
+     * @param {jquery Event} event
+     */
+    function exitHandler(event) {
+        if ((this.isFullScreen) && (event.keyCode === 27)) {
+            event.preventDefault();
+            this.videoCommands.execute('toggleFullScreen');
+        }
+    }
+});
+
+}(RequireJS.define));
diff --git a/common/lib/xmodule/xmodule/js/src/video/05_video_quality_control.js b/common/lib/xmodule/xmodule/js/src/video/05_video_quality_control.js
index 03eb4ccad01..11965fe31e7 100644
--- a/common/lib/xmodule/xmodule/js/src/video/05_video_quality_control.js
+++ b/common/lib/xmodule/xmodule/js/src/video/05_video_quality_control.js
@@ -5,6 +5,12 @@ define(
 'video/05_video_quality_control.js',
 [],
 function () {
+    var template = [
+        '<a href="#" class="quality-control is-hidden" title="',
+            gettext('HD off'), '" role="button" aria-disabled="false">',
+            gettext('HD off'),
+        '</a>'
+    ].join('');
 
     // VideoQualityControl() function - what this module "exports".
     return function (state) {
@@ -12,7 +18,6 @@ function () {
 
         // Changing quality for now only works for YouTube videos.
         if (state.videoType !== 'youtube') {
-            state.el.find('a.quality-control').remove();
             return;
         }
 
@@ -36,6 +41,7 @@ function () {
     //     get the 'state' object as a context.
     function _makeFunctionsPublic(state) {
         var methodsDict = {
+            destroy: destroy,
             fetchAvailableQualities: fetchAvailableQualities,
             onQualityChange: onQualityChange,
             showQualityControl: showQualityControl,
@@ -45,16 +51,25 @@ function () {
         state.bindTo(methodsDict, state.videoQualityControl, state);
     }
 
+    function destroy() {
+        this.videoQualityControl.el.off({
+            'click': this.videoQualityControl.toggleQuality,
+            'destroy': this.videoQualityControl.destroy
+        });
+        this.el.off('.quality');
+        this.videoQualityControl.el.remove();
+        delete this.videoQualityControl;
+    }
+
     // function _renderElements(state)
     //
     //     Create any necessary DOM elements, attach them, and set their initial configuration. Also
     //     make the created DOM elements available via the 'state' object. Much easier to work this
     //     way - you don't have to do repeated jQuery element selects.
     function _renderElements(state) {
-        state.videoQualityControl.el = state.el.find('a.quality-control');
-
-        state.videoQualityControl.el.show();
+        var element = state.videoQualityControl.el = $(template);
         state.videoQualityControl.quality = 'large';
+        state.el.find('.secondary-controls').append(element);
     }
 
     // function _bindHandlers(state)
@@ -64,9 +79,11 @@ function () {
         state.videoQualityControl.el.on('click',
             state.videoQualityControl.toggleQuality
         );
-        state.el.on('play', _.once(
+        state.el.on('play.quality', _.once(
             state.videoQualityControl.fetchAvailableQualities
         ));
+
+        state.el.on('destroy.quality', state.videoQualityControl.destroy);
     }
 
     // ***************************************************************
@@ -141,7 +158,7 @@ function () {
         event.preventDefault();
 
         newQuality = isHD ? 'large' : 'highres';
-        
+
         this.trigger('videoPlayer.handlePlaybackQualityChange', newQuality);
     }
 
diff --git a/common/lib/xmodule/xmodule/js/src/video/06_video_progress_slider.js b/common/lib/xmodule/xmodule/js/src/video/06_video_progress_slider.js
index 61da5b7ce09..d5c5439e47a 100644
--- a/common/lib/xmodule/xmodule/js/src/video/06_video_progress_slider.js
+++ b/common/lib/xmodule/xmodule/js/src/video/06_video_progress_slider.js
@@ -12,15 +12,17 @@ define(
 'video/06_video_progress_slider.js',
 [],
 function () {
+    var template = [
+        '<div class="slider" title="', gettext('Video position'), '"></div>'
+    ].join('');
+
     // VideoProgressSlider() function - what this module "exports".
     return function (state) {
         var dfd = $.Deferred();
 
         state.videoProgressSlider = {};
-
         _makeFunctionsPublic(state);
         _renderElements(state);
-        // No callbacks to DOM events (click, mousemove, etc.).
 
         dfd.resolve();
         return dfd.promise();
@@ -36,6 +38,7 @@ function () {
     //     these functions will get the 'state' object as a context.
     function _makeFunctionsPublic(state) {
         var methodsDict = {
+            destroy: destroy,
             buildSlider: buildSlider,
             getRangeParams: getRangeParams,
             onSlide: onSlide,
@@ -49,6 +52,12 @@ function () {
         state.bindTo(methodsDict, state.videoProgressSlider, state);
     }
 
+    function destroy() {
+        this.videoProgressSlider.el.removeAttr('tabindex').slider('destroy');
+        this.el.off('destroy', this.videoProgressSlider.destroy);
+        delete this.videoProgressSlider;
+    }
+
     // function _renderElements(state)
     //
     //     Create any necessary DOM elements, attach them, and set their
@@ -56,8 +65,9 @@ function () {
     //     via the 'state' object. Much easier to work this way - you don't
     //     have to do repeated jQuery element selects.
     function _renderElements(state) {
-        state.videoProgressSlider.el = state.videoControl.sliderEl;
+        state.videoProgressSlider.el = $(template);
 
+        state.el.find('.video-controls').prepend(state.videoProgressSlider.el);
         state.videoProgressSlider.buildSlider();
         _buildHandle(state);
     }
@@ -81,6 +91,8 @@ function () {
             'aria-valuemin': '0',
             'aria-valuenow': state.videoPlayer.currentTime
         });
+
+        state.el.on('destroy', state.videoProgressSlider.destroy);
     }
 
     // ***************************************************************
@@ -109,7 +121,7 @@ function () {
     // whole slider). Remember that endTime === null means the end-time
     // is set to the end of video by default.
     function updateStartEndTimeRegion(params) {
-        var left, width, start, end, duration, rangeParams;
+        var start, end, duration, rangeParams;
 
         // We must have a duration in order to determine the area of range.
         // It also must be non-zero.
diff --git a/common/lib/xmodule/xmodule/js/src/video/07_video_volume_control.js b/common/lib/xmodule/xmodule/js/src/video/07_video_volume_control.js
index 646fe8e732a..7177ee9215a 100644
--- a/common/lib/xmodule/xmodule/js/src/video/07_video_volume_control.js
+++ b/common/lib/xmodule/xmodule/js/src/video/07_video_volume_control.js
@@ -17,6 +17,10 @@ function() {
             return new VolumeControl(state, i18n);
         }
 
+        _.bindAll(this, 'keyDownHandler', 'updateVolumeSilently',
+            'onVolumeChangeHandler', 'openMenu', 'closeMenu',
+            'toggleMuteHandler', 'keyDownButtonHandler', 'destroy'
+        );
         this.state = state;
         this.state.videoVolumeControl = this;
         this.i18n = i18n;
@@ -33,17 +37,55 @@ function() {
         /** Step to increase/decrease volume level via keyboard. */
         step: 20,
 
+        template: [
+            '<div class="volume">',
+                '<a href="#" role="button" aria-disabled="false" title="',
+                    gettext('Volume'), '" aria-label="',
+                    gettext('Click on this button to mute or unmute this video or press UP or DOWN buttons to increase or decrease volume level.'),
+                    '"></a>',
+                '<div role="presentation" class="volume-slider-container">',
+                    '<div class="volume-slider"></div>',
+                '</div>',
+            '</div>'
+        ].join(''),
+
+        destroy: function () {
+            this.volumeSlider.slider('destroy');
+            this.state.el.find('iframe').removeAttr('tabindex');
+            this.a11y.destroy();
+            this.cookie = this.a11y = null;
+            this.closeMenu();
+
+            this.state.el
+                .off('play.volume')
+                .off({
+                    'keydown': this.keyDownHandler,
+                    'volumechange': this.onVolumeChangeHandler
+                });
+            this.el.off({
+                'mouseenter': this.openMenu,
+                'mouseleave': this.closeMenu
+            });
+            this.button.off({
+                'mousedown': this.toggleMuteHandler,
+                'keydown': this.keyDownButtonHandler,
+                'focus': this.openMenu,
+                'blur': this.closeMenu
+            });
+            this.el.remove();
+            delete this.state.videoVolumeControl;
+        },
+
         /** Initializes the module. */
         initialize: function() {
             var volume;
 
-            this.el = this.state.el.find('.volume');
-
             if (this.state.isTouch) {
                 // iOS doesn't support volume change
-                this.el.remove();
                 return false;
             }
+
+            this.el = $(this.template);
             // Youtube iframe react on key buttons and has his own handlers.
             // So, we disallow focusing on iframe.
             this.state.el.find('iframe').attr('tabindex', -1);
@@ -80,26 +122,28 @@ function() {
             // Therefore, we do not need redundant focusing on slider in TAB
             // order.
             container.find('a').attr('tabindex', -1);
+            this.state.el.find('.secondary-controls').append(this.el);
         },
 
         /** Bind any necessary function callbacks to DOM events. */
         bindHandlers: function() {
             this.state.el.on({
-                'keydown': this.keyDownHandler.bind(this),
-                'play': _.once(this.updateVolumeSilently.bind(this)),
-                'volumechange': this.onVolumeChangeHandler.bind(this)
+                'keydown': this.keyDownHandler,
+                'play.volume': _.once(this.updateVolumeSilently),
+                'volumechange': this.onVolumeChangeHandler
             });
             this.el.on({
-                'mouseenter': this.openMenu.bind(this),
-                'mouseleave': this.closeMenu.bind(this)
+                'mouseenter': this.openMenu,
+                'mouseleave': this.closeMenu
             });
             this.button.on({
                 'click': false,
-                'mousedown': this.toggleMuteHandler.bind(this),
-                'keydown': this.keyDownButtonHandler.bind(this),
-                'focus': this.openMenu.bind(this),
-                'blur': this.closeMenu.bind(this)
+                'mousedown': this.toggleMuteHandler,
+                'keydown': this.keyDownButtonHandler,
+                'focus': this.openMenu,
+                'blur': this.closeMenu
             });
+            this.state.el.on('destroy', this.destroy);
         },
 
         /**
@@ -343,6 +387,10 @@ function() {
     };
 
     Accessibility.prototype = {
+        destroy: function () {
+            this.liveRegion.remove();
+        },
+
         /** Initializes the module. */
         initialize: function() {
             this.liveRegion = $('<div />', {
diff --git a/common/lib/xmodule/xmodule/js/src/video/08_video_speed_control.js b/common/lib/xmodule/xmodule/js/src/video/08_video_speed_control.js
index 8f4b95d36df..c813cb48ab6 100644
--- a/common/lib/xmodule/xmodule/js/src/video/08_video_speed_control.js
+++ b/common/lib/xmodule/xmodule/js/src/video/08_video_speed_control.js
@@ -16,6 +16,10 @@ function (Iterator) {
             return new SpeedControl(state);
         }
 
+        _.bindAll(this, 'onSetSpeed', 'onRenderSpeed', 'clickLinkHandler',
+            'keyDownLinkHandler', 'mouseEnterHandler', 'mouseLeaveHandler',
+            'clickMenuHandler', 'keyDownMenuHandler', 'destroy'
+        );
         this.state = state;
         this.state.videoSpeedControl = this;
         this.initialize();
@@ -24,24 +28,51 @@ function (Iterator) {
     };
 
     SpeedControl.prototype = {
+        template: [
+            '<div class="speeds menu-container">',
+                '<a class="speed-button" href="#" title="',
+                    gettext('Speeds'), '" role="button" aria-disabled="false">',
+                    '<span class="label">', gettext('Speed'), '</span>',
+                    '<span class="value"></span>',
+                '</a>',
+              '<ol class="video-speeds menu" role="menu"></ol>',
+            '</div>'
+        ].join(''),
+
+        destroy: function () {
+            this.el.off({
+                'mouseenter': this.mouseEnterHandler,
+                'mouseleave': this.mouseLeaveHandler,
+                'click': this.clickMenuHandler,
+                'keydown': this.keyDownMenuHandler
+            });
+
+            this.state.el.off({
+                'speed:set': this.onSetSpeed,
+                'speed:render': this.onRenderSpeed
+            });
+            this.closeMenu(true);
+            this.speedsContainer.remove();
+            this.el.remove();
+            delete this.state.videoSpeedControl;
+        },
+
         /** Initializes the module. */
         initialize: function () {
             var state = this.state;
 
-            this.el = state.el.find('.speeds');
-            this.speedsContainer = this.el.find('.video-speeds');
-            this.speedButton = this.el.find('.speed-button');
-
             if (!this.isPlaybackRatesSupported(state)) {
-                this.el.remove();
                 console.log(
                     '[Video info]: playbackRate is not supported.'
                 );
 
                 return false;
             }
-
+            this.el = $(this.template);
+            this.speedsContainer = this.el.find('.video-speeds');
+            this.speedButton = this.el.find('.speed-button');
             this.render(state.speeds, state.speed);
+            this.setSpeed(state.speed, true, true);
             this.bindHandlers();
 
             return true;
@@ -51,13 +82,11 @@ function (Iterator) {
          * Creates any necessary DOM elements, attach them, and set their,
          * initial configuration.
          * @param {array} speeds List of speeds available for the player.
-         * @param {string|number} currentSpeed Current speed for the player.
          */
-        render: function (speeds, currentSpeed) {
-            var self = this,
-                speedsContainer = this.speedsContainer,
+        render: function (speeds) {
+            var speedsContainer = this.speedsContainer,
                 reversedSpeeds = speeds.concat().reverse(),
-                speedsList = $.map(reversedSpeeds, function (speed, index) {
+                speedsList = $.map(reversedSpeeds, function (speed) {
                     return [
                         '<li data-speed="', speed, '" role="presentation">',
                             '<a class="speed-link" href="#" role="menuitem" tabindex="-1">',
@@ -69,7 +98,7 @@ function (Iterator) {
 
             speedsContainer.html(speedsList.join(''));
             this.speedLinks = new Iterator(speedsContainer.find('.speed-link'));
-            this.setSpeed(currentSpeed, true, true);
+            this.state.el.find('.secondary-controls').prepend(this.el);
         },
 
         /**
@@ -77,31 +106,34 @@ function (Iterator) {
          * mousemove, etc.).
          */
         bindHandlers: function () {
-            var self = this;
-
             // Attach various events handlers to the speed menu button.
             this.el.on({
-                'mouseenter': this.mouseEnterHandler.bind(this),
-                'mouseleave': this.mouseLeaveHandler.bind(this),
-                'click': this.clickMenuHandler.bind(this),
-                'keydown': this.keyDownMenuHandler.bind(this)
+                'mouseenter': this.mouseEnterHandler,
+                'mouseleave': this.mouseLeaveHandler,
+                'click': this.clickMenuHandler,
+                'keydown': this.keyDownMenuHandler
             });
 
             // Attach click and keydown event handlers to the individual speed
             // entries.
             this.speedsContainer.on({
-                click: this.clickLinkHandler.bind(this),
-                keydown: this.keyDownLinkHandler.bind(this)
+                click: this.clickLinkHandler,
+                keydown: this.keyDownLinkHandler
             }, 'a.speed-link');
 
             this.state.el.on({
-                'speed:set': function (event, speed) {
-                    self.setSpeed(speed, true);
-                },
-                'speed:render': function (event, speeds, currentSpeed) {
-                    self.render(speeds, currentSpeed);
-                }
+                'speed:set': this.onSetSpeed,
+                'speed:render': this.onRenderSpeed
             });
+            this.state.el.on('destroy', this.destroy);
+        },
+
+        onSetSpeed: function (event, speed) {
+            this.setSpeed(speed, true);
+        },
+
+        onRenderSpeed: function (event, speeds, currentSpeed) {
+            this.render(speeds, currentSpeed);
         },
 
         /**
@@ -133,7 +165,7 @@ function (Iterator) {
             // element to have clicks close the menu when they happen
             // outside of it.
             if (bindEvent) {
-                $(window).on('click.speedMenu', this.clickMenuHandler.bind(this));
+                $(window).on('click.speedMenu', this.clickMenuHandler);
             }
 
             this.el.addClass('is-opened');
@@ -175,7 +207,7 @@ function (Iterator) {
                 this.currentSpeed = speed;
 
                 if (!silent) {
-                    this.el.trigger('speedchange', [speed]);
+                    this.el.trigger('speedchange', [speed, this.state.speed]);
                 }
             }
         },
diff --git a/common/lib/xmodule/xmodule/js/src/video/095_video_context_menu.js b/common/lib/xmodule/xmodule/js/src/video/095_video_context_menu.js
index 33fbfa752bd..951f2a639e9 100644
--- a/common/lib/xmodule/xmodule/js/src/video/095_video_context_menu.js
+++ b/common/lib/xmodule/xmodule/js/src/video/095_video_context_menu.js
@@ -656,6 +656,12 @@ function (Component) {
 
         if (!state.isYoutubeType()) {
             state.el.find('video').contextmenu(state.el, options);
+            state.el.on('destroy', function () {
+                var contextmenu = $(this).find('video').data('contextmenu');
+                if (contextmenu) {
+                    contextmenu.destroy();
+                }
+            });
         }
 
         return $.Deferred().resolve().promise();
diff --git a/common/lib/xmodule/xmodule/js/src/video/09_bumper.js b/common/lib/xmodule/xmodule/js/src/video/09_bumper.js
new file mode 100644
index 00000000000..7c3ee169291
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/src/video/09_bumper.js
@@ -0,0 +1,109 @@
+(function (define) {
+'use strict';
+define('video/09_bumper.js',[], function () {
+    /**
+     * VideoBumper module.
+     * @exports video/09_bumper.js
+     * @constructor
+     * @param {Object} player The player factory.
+     * @param {Object} state The object containing the state of the video
+     * @return {jquery Promise}
+     */
+    var VideoBumper = function (player, state) {
+        if (!(this instanceof VideoBumper)) {
+            return new VideoBumper(player, state);
+        }
+
+        _.bindAll(
+            this, 'showMainVideoHandler', 'destroy', 'skipByDuration', 'destroyAndResolve'
+        );
+        this.dfd = $.Deferred();
+        this.element = state.el;
+        this.element.addClass('is-bumper');
+        this.player = player;
+        this.state = state;
+        this.doNotShowAgain = false;
+        this.state.videoBumper = this;
+        this.bindHandlers();
+        this.initialize();
+        this.maxBumperDuration = 35; // seconds
+    };
+
+    VideoBumper.prototype = {
+        initialize: function () {
+            this.player();
+        },
+
+        getPromise: function () {
+            return this.dfd.promise();
+        },
+
+        showMainVideoHandler: function () {
+            this.state.storage.setItem('isBumperShown', true);
+            setTimeout(function () {
+                this.saveState();
+                this.showMainVideo();
+            }.bind(this), 20);
+        },
+
+        destroyAndResolve: function () {
+            this.destroy();
+            this.dfd.resolve();
+        },
+
+        showMainVideo: function () {
+            if (this.state.videoPlayer) {
+                this.destroyAndResolve();
+            } else {
+                this.state.el.on('initialize', this.destroyAndResolve);
+            }
+        },
+
+        skip: function () {
+            this.element.trigger('skip', [this.doNotShowAgain]);
+            this.showMainVideoHandler();
+        },
+
+        skipAndDoNotShowAgain: function () {
+            this.doNotShowAgain = true;
+            this.skip();
+        },
+
+        skipByDuration: function (event, time) {
+            if (time > this.maxBumperDuration) {
+                this.element.trigger('ended');
+            }
+        },
+
+        bindHandlers: function () {
+            var events = ['ended', 'error'].join(' ');
+            this.element.on(events, this.showMainVideoHandler);
+            this.element.on('timeupdate', this.skipByDuration);
+        },
+
+        saveState: function () {
+            var info = {bumper_last_view_date: true};
+            if (this.doNotShowAgain) {
+                _.extend(info, {bumper_do_not_show_again: true});
+            }
+            this.state.videoSaveStatePlugin.saveState(true, info);
+        },
+
+        destroy: function () {
+            var events = ['ended', 'error'].join(' ');
+            this.element.off(events, this.showMainVideoHandler);
+            this.element.off({
+                'timeupdate': this.skipByDuration,
+                'initialize': this.destroyAndResolve
+            });
+            this.element.removeClass('is-bumper');
+            if (_.isFunction(this.state.videoPlayer.destroy)) {
+                this.state.videoPlayer.destroy();
+            }
+            delete this.state.videoBumper;
+        }
+    };
+
+    return VideoBumper;
+});
+}(RequireJS.define));
diff --git a/common/lib/xmodule/xmodule/js/src/video/09_events_bumper_plugin.js b/common/lib/xmodule/xmodule/js/src/video/09_events_bumper_plugin.js
new file mode 100644
index 00000000000..4e18332eb23
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/src/video/09_events_bumper_plugin.js
@@ -0,0 +1,112 @@
+(function(define) {
+'use strict';
+define('video/09_events_bumper_plugin.js', [], function() {
+    /**
+     * Events module.
+     * @exports video/09_events_bumper_plugin.js
+     * @constructor
+     * @param {Object} state The object containing the state of the video
+     * @param {Object} i18n The object containing strings with translations.
+     * @param {Object} options
+     * @return {jquery Promise}
+     */
+    var EventsBumperPlugin = function(state, i18n, options) {
+        if (!(this instanceof EventsBumperPlugin)) {
+            return new EventsBumperPlugin(state, i18n, options);
+        }
+
+        _.bindAll(this, 'onReady', 'onPlay', 'onEnded', 'onShowLanguageMenu', 'onHideLanguageMenu', 'onSkip',
+            'onShowCaptions', 'onHideCaptions', 'destroy');
+        this.state = state;
+        this.options = _.extend({}, options);
+        this.state.videoEventsBumperPlugin = this;
+        this.i18n = i18n;
+        this.initialize();
+
+        return $.Deferred().resolve().promise();
+    };
+
+    EventsBumperPlugin.moduleName = 'EventsBumperPlugin';
+    EventsBumperPlugin.prototype = {
+        destroy: function () {
+            this.state.el.off(this.events);
+            delete this.state.videoEventsBumperPlugin;
+        },
+
+        initialize: function() {
+            this.events = {
+                'ready': this.onReady,
+                'play': this.onPlay,
+                'ended stop': this.onEnded,
+                'skip': this.onSkip,
+                'language_menu:show': this.onShowLanguageMenu,
+                'language_menu:hide': this.onHideLanguageMenu,
+                'captions:show': this.onShowCaptions,
+                'captions:hide': this.onHideCaptions,
+                'destroy': this.destroy
+            };
+            this.bindHandlers();
+        },
+
+        bindHandlers: function() {
+            this.state.el.on(this.events);
+        },
+
+        onReady: function () {
+            this.log('edx.video.bumper.loaded');
+        },
+
+        onPlay: function () {
+            this.log('edx.video.bumper.played', {currentTime: this.getCurrentTime()});
+        },
+
+        onEnded: function () {
+            this.log('edx.video.bumper.stopped', {currentTime: this.getCurrentTime()});
+        },
+
+        onSkip: function (event, doNotShowAgain) {
+            var info = {currentTime: this.getCurrentTime()},
+                eventName = 'edx.video.bumper.' + (doNotShowAgain ? 'dismissed': 'skipped');
+            this.log(eventName, info);
+        },
+
+        onShowLanguageMenu: function () {
+            this.log('edx.video.bumper.transcript.menu.shown');
+        },
+
+        onHideLanguageMenu: function () {
+            this.log('edx.video.bumper.transcript.menu.hidden');
+        },
+
+        onShowCaptions: function () {
+            this.log('edx.video.bumper.transcript.shown', {currentTime: this.getCurrentTime()});
+        },
+
+        onHideCaptions: function () {
+            this.log('edx.video.bumper.transcript.hidden', {currentTime: this.getCurrentTime()});
+        },
+
+        getCurrentTime: function () {
+            var player = this.state.videoPlayer;
+            return player ? player.currentTime : 0;
+        },
+
+        getDuration: function () {
+            var player = this.state.videoPlayer;
+            return player ? player.duration() : 0;
+        },
+
+        log: function (eventName, data) {
+            var logInfo = _.extend({
+                host_component_id: this.state.id,
+                bumper_id: this.state.config.sources[0] || '',
+                duration: this.getDuration(),
+                code: 'html5'
+            }, data, this.options.data);
+            Logger.log(eventName, logInfo);
+        }
+    };
+
+    return EventsBumperPlugin;
+});
+}(RequireJS.define));
diff --git a/common/lib/xmodule/xmodule/js/src/video/09_events_plugin.js b/common/lib/xmodule/xmodule/js/src/video/09_events_plugin.js
new file mode 100644
index 00000000000..bf25c7f92d9
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/src/video/09_events_plugin.js
@@ -0,0 +1,129 @@
+(function(define) {
+'use strict';
+define('video/09_events_plugin.js', [], function() {
+    /**
+     * Events module.
+     * @exports video/09_events_plugin.js
+     * @constructor
+     * @param {Object} state The object containing the state of the video
+     * @param {Object} i18n The object containing strings with translations.
+     * @param {Object} options
+     * @return {jquery Promise}
+     */
+    var EventsPlugin = function(state, i18n, options) {
+        if (!(this instanceof EventsPlugin)) {
+            return new EventsPlugin(state, i18n, options);
+        }
+
+        _.bindAll(this, 'onReady', 'onPlay', 'onPause', 'onEnded', 'onSeek',
+            'onSpeedChange', 'onShowLanguageMenu', 'onHideLanguageMenu', 'onSkip',
+            'onShowCaptions', 'onHideCaptions', 'destroy');
+        this.state = state;
+        this.options = _.extend({}, options);
+        this.state.videoEventsPlugin = this;
+        this.i18n = i18n;
+        this.initialize();
+
+        return $.Deferred().resolve().promise();
+    };
+
+    EventsPlugin.moduleName = 'EventsPlugin';
+    EventsPlugin.prototype = {
+        destroy: function () {
+            this.state.el.off(this.events);
+            delete this.state.videoEventsPlugin;
+        },
+
+        initialize: function() {
+            this.events = {
+                'ready': this.onReady,
+                'play': this.onPlay,
+                'pause': this.onPause,
+                'ended stop': this.onEnded,
+                'seek': this.onSeek,
+                'skip': this.onSkip,
+                'speedchange': this.onSpeedChange,
+                'language_menu:show': this.onShowLanguageMenu,
+                'language_menu:hide': this.onHideLanguageMenu,
+                'captions:show': this.onShowCaptions,
+                'captions:hide': this.onHideCaptions,
+                'destroy': this.destroy
+            };
+            this.bindHandlers();
+        },
+
+        bindHandlers: function() {
+            this.state.el.on(this.events);
+        },
+
+        onReady: function () {
+            this.log('load_video');
+        },
+
+        onPlay: function () {
+            this.log('play_video', {currentTime: this.getCurrentTime()});
+        },
+
+        onPause: function () {
+            this.log('pause_video', {currentTime: this.getCurrentTime()});
+        },
+
+        onEnded: function () {
+            this.log('stop_video', {currentTime: this.getCurrentTime()});
+        },
+
+        onSkip: function (event, doNotShowAgain) {
+            var info = {currentTime: this.getCurrentTime()},
+                eventName = doNotShowAgain ? 'do_not_show_again_video': 'skip_video';
+            this.log(eventName, info);
+        },
+
+        onSeek: function (event, time, oldTime, type) {
+            this.log('seek_video', {
+                old_time: oldTime,
+                new_time: time,
+                type: type
+            });
+        },
+
+        onSpeedChange: function (event, newSpeed, oldSpeed) {
+            this.log('speed_change_video', {
+                current_time: this.getCurrentTime(),
+                old_speed: oldSpeed,
+                new_speed: newSpeed
+            });
+        },
+
+        onShowLanguageMenu: function () {
+            this.log('video_show_cc_menu');
+        },
+
+        onHideLanguageMenu: function () {
+            this.log('video_hide_cc_menu');
+        },
+
+        onShowCaptions: function () {
+            this.log('show_transcript', {current_time: this.getCurrentTime()});
+        },
+
+        onHideCaptions: function () {
+            this.log('hide_transcript', {current_time: this.getCurrentTime()});
+        },
+
+        getCurrentTime: function () {
+            var player = this.state.videoPlayer;
+            return player ? player.currentTime : 0;
+        },
+
+        log: function (eventName, data) {
+            var logInfo = _.extend({
+                id: this.state.id,
+                code: this.state.isYoutubeType() ? this.state.youtubeId() : 'html5'
+            }, data, this.options.data);
+            Logger.log(eventName, logInfo);
+        }
+    };
+
+    return EventsPlugin;
+});
+}(RequireJS.define));
diff --git a/common/lib/xmodule/xmodule/js/src/video/09_play_pause_control.js b/common/lib/xmodule/xmodule/js/src/video/09_play_pause_control.js
new file mode 100644
index 00000000000..201c50c4728
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/src/video/09_play_pause_control.js
@@ -0,0 +1,87 @@
+(function(define) {
+'use strict';
+define('video/09_play_pause_control.js', [], function() {
+    /**
+     * Play/pause control module.
+     * @exports video/09_play_pause_control.js
+     * @constructor
+     * @param {Object} state The object containing the state of the video
+     * @param {Object} i18n The object containing strings with translations.
+     * @return {jquery Promise}
+     */
+    var PlayPauseControl = function(state, i18n) {
+        if (!(this instanceof PlayPauseControl)) {
+            return new PlayPauseControl(state, i18n);
+        }
+
+        _.bindAll(this, 'play', 'pause', 'onClick', 'destroy');
+        this.state = state;
+        this.state.videoPlayPauseControl = this;
+        this.i18n = i18n;
+        this.initialize();
+
+        return $.Deferred().resolve().promise();
+    };
+
+    PlayPauseControl.prototype = {
+        template: [
+            '<a class="video_control play" href="#" title="',
+                gettext('Play'), '" role="button" aria-disabled="false">',
+                gettext('Play'),
+            '</a>'
+        ].join(''),
+
+        destroy: function () {
+            this.el.remove();
+            this.state.el.off('destroy', this.destroy);
+            delete this.state.videoPlayPauseControl;
+        },
+
+        /** Initializes the module. */
+        initialize: function() {
+            this.el = $(this.template);
+            this.render();
+            this.bindHandlers();
+        },
+
+        /**
+         * Creates any necessary DOM elements, attach them, and set their,
+         * initial configuration.
+         */
+        render: function() {
+            this.state.el.find('.vcr').prepend(this.el);
+        },
+
+        /** Bind any necessary function callbacks to DOM events. */
+        bindHandlers: function() {
+            this.el.on({
+                'click': this.onClick
+            });
+            this.state.el.on({
+                'play': this.play,
+                'pause ended': this.pause,
+                'destroy': this.destroy
+            });
+        },
+
+        onClick: function (event) {
+            event.preventDefault();
+            this.state.videoCommands.execute('togglePlayback');
+        },
+
+        play: function () {
+            this.el
+                .attr('title', this.i18n['Pause']).text(this.i18n['Pause'])
+                .removeClass('play').addClass('pause');
+        },
+
+        pause: function () {
+            this.el
+                .attr('title', this.i18n['Play']).text(this.i18n['Play'])
+                .removeClass('pause').addClass('play');
+        }
+    };
+
+    return PlayPauseControl;
+});
+}(RequireJS.define));
diff --git a/common/lib/xmodule/xmodule/js/src/video/09_play_placeholder.js b/common/lib/xmodule/xmodule/js/src/video/09_play_placeholder.js
new file mode 100644
index 00000000000..bcd20dabbfe
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/src/video/09_play_placeholder.js
@@ -0,0 +1,87 @@
+(function(define) {
+'use strict';
+define('video/09_play_placeholder.js', [], function() {
+    /**
+     * Play placeholder control module.
+     * @exports video/09_play_placeholder.js
+     * @constructor
+     * @param {Object} state The object containing the state of the video
+     * @param {Object} i18n The object containing strings with translations.
+     * @return {jquery Promise}
+     */
+    var PlayPlaceholder = function(state, i18n) {
+        if (!(this instanceof PlayPlaceholder)) {
+            return new PlayPlaceholder(state, i18n);
+        }
+
+        _.bindAll(this, 'onClick', 'hide', 'show', 'destroy');
+        this.state = state;
+        this.state.videoPlayPlaceholder = this;
+        this.i18n = i18n;
+        this.initialize();
+
+        return $.Deferred().resolve().promise();
+    };
+
+    PlayPlaceholder.prototype = {
+        destroy: function () {
+            this.el.off('click', this.onClick);
+            this.state.el.on({
+                'destroy': this.destroy,
+                'play': this.hide,
+                'ended pause': this.show
+            });
+            this.hide();
+            delete this.state.videoPlayPlaceholder;
+        },
+
+        /**
+         * Indicates whether the placeholder should be shown. We display it
+         * for html5 videos on iPad and Android devices.
+         * @return {Boolean}
+         */
+        shouldBeShown: function () {
+            return /iPad|Android/i.test(this.state.isTouch[0]) && !this.state.isYoutubeType();
+        },
+
+        /** Initializes the module. */
+        initialize: function() {
+            if (!this.shouldBeShown()) {
+                return false;
+            }
+
+            this.el = this.state.el.find('.btn-play');
+            this.bindHandlers();
+            this.show();
+        },
+
+        /** Bind any necessary function callbacks to DOM events. */
+        bindHandlers: function() {
+            this.el.on('click', this.onClick);
+            this.state.el.on({
+                'destroy': this.destroy,
+                'play': this.hide,
+                'ended pause': this.show
+            });
+        },
+
+        onClick: function () {
+            this.state.videoCommands.execute('play');
+        },
+
+        hide: function () {
+            this.el
+                .addClass('is-hidden')
+                .attr({'aria-hidden': 'true', 'tabindex': -1});
+        },
+
+        show: function () {
+            this.el
+                .removeClass('is-hidden')
+                .attr({'aria-hidden': 'false', 'tabindex': 0});
+        }
+    };
+
+    return PlayPlaceholder;
+});
+}(RequireJS.define));
diff --git a/common/lib/xmodule/xmodule/js/src/video/09_play_skip_control.js b/common/lib/xmodule/xmodule/js/src/video/09_play_skip_control.js
new file mode 100644
index 00000000000..c042230c7c0
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/src/video/09_play_skip_control.js
@@ -0,0 +1,84 @@
+(function(define) {
+'use strict';
+define('video/09_play_skip_control.js', [], function() {
+    /**
+     * Play/skip control module.
+     * @exports video/09_play_skip_control.js
+     * @constructor
+     * @param {Object} state The object containing the state of the video
+     * @param {Object} i18n The object containing strings with translations.
+     * @return {jquery Promise}
+     */
+    var PlaySkipControl = function(state, i18n) {
+        if (!(this instanceof PlaySkipControl)) {
+            return new PlaySkipControl(state, i18n);
+        }
+
+        _.bindAll(this, 'play', 'onClick', 'destroy');
+        this.state = state;
+        this.state.videoPlaySkipControl = this;
+        this.i18n = i18n;
+        this.initialize();
+
+        return $.Deferred().resolve().promise();
+    };
+
+    PlaySkipControl.prototype = {
+        template: [
+            '<a class="video_control play play-skip-control" href="#" title="',
+                gettext('Play'), '" role="button" aria-disabled="false">',
+                gettext('Play'),
+            '</a>'
+        ].join(''),
+
+        destroy: function () {
+            this.el.remove();
+            this.state.el.off('destroy', this.destroy);
+            delete this.state.videoPlaySkipControl;
+        },
+
+        /** Initializes the module. */
+        initialize: function() {
+            this.el = $(this.template);
+            this.render();
+            this.bindHandlers();
+        },
+
+        /**
+         * Creates any necessary DOM elements, attach them, and set their,
+         * initial configuration.
+         */
+        render: function() {
+            this.state.el.find('.vcr').prepend(this.el);
+        },
+
+        /** Bind any necessary function callbacks to DOM events. */
+        bindHandlers: function() {
+            this.el.on('click', this.onClick);
+            this.state.el.on({
+                'play': this.play,
+                'destroy': this.destroy
+            });
+        },
+
+        onClick: function (event) {
+            event.preventDefault();
+            if (this.state.videoPlayer.isPlaying()) {
+                this.state.videoCommands.execute('skip');
+            } else {
+                this.state.videoCommands.execute('play');
+            }
+        },
+
+        play: function () {
+            this.el
+                .attr('title', gettext('Skip')).text(gettext('Skip'))
+                .removeClass('play').addClass('skip');
+            // Disable possibility to pause the video.
+            this.state.el.find('video').off('click');
+        }
+    };
+
+    return PlaySkipControl;
+});
+}(RequireJS.define));
diff --git a/common/lib/xmodule/xmodule/js/src/video/09_poster.js b/common/lib/xmodule/xmodule/js/src/video/09_poster.js
new file mode 100644
index 00000000000..b243200a300
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/src/video/09_poster.js
@@ -0,0 +1,66 @@
+(function (define) {
+'use strict';
+define('video/09_poster.js', [], function () {
+    /**
+     * Poster module.
+     * @exports video/09_poster.js
+     * @constructor
+     * @param {jquery Element} element
+     * @param {Object} options
+     */
+    var VideoPoster = function (element, options) {
+        if (!(this instanceof VideoPoster)) {
+            return new VideoPoster(element, options);
+        }
+
+        _.bindAll(this, 'onClick', 'destroy');
+        this.element = element;
+        this.container = element.find('.video-player');
+        this.options = options || {};
+        this.initialize();
+    };
+
+    VideoPoster.moduleName = 'Poster';
+    VideoPoster.prototype = {
+        template: _.template([
+            '<div class="video-pre-roll is-<%= type %> poster" ',
+                'style="background-image: url(<%= url %>)">',
+                '<button class="btn-play">', gettext('Play video'), '</button>',
+            '</div>'
+        ].join('')),
+
+        initialize: function () {
+            this.el = $(this.template({
+                url: this.options.poster.url,
+                type: this.options.poster.type
+            }));
+            this.element.addClass('is-pre-roll');
+            this.render();
+            this.bindHandlers();
+        },
+
+        bindHandlers: function () {
+            this.el.on('click', this.onClick);
+            this.element.on('destroy', this.destroy);
+        },
+
+        render: function () {
+            this.container.append(this.el);
+        },
+
+        onClick: function () {
+            if (_.isFunction(this.options.onClick)) {
+                this.options.onClick();
+            }
+            this.destroy();
+        },
+
+        destroy: function () {
+            this.element.off('destroy', this.destroy).removeClass('is-pre-roll');
+            this.el.remove();
+        }
+    };
+
+    return VideoPoster;
+});
+}(RequireJS.define));
diff --git a/common/lib/xmodule/xmodule/js/src/video/09_save_state_plugin.js b/common/lib/xmodule/xmodule/js/src/video/09_save_state_plugin.js
new file mode 100644
index 00000000000..11d2772803c
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/src/video/09_save_state_plugin.js
@@ -0,0 +1,118 @@
+(function(define) {
+'use strict';
+define('video/09_save_state_plugin.js', [], function() {
+    /**
+     * Save state module.
+     * @exports video/09_save_state_plugin.js
+     * @constructor
+     * @param {Object} state The object containing the state of the video
+     * @param {Object} i18n The object containing strings with translations.
+     * @param {Object} options
+     * @return {jquery Promise}
+     */
+    var SaveStatePlugin = function(state, i18n, options) {
+        if (!(this instanceof SaveStatePlugin)) {
+            return new SaveStatePlugin(state, i18n, options);
+        }
+
+        _.bindAll(this, 'onSpeedChange', 'saveStateHandler', 'bindUnloadHandler', 'onUnload', 'onYoutubeAvailability',
+            'onLanguageChange', 'destroy');
+        this.state = state;
+        this.options = _.extend({events: []}, options);
+        this.state.videoSaveStatePlugin = this;
+        this.i18n = i18n;
+        this.initialize();
+
+        return $.Deferred().resolve().promise();
+    };
+
+
+    SaveStatePlugin.moduleName = 'SaveStatePlugin';
+    SaveStatePlugin.prototype = {
+        destroy: function () {
+            this.state.el.off(this.events).off('destroy', this.destroy);
+            $(window).off('unload', this.onUnload);
+            delete this.state.videoSaveStatePlugin;
+        },
+
+        initialize: function() {
+            this.events = {
+                'speedchange': this.onSpeedChange,
+                'play': this.bindUnloadHandler,
+                'pause destroy': this.saveStateHandler,
+                'language_menu:change': this.onLanguageChange,
+                'youtube_availability': this.onYoutubeAvailability
+            };
+            this.bindHandlers();
+        },
+
+        bindHandlers: function() {
+            if (this.options.events.length) {
+                _.each(this.options.events, function (eventName) {
+                    var callback;
+                    if (_.has(this.events, eventName)) {
+                        callback = this.events[eventName];
+                        this.state.el.on(eventName, callback);
+                    }
+                }, this);
+            } else {
+                this.state.el.on(this.events);
+            }
+            this.state.el.on('destroy', this.destroy);
+        },
+
+        bindUnloadHandler: _.once(function () {
+            $(window).on('unload.video', this.onUnload);
+        }),
+
+        onSpeedChange: function (event, newSpeed) {
+            this.saveState(true, {speed: newSpeed});
+            this.state.storage.setItem('speed', newSpeed, true);
+            this.state.storage.setItem('general_speed', newSpeed);
+        },
+
+        saveStateHandler: function () {
+            this.saveState(true);
+        },
+
+        onUnload: function () {
+            this.saveState();
+        },
+
+        onLanguageChange: function (event, langCode) {
+            this.state.storage.setItem('language', langCode);
+        },
+
+        onYoutubeAvailability: function (event, youtubeIsAvailable) {
+            this.saveState(true, {youtube_is_available: youtubeIsAvailable});
+        },
+
+        saveState: function (async, data) {
+            if (!($.isPlainObject(data))) {
+                data = {
+                    saved_video_position: this.state.videoPlayer.currentTime
+                };
+            }
+
+            if (data.speed) {
+                this.state.storage.setItem('speed', data.speed, true);
+            }
+
+            if (_.has(data, 'saved_video_position')) {
+                this.state.storage.setItem('savedVideoPosition', data.saved_video_position, true);
+                data.saved_video_position = Time.formatFull(data.saved_video_position);
+            }
+
+            $.ajax({
+                url: this.state.config.saveStateUrl,
+                type: 'POST',
+                async: async ? true : false,
+                dataType: 'json',
+                data: data
+            });
+        }
+    };
+
+    return SaveStatePlugin;
+});
+}(RequireJS.define));
diff --git a/common/lib/xmodule/xmodule/js/src/video/09_skip_control.js b/common/lib/xmodule/xmodule/js/src/video/09_skip_control.js
new file mode 100644
index 00000000000..c508c3ebb19
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/src/video/09_skip_control.js
@@ -0,0 +1,74 @@
+(function(define) {
+'use strict';
+// VideoSkipControl module.
+define(
+'video/09_skip_control.js', [],
+function() {
+    /**
+     * Video skip control module.
+     * @exports video/09_skip_control.js
+     * @constructor
+     * @param {Object} state The object containing the state of the video
+     * @param {Object} i18n The object containing strings with translations.
+     * @return {jquery Promise}
+     */
+    var SkipControl = function(state, i18n) {
+        if (!(this instanceof SkipControl)) {
+            return new SkipControl(state, i18n);
+        }
+
+        _.bindAll(this, 'onClick', 'render', 'destroy');
+        this.state = state;
+        this.state.videoSkipControl = this;
+        this.i18n = i18n;
+        this.initialize();
+
+        return $.Deferred().resolve().promise();
+    };
+
+    SkipControl.prototype = {
+        template: [
+            '<a class="video_control skip skip-control" href="#" title="',
+                gettext('Do not show again'), '" role="button" aria-disabled="false">',
+                gettext('Do not show again'),
+            '</a>'
+        ].join(''),
+
+        destroy: function () {
+            this.el.remove();
+            this.state.el.off('.skip');
+            delete this.state.videoSkipControl;
+        },
+
+        /** Initializes the module. */
+        initialize: function() {
+            this.el = $(this.template);
+            this.bindHandlers();
+        },
+
+        /**
+         * Creates any necessary DOM elements, attach them, and set their,
+         * initial configuration.
+         */
+        render: function() {
+            this.state.el.find('.vcr a').after(this.el);
+        },
+
+        /** Bind any necessary function callbacks to DOM events. */
+        bindHandlers: function() {
+            this.el.on('click', this.onClick);
+            this.state.el.on({
+                'play.skip': _.once(this.render),
+                'destroy.skip': this.destroy
+            });
+        },
+
+        onClick: function (event) {
+            event.preventDefault();
+            this.state.videoCommands.execute('skip', true);
+        }
+    };
+
+    return SkipControl;
+});
+}(RequireJS.define));
diff --git a/common/lib/xmodule/xmodule/js/src/video/09_video_caption.js b/common/lib/xmodule/xmodule/js/src/video/09_video_caption.js
index 27608e99a54..56d22655a66 100644
--- a/common/lib/xmodule/xmodule/js/src/video/09_video_caption.js
+++ b/common/lib/xmodule/xmodule/js/src/video/09_video_caption.js
@@ -1,5 +1,4 @@
 (function (define) {
-
 // VideoCaption module.
 define(
 'video/09_video_caption.js',
@@ -24,6 +23,10 @@ function (Sjson, AsyncProcess) {
             return new VideoCaption(state);
         }
 
+        _.bindAll(this, 'toggle', 'onMouseEnter', 'onMouseLeave', 'onMovement',
+            'onContainerMouseEnter', 'onContainerMouseLeave', 'fetchCaption',
+            'onResize', 'pause', 'play', 'onCaptionUpdate', 'onCaptionHandler', 'destroy'
+        );
         this.state = state;
         this.state.videoCaption = this;
         this.renderElements();
@@ -32,29 +35,61 @@ function (Sjson, AsyncProcess) {
     };
 
     VideoCaption.prototype = {
+        langTemplate: [
+            '<div class="lang menu-container">',
+                '<a href="#" class="hide-subtitles" title="',
+                    gettext('Turn off captions'), '" role="button" aria-disabled="false">',
+                    gettext('Turn off captions'),
+                '</a>',
+            '</div>'
+        ].join(''),
+
+        template: [
+            '<ol id="transcript-captions" class="subtitles" tabindex="0" role="group" aria-label="',
+                gettext('Activating an item in this group will spool the video to the corresponding time point. To skip transcript, go to previous item.'),
+                '">',
+                '<li></li>',
+            '</ol>'
+        ].join(''),
+
+        destroy: function () {
+            this.state.el
+                .off({
+                    'caption:fetch': this.fetchCaption,
+                    'caption:resize': this.onResize,
+                    'caption:update': this.onCaptionUpdate,
+                    'ended': this.pause,
+                    'fullscreen': this.onResize,
+                    'pause': this.pause,
+                    'play': this.play,
+                    'destroy': this.destroy
+                })
+                .removeClass('is-captions-rendered');
+            if (this.fetchXHR && this.fetchXHR.abort) {
+                this.fetchXHR.abort();
+            }
+            if (this.availableTranslationsXHR && this.availableTranslationsXHR.abort) {
+                this.availableTranslationsXHR.abort();
+            }
+            this.subtitlesEl.remove();
+            this.container.remove();
+            delete this.state.videoCaption;
+        },
         /**
         * @desc Initiate rendering of elements, and set their initial configuration.
         *
         */
         renderElements: function () {
-            var state = this.state,
-                languages = this.state.config.transcriptLanguages;
+            var languages = this.state.config.transcriptLanguages;
 
             this.loaded = false;
-            this.subtitlesEl = state.el.find('ol.subtitles');
-            this.container = state.el.find('.lang');
-            this.hideSubtitlesEl = state.el.find('a.hide-subtitles');
+            this.subtitlesEl = $(this.template);
+            this.container = $(this.langTemplate);
+            this.hideSubtitlesEl = this.container.find('a.hide-subtitles');
 
             if (_.keys(languages).length) {
                 this.renderLanguageMenu(languages);
-
-                if (!this.fetchCaption()) {
-                    this.hideCaptions(true);
-                    this.hideSubtitlesEl.hide();
-                }
-            } else {
-                this.hideCaptions(true, false);
-                this.hideSubtitlesEl.hide();
+                this.fetchCaption();
             }
         },
 
@@ -64,65 +99,40 @@ function (Sjson, AsyncProcess) {
         *
         */
         bindHandlers: function () {
-            var self = this,
-                state = this.state,
+            var state = this.state,
                 events = [
                     'mouseover', 'mouseout', 'mousedown', 'click', 'focus', 'blur',
                     'keydown'
                 ].join(' ');
 
-            // Change context to VideoCaption of event handlers using `bind`.
-            this.hideSubtitlesEl.on('click', this.toggle.bind(this));
+            this.hideSubtitlesEl.on('click', this.toggle);
             this.subtitlesEl
                 .on({
-                    mouseenter: this.onMouseEnter.bind(this),
-                    mouseleave: this.onMouseLeave.bind(this),
-                    mousemove: this.onMovement.bind(this),
-                    mousewheel: this.onMovement.bind(this),
-                    DOMMouseScroll: this.onMovement.bind(this)
+                    mouseenter: this.onMouseEnter,
+                    mouseleave: this.onMouseLeave,
+                    mousemove: this.onMovement,
+                    mousewheel: this.onMovement,
+                    DOMMouseScroll: this.onMovement
                 })
-                .on(events, 'li[data-index]', function (event) {
-                    switch (event.type) {
-                        case 'mouseover':
-                        case 'mouseout':
-                            self.captionMouseOverOut(event);
-                            break;
-                        case 'mousedown':
-                            self.captionMouseDown(event);
-                            break;
-                        case 'click':
-                            self.captionClick(event);
-                            break;
-                        case 'focusin':
-                            self.captionFocus(event);
-                            break;
-                        case 'focusout':
-                            self.captionBlur(event);
-                            break;
-                        case 'keydown':
-                            self.captionKeyDown(event);
-                            break;
-                    }
-                });
+                .on(events, 'li[data-index]', this.onCaptionHandler);
 
             if (this.showLanguageMenu) {
                 this.container.on({
-                    mouseenter: this.onContainerMouseEnter.bind(this),
-                    mouseleave: this.onContainerMouseLeave.bind(this)
+                    mouseenter: this.onContainerMouseEnter,
+                    mouseleave: this.onContainerMouseLeave
                 });
             }
 
             state.el
                 .on({
-                    'caption:fetch': this.fetchCaption.bind(this),
-                    'caption:resize': this.onResize.bind(this),
-                    'caption:update': function (event, time) {
-                        self.updatePlayTime(time);
-                    },
-                    'ended': this.pause.bind(this),
-                    'fullscreen': this.onResize.bind(this),
-                    'pause': this.pause.bind(this),
-                    'play': this.play.bind(this)
+                    'caption:fetch': this.fetchCaption,
+                    'caption:resize': this.onResize,
+                    'caption:update': this.onCaptionUpdate,
+                    'ended': this.pause,
+                    'fullscreen': this.onResize,
+                    'pause': this.pause,
+                    'play': this.play,
+                    'destroy': this.destroy
                 });
 
             if ((state.videoType === 'html5') && (state.config.autohideHtml5)) {
@@ -130,6 +140,33 @@ function (Sjson, AsyncProcess) {
             }
         },
 
+        onCaptionUpdate: function (event, time) {
+            this.updatePlayTime(time);
+        },
+
+        onCaptionHandler: function (event) {
+            switch (event.type) {
+                case 'mouseover':
+                case 'mouseout':
+                    this.captionMouseOverOut(event);
+                    break;
+                case 'mousedown':
+                    this.captionMouseDown(event);
+                    break;
+                case 'click':
+                    this.captionClick(event);
+                    break;
+                case 'focusin':
+                    this.captionFocus(event);
+                    break;
+                case 'focusout':
+                    this.captionBlur(event);
+                    break;
+                case 'keydown':
+                    this.captionKeyDown(event);
+                    break;
+            }
+        },
 
         /**
         * @desc Opens language menu.
@@ -138,8 +175,8 @@ function (Sjson, AsyncProcess) {
         */
         onContainerMouseEnter: function (event) {
             event.preventDefault();
-            this.state.videoPlayer.log('video_show_cc_menu', {});
             $(event.currentTarget).addClass('is-opened');
+            this.state.el.trigger('language_menu:show');
         },
 
         /**
@@ -149,8 +186,8 @@ function (Sjson, AsyncProcess) {
         */
         onContainerMouseLeave: function (event) {
             event.preventDefault();
-            this.state.videoPlayer.log('video_hide_cc_menu', {});
             $(event.currentTarget).removeClass('is-opened');
+            this.state.el.trigger('language_menu:hide');
         },
 
         /**
@@ -247,12 +284,11 @@ function (Sjson, AsyncProcess) {
             var self = this,
                 state = this.state,
                 language = state.getCurrentLanguage(),
+                url = state.config.transcriptTranslationUrl.replace('__lang__', language),
                 data, youtubeId;
 
             if (this.loaded) {
                 this.hideCaptions(false);
-            } else {
-                this.hideCaptions(state.hide_captions, false);
             }
 
             if (this.fetchXHR && this.fetchXHR.abort) {
@@ -266,16 +302,14 @@ function (Sjson, AsyncProcess) {
                     return false;
                 }
 
-                data = {
-                    videoId: youtubeId
-                };
+                data = {videoId: youtubeId};
             }
 
             state.el.removeClass('is-captions-rendered');
             // Fetch the captions file. If no file was specified, or if an error
             // occurred, then we hide the captions panel, and the "CC" button
             this.fetchXHR = $.ajaxWithPrefix({
-                url: state.config.transcriptTranslationUrl + '/' + language,
+                url: url,
                 notifyOnError: false,
                 data: data,
                 success: function (sjson) {
@@ -300,7 +334,9 @@ function (Sjson, AsyncProcess) {
                         } else {
                             self.renderCaption(start, captions);
                         }
-
+                        self.hideCaptions(state.hide_captions, false);
+                        self.state.el.find('.video-wrapper').after(self.subtitlesEl);
+                        self.state.el.find('.secondary-controls').append(self.container);
                         self.bindHandlers();
                     }
 
@@ -336,7 +372,7 @@ function (Sjson, AsyncProcess) {
             var self = this,
                 state = this.state;
 
-            return $.ajaxWithPrefix({
+            this.availableTranslationsXHR = $.ajaxWithPrefix({
                 url: state.config.transcriptAvailableTranslationsUrl,
                 notifyOnError: false,
                 success: function (response) {
@@ -359,6 +395,8 @@ function (Sjson, AsyncProcess) {
                     self.hideSubtitlesEl.hide();
                 }
             });
+
+            return this.availableTranslationsXHR;
         },
 
         /**
@@ -417,11 +455,11 @@ function (Sjson, AsyncProcess) {
 
                 if (state.lang !== langCode) {
                     state.lang = langCode;
-                    state.storage.setItem('language', langCode);
                     el  .addClass('is-active')
                         .siblings('li')
                         .removeClass('is-active');
 
+                    state.el.trigger('language_menu:change', [langCode]);
                     self.fetchCaption();
                 }
             });
@@ -658,7 +696,7 @@ function (Sjson, AsyncProcess) {
         *
         */
         play: function () {
-            var startAndCaptions, start, end;
+            var captions, startAndCaptions, start;
             if (this.loaded) {
                 if (!this.rendered) {
                     startAndCaptions = this.getBoundedCaptions();
@@ -689,10 +727,7 @@ function (Sjson, AsyncProcess) {
         */
         updatePlayTime: function (time) {
             var state = this.state,
-                startTime,
-                endTime,
-                params,
-                newIndex;
+                params, newIndex;
 
             if (this.loaded) {
                 if (state.isFlashMode()) {
@@ -797,9 +832,9 @@ function (Sjson, AsyncProcess) {
             event.preventDefault();
 
             if (this.state.el.hasClass('closed')) {
-                this.hideCaptions(false);
+                this.hideCaptions(false, true, true);
             } else {
-                this.hideCaptions(true);
+                this.hideCaptions(true, true, true);
             }
         },
 
@@ -811,38 +846,35 @@ function (Sjson, AsyncProcess) {
         * @param {boolean} update_cookie Flag to update or not the cookie.
         *
         */
-        hideCaptions: function (hide_captions, update_cookie) {
+        hideCaptions: function (hide_captions, update_cookie, trigger_event) {
             var hideSubtitlesEl = this.hideSubtitlesEl,
-                state = this.state,
-                type, text;
+                state = this.state, text;
 
             if (typeof update_cookie === 'undefined') {
                 update_cookie = true;
             }
 
             if (hide_captions) {
-                type = 'hide_transcript';
                 state.captionsHidden = true;
                 state.el.addClass('closed');
                 text = gettext('Turn on captions');
+                if (trigger_event) {
+                    this.state.el.trigger('captions:hide');
+                }
             } else {
-                type = 'show_transcript';
                 state.captionsHidden = false;
                 state.el.removeClass('closed');
                 this.scrollCaption();
                 text = gettext('Turn off captions');
+                if (trigger_event) {
+                    this.state.el.trigger('captions:show');
+                }
             }
 
             hideSubtitlesEl
                 .attr('title', text)
                 .text(gettext(text));
 
-            if (state.videoPlayer) {
-                state.videoPlayer.log(type, {
-                    currentTime: state.videoPlayer.currentTime
-                });
-            }
-
             if (state.resizer) {
                 if (state.isFullScreen) {
                     state.resizer.setMode('both');
@@ -868,9 +900,8 @@ function (Sjson, AsyncProcess) {
         */
         captionHeight: function () {
             var state = this.state;
-
             if (state.isFullScreen) {
-                return state.container.height() - state.videoControl.height;
+                return state.container.height() - state.videoFullScreen.height;
             } else {
                 return state.container.height();
             }
@@ -889,8 +920,8 @@ function (Sjson, AsyncProcess) {
             ) {
                 // In case of html5 autoshowing subtitles, we adjust height of
                 // subs, by height of scrollbar.
-                height = state.videoControl.el.height() +
-                    0.5 * state.videoControl.sliderEl.height();
+                height = state.el.find('.video-controls').height() +
+                    0.5 * state.el.find('.slider').height();
                 // Height of videoControl does not contain height of slider.
                 // css is set to absolute, to avoid yanking when slider
                 // autochanges its height.
diff --git a/common/lib/xmodule/xmodule/js/src/video/10_commands.js b/common/lib/xmodule/xmodule/js/src/video/10_commands.js
index ad78066864d..5ed94c92964 100644
--- a/common/lib/xmodule/xmodule/js/src/video/10_commands.js
+++ b/common/lib/xmodule/xmodule/js/src/video/10_commands.js
@@ -1,11 +1,8 @@
 (function(define) {
 'use strict';
-// VideoCommands module.
 define('video/10_commands.js', [], function() {
     var VideoCommands, Command, playCommand, pauseCommand, togglePlaybackCommand,
-        muteCommand, unmuteCommand, toggleMuteCommand, toggleFullScreenCommand,
-        setSpeedCommand;
-
+        toggleMuteCommand, toggleFullScreenCommand, setSpeedCommand, skipCommand;
     /**
      * Video commands module.
      * @exports video/10_commands.js
@@ -19,6 +16,7 @@ define('video/10_commands.js', [], function() {
             return new VideoCommands(state, i18n);
         }
 
+        _.bindAll(this, 'destroy');
         this.state = state;
         this.state.videoCommands = this;
         this.i18n = i18n;
@@ -29,9 +27,15 @@ define('video/10_commands.js', [], function() {
     };
 
     VideoCommands.prototype = {
+        destroy: function () {
+            this.state.el.off('destroy', this.destroy);
+            delete this.state.videoCommands;
+        },
+
         /** Initializes the module. */
         initialize: function() {
             this.commands = this.getCommands();
+            this.state.el.on('destroy', this.destroy);
         },
 
         execute: function (command) {
@@ -48,7 +52,8 @@ define('video/10_commands.js', [], function() {
             var commands = {},
                 commandsList = [
                     playCommand, pauseCommand, togglePlaybackCommand,
-                    toggleMuteCommand, toggleFullScreenCommand, setSpeedCommand
+                    toggleMuteCommand, toggleFullScreenCommand, setSpeedCommand,
+                    skipCommand
                 ];
 
             _.each(commandsList, function(command) {
@@ -73,7 +78,7 @@ define('video/10_commands.js', [], function() {
     });
 
     togglePlaybackCommand = new Command('togglePlayback', function (state) {
-        if (state.videoControl.isPlaying) {
+        if (state.videoPlayer.isPlaying()) {
             pauseCommand.execute(state);
         } else {
             playCommand.execute(state);
@@ -85,13 +90,21 @@ define('video/10_commands.js', [], function() {
     });
 
     toggleFullScreenCommand = new Command('toggleFullScreen', function (state) {
-        state.videoControl.toggleFullScreen();
+        state.videoFullScreen.toggle();
     });
 
     setSpeedCommand = new Command('speed', function (state, speed) {
         state.videoSpeedControl.setSpeed(state.speedToString(speed));
     });
 
+    skipCommand = new Command('skip', function (state, doNotShowAgain) {
+        if (doNotShowAgain) {
+            state.videoBumper.skipAndDoNotShowAgain();
+        } else {
+            state.videoBumper.skip();
+        }
+    });
+
     return VideoCommands;
 });
 }(RequireJS.define));
diff --git a/common/lib/xmodule/xmodule/js/src/video/10_main.js b/common/lib/xmodule/xmodule/js/src/video/10_main.js
index 1ac53911172..be3bbc8ae87 100644
--- a/common/lib/xmodule/xmodule/js/src/video/10_main.js
+++ b/common/lib/xmodule/xmodule/js/src/video/10_main.js
@@ -1,6 +1,5 @@
 (function (require, $) {
     'use strict';
-
     // In the case when the Video constructor will be called before RequireJS finishes loading all of the Video
     // dependencies, we will have a mock function that will collect all the elements that must be initialized as
     // Video elements.
@@ -35,74 +34,122 @@
     // Main module.
     require(
         [
+            'video/00_video_storage.js',
             'video/01_initialize.js',
             'video/025_focus_grabber.js',
             'video/035_video_accessible_menu.js',
             'video/04_video_control.js',
+            'video/04_video_full_screen.js',
             'video/05_video_quality_control.js',
             'video/06_video_progress_slider.js',
             'video/07_video_volume_control.js',
             'video/08_video_speed_control.js',
             'video/09_video_caption.js',
+            'video/09_play_placeholder.js',
+            'video/09_play_pause_control.js',
+            'video/09_play_skip_control.js',
+            'video/09_skip_control.js',
+            'video/09_bumper.js',
+            'video/09_save_state_plugin.js',
+            'video/09_events_plugin.js',
+            'video/09_events_bumper_plugin.js',
+            'video/09_poster.js',
             'video/10_commands.js',
             'video/095_video_context_menu.js'
         ],
         function (
-            initialize,
-            FocusGrabber,
-            VideoAccessibleMenu,
-            VideoControl,
-            VideoQualityControl,
-            VideoProgressSlider,
-            VideoVolumeControl,
-            VideoSpeedControl,
-            VideoCaption,
-            VideoCommands,
+            VideoStorage, initialize, FocusGrabber, VideoAccessibleMenu, VideoControl, VideoFullScreen,
+            VideoQualityControl, VideoProgressSlider, VideoVolumeControl, VideoSpeedControl, VideoCaption,
+            VideoPlayPlaceholder, VideoPlayPauseControl, VideoPlaySkipControl, VideoSkipControl, VideoBumper,
+            VideoSaveStatePlugin, VideoEventsPlugin, VideoEventsBumperPlugin, VideoPoster, VideoCommands,
             VideoContextMenu
         ) {
             var youtubeXhr = null,
                 oldVideo = window.Video;
 
             window.Video = function (element) {
-                var previousState = window.Video.previousState,
-                    state;
-
-                // Check for existance of previous state, uninitialize it if necessary, and create a new state. Store
-                // new state for future invocation of this module consturctor function.
-                if (previousState && previousState.videoPlayer) {
-                    previousState.saveState(true);
-                    $(window).off('unload', previousState.saveState);
+                var el = $(element).find('.video'),
+                    id = el.attr('id').replace(/video_/, ''),
+                    storage = VideoStorage('VideoState', id),
+                    bumperMetadata = el.data('bumper-metadata'),
+                    mainVideoModules = [FocusGrabber, VideoControl, VideoPlayPlaceholder,
+                        VideoPlayPauseControl, VideoProgressSlider, VideoSpeedControl, VideoVolumeControl,
+                        VideoQualityControl, VideoFullScreen, VideoCaption, VideoCommands, VideoContextMenu,
+                        VideoSaveStatePlugin, VideoEventsPlugin],
+                    bumperVideoModules = [VideoControl, VideoPlaySkipControl, VideoSkipControl,
+                        VideoVolumeControl, VideoCaption, VideoCommands, VideoSaveStatePlugin, VideoEventsBumperPlugin],
+                    state = {
+                        el: el,
+                        id: id,
+                        metadata: el.data('metadata'),
+                        storage: storage,
+                        options: {},
+                        youtubeXhr: youtubeXhr,
+                        modules: mainVideoModules
+                    };
+
+                var getBumperState = function (metadata) {
+                    var bumperState = $.extend(true, {
+                            el: el,
+                            id: id,
+                            storage: storage,
+                            options: {},
+                            youtubeXhr: youtubeXhr
+                        }, {metadata: metadata});
+
+                    bumperState.modules = bumperVideoModules;
+                    bumperState.options = {
+                        SaveStatePlugin: {events: ['language_menu:change']}
+                    };
+                    return bumperState;
+                };
+
+                var player = function (state) {
+                    return function () {
+                        _.extend(state.metadata, {autoplay: true, focusFirstControl: true});
+                        initialize(state, element);
+                    };
+                };
+
+                new VideoAccessibleMenu(el, {
+                    storage: storage,
+                    saveStateUrl: state.metadata.saveStateUrl
+                });
+
+                if (bumperMetadata) {
+                    new VideoPoster(el, {
+                        poster: el.data('poster'),
+                        onClick: _.once(function () {
+                            var mainVideoPlayer = player(state), bumper, bumperState;
+                            if (storage.getItem('isBumperShown')) {
+                                mainVideoPlayer();
+                            } else {
+                                bumperState = getBumperState(bumperMetadata);
+                                bumper = new VideoBumper(player(bumperState), bumperState);
+                                state.bumperState = bumperState;
+                                bumper.getPromise().done(function () {
+                                    delete state.bumperState;
+                                    mainVideoPlayer();
+                                });
+                            }
+                        })
+                    });
+                } else {
+                    initialize(state, element);
                 }
 
-                state = {};
-                // Because this constructor can be called multiple times on a single page (when the user switches
-                // verticals, the page doesn't reload, but the content changes), we must will check each time if there
-                // is a previous copy of 'state' object. If there is, we will make sure that copy exists cleanly. We
-                // have to do this because when verticals switch, the code does not handle any Xmodule JS code that is
-                // running - it simply removes DOM elements from the page. Any functions that were running during this,
-                // and that will run afterwards (expecting the DOM elements to be present) must be stopped by hand.
-                window.Video.previousState = state;
-
-                state.modules = [
-                    FocusGrabber,
-                    VideoAccessibleMenu,
-                    VideoControl,
-                    VideoQualityControl,
-                    VideoProgressSlider,
-                    VideoVolumeControl,
-                    VideoSpeedControl,
-                    VideoCaption,
-                    VideoCommands,
-                    VideoContextMenu
-                ];
-
-                state.youtubeXhr = youtubeXhr;
-                initialize(state, element);
                 if (!youtubeXhr) {
                     youtubeXhr = state.youtubeXhr;
                 }
 
-                $(element).find('.video').data('video-player-state', state);
+                el.data('video-player-state', state);
+                var onSequenceChange = function onSequenceChange () {
+                    if (state && state.videoPlayer) {
+                        state.videoPlayer.destroy();
+                    }
+                    $('.sequence').off('sequence:change', onSequenceChange);
+                };
+                $('.sequence').on('sequence:change', onSequenceChange);
 
                 // Because the 'state' object is only available inside this closure, we will also make it available to
                 // the caller by returning it. This is necessary so that we can test Video with Jasmine.
diff --git a/common/lib/xmodule/xmodule/modulestore/inheritance.py b/common/lib/xmodule/xmodule/modulestore/inheritance.py
index d6293647c71..d30a216df54 100644
--- a/common/lib/xmodule/xmodule/modulestore/inheritance.py
+++ b/common/lib/xmodule/xmodule/modulestore/inheritance.py
@@ -149,6 +149,18 @@ class InheritanceMixin(XBlockMixin):
         default=True,
         scope=Scope.settings
     )
+    video_bumper = Dict(
+        display_name=_("Video Pre-Roll"),
+        help=_(
+            """Identify a video, 5-10 seconds in length, to play before course videos. Enter the video ID from"""
+            """ the Video Uploads page and one or more transcript files in the following format:"""
+            """ {"video_id": "ID", "transcripts": {"language": "/static/filename.srt"}}."""
+            """ For example, an entry for a video with two transcripts looks like this:"""
+            """ {"video_id": "77cef264-d6f5-4cf2-ad9d-0178ab8c77be","""
+            """ "transcripts": {"en": "/static/DemoX-D01_1.srt", "uk": "/static/DemoX-D01_1_uk.srt"}}"""
+        ),
+        scope=Scope.settings
+    )
 
     reset_key = "DEFAULT_SHOW_RESET_BUTTON"
     default_reset_button = getattr(settings, reset_key) if hasattr(settings, reset_key) else False
diff --git a/common/lib/xmodule/xmodule/video_module/__init__.py b/common/lib/xmodule/xmodule/video_module/__init__.py
index 9aa96ecee95..ad2040c1983 100644
--- a/common/lib/xmodule/xmodule/video_module/__init__.py
+++ b/common/lib/xmodule/xmodule/video_module/__init__.py
@@ -8,3 +8,4 @@ Container for video module and it's utils.
 from .transcripts_utils import *
 from .video_utils import *
 from .video_module import *
+from .bumper_utils import *
diff --git a/common/lib/xmodule/xmodule/video_module/bumper_utils.py b/common/lib/xmodule/xmodule/video_module/bumper_utils.py
new file mode 100644
index 00000000000..0bb914d2802
--- /dev/null
+++ b/common/lib/xmodule/xmodule/video_module/bumper_utils.py
@@ -0,0 +1,142 @@
+"""
+Utils for video bumper
+"""
+import copy
+import json
+import pytz
+import logging
+from collections import OrderedDict
+
+from datetime import datetime, timedelta
+from django.conf import settings
+
+from .video_utils import set_query_parameter
+
+try:
+    import edxval.api as edxval_api
+except ImportError:
+    edxval_api = None
+
+log = logging.getLogger(__name__)
+
+
+def get_bumper_settings(video):
+    """
+    Get bumper settings from video instance.
+    """
+    bumper_settings = copy.deepcopy(getattr(video, 'video_bumper', {}))
+
+    # clean up /static/ prefix from bumper transcripts
+    for lang, transcript_url in bumper_settings.get('transcripts', {}).items():
+        bumper_settings['transcripts'][lang] = transcript_url.replace("/static/", "")
+
+    return bumper_settings
+
+
+def is_bumper_enabled(video):
+    """
+    Check if bumper enabled.
+
+    - Feature flag ENABLE_VIDEO_BUMPER should be set to True
+    - Do not show again button should not be clicked by user.
+    - Current time minus periodicity must be greater that last time viewed
+    - edxval_api should be presented
+
+    Returns:
+         bool.
+    """
+    bumper_last_view_date = getattr(video, 'bumper_last_view_date', None)
+    utc_now = datetime.utcnow().replace(tzinfo=pytz.utc)
+    periodicity = settings.FEATURES.get('SHOW_BUMPER_PERIODICITY', 0)
+    has_viewed = any([
+        getattr(video, 'bumper_do_not_show_again'),
+        (bumper_last_view_date and bumper_last_view_date + timedelta(seconds=periodicity) > utc_now)
+    ])
+    is_studio = getattr(video.system, "is_author_mode", False)
+    return bool(
+        not is_studio and
+        settings.FEATURES.get('ENABLE_VIDEO_BUMPER') and
+        get_bumper_settings(video) and
+        edxval_api and
+        not has_viewed
+    )
+
+
+def bumperize(video):
+    """
+    Populate video with bumper settings, if they are presented.
+    """
+    video.bumper = {
+        'enabled': False,
+        'edx_video_id': "",
+        'transcripts': {},
+        'metadata': None,
+    }
+
+    if not is_bumper_enabled(video):
+        return
+
+    bumper_settings = get_bumper_settings(video)
+
+    try:
+        video.bumper['edx_video_id'] = bumper_settings['video_id']
+        video.bumper['transcripts'] = bumper_settings['transcripts']
+    except (TypeError, KeyError):
+        log.warning(
+            "Could not retrieve video bumper information from course settings"
+        )
+        return
+
+    sources = get_bumper_sources(video)
+    if not sources:
+        return
+
+    video.bumper.update({
+        'metadata': bumper_metadata(video, sources),
+        'enabled': True,  # Video poster needs this.
+    })
+
+
+def get_bumper_sources(video):
+    """
+    Get bumper sources from edxval.
+
+    Returns list of sources.
+    """
+    try:
+        val_profiles = ["desktop_webm", "desktop_mp4"]
+        val_video_urls = edxval_api.get_urls_for_profiles(video.bumper['edx_video_id'], val_profiles)
+        bumper_sources = filter(None, [val_video_urls[p] for p in val_profiles])
+    except edxval_api.ValInternalError:
+        # if no bumper sources, nothing will be showed
+        log.warning(
+            "Could not retrieve information from VAL for Bumper edx Video ID: %s.", video.bumper['edx_video_id']
+        )
+        return []
+
+    return bumper_sources
+
+
+def bumper_metadata(video, sources):
+    """
+    Generate bumper metadata.
+    """
+    transcripts = video.get_transcripts_info(is_bumper=True)
+    unused_track_url, bumper_transcript_language, bumper_languages = video.get_transcripts_for_student(transcripts)
+
+    metadata = OrderedDict({
+        'saveStateUrl': video.system.ajax_url + '/save_user_state',
+        'showCaptions': json.dumps(video.show_captions),
+        'sources': sources,
+        'streams': '',
+        'transcriptLanguage': bumper_transcript_language,
+        'transcriptLanguages': bumper_languages,
+        'transcriptTranslationUrl': set_query_parameter(
+            video.runtime.handler_url(video, 'transcript', 'translation/__lang__').rstrip('/?'), 'is_bumper', 1
+        ),
+        'transcriptAvailableTranslationsUrl': set_query_parameter(
+            video.runtime.handler_url(video, 'transcript', 'available_translations').rstrip('/?'), 'is_bumper', 1
+        ),
+    })
+
+    return metadata
diff --git a/common/lib/xmodule/xmodule/video_module/transcripts_utils.py b/common/lib/xmodule/xmodule/video_module/transcripts_utils.py
index 8dca188d9ad..fd82ed34528 100644
--- a/common/lib/xmodule/xmodule/video_module/transcripts_utils.py
+++ b/common/lib/xmodule/xmodule/video_module/transcripts_utils.py
@@ -15,6 +15,8 @@ from xmodule.exceptions import NotFoundError
 from xmodule.contentstore.content import StaticContent
 from xmodule.contentstore.django import contentstore
 
+from .bumper_utils import get_bumper_settings
+
 
 log = logging.getLogger(__name__)
 
@@ -408,20 +410,23 @@ def generate_sjson_for_all_speeds(item, user_filename, result_subs_dict, lang):
     )
 
 
-def get_or_create_sjson(item):
+def get_or_create_sjson(item, transcripts):
     """
     Get sjson if already exists, otherwise generate it.
 
     Generate sjson with subs_id name, from user uploaded srt.
     Subs_id is extracted from srt filename, which was set by user.
 
+    Args:
+        transcipts (dict): dictionary of (language: file) pairs.
+
     Raises:
         TranscriptException: when srt subtitles do not exist,
         and exceptions from generate_subs_from_source.
 
     `item` is module object.
     """
-    user_filename = item.transcripts[item.transcript_language]
+    user_filename = transcripts[item.transcript_language]
     user_subs_id = os.path.splitext(user_filename)[0]
     source_subs_id, result_subs_dict = user_subs_id, {1.0: user_subs_id}
     try:
@@ -517,7 +522,7 @@ class VideoTranscriptsMixin(object):
     This is necessary for both VideoModule and VideoDescriptor.
     """
 
-    def available_translations(self, verify_assets=True):
+    def available_translations(self, transcripts, verify_assets=True):
         """Return a list of language codes for which we have transcripts.
 
         Args:
@@ -528,39 +533,51 @@ class VideoTranscriptsMixin(object):
                 when trying to make a listing of videos and their languages.
 
                 Defaults to True.
+
+            transcripts (dict): A dict with all transcripts and a sub.
+
+                Defaults to False
         """
         translations = []
+        sub, other_lang = transcripts["sub"], transcripts["transcripts"]
 
         # If we're not verifying the assets, we just trust our field values
         if not verify_assets:
-            translations = list(self.transcripts)
-            if not translations or self.sub:
+            translations = list(other_lang)
+            if not translations or sub:
                 translations += ['en']
             return set(translations)
 
         # If we've gotten this far, we're going to verify that the transcripts
         # being referenced are actually in the contentstore.
-        if self.sub:  # check if sjson exists for 'en'.
+        if sub:  # check if sjson exists for 'en'.
             try:
-                Transcript.asset(self.location, self.sub, 'en')
+                Transcript.asset(self.location, sub, 'en')
             except NotFoundError:
-                pass
+                try:
+                    Transcript.asset(self.location, None, None, sub)
+                except NotFoundError:
+                    pass
+                else:
+                    translations = ['en']
             else:
                 translations = ['en']
 
-        for lang in self.transcripts:
+        for lang in other_lang:
             try:
-                Transcript.asset(self.location, None, None, self.transcripts[lang])
+                Transcript.asset(self.location, None, None, other_lang[lang])
             except NotFoundError:
                 continue
             translations.append(lang)
 
         return translations
 
-    def get_transcript(self, transcript_format='srt', lang=None):
+    def get_transcript(self, transcripts, transcript_format='srt', lang=None):
         """
         Returns transcript, filename and MIME type.
 
+        transcripts (dict): A dict with all transcripts and a sub.
+
         Raises:
             - NotFoundError if cannot find transcript file in storage.
             - ValueError if transcript file is empty or incorrect JSON.
@@ -572,11 +589,12 @@ class VideoTranscriptsMixin(object):
         If language is not 'en', give back transcript in proper language and format.
         """
         if not lang:
-            lang = self.transcript_language
+            lang = self.get_default_transcript_language(transcripts)
 
+        sub, other_lang = transcripts["sub"], transcripts["transcripts"]
         if lang == 'en':
-            if self.sub:  # HTML5 case and (Youtube case for new style videos)
-                transcript_name = self.sub
+            if sub:  # HTML5 case and (Youtube case for new style videos)
+                transcript_name = sub
             elif self.youtube_id_1_0:  # old courses
                 transcript_name = self.youtube_id_1_0
             else:
@@ -587,8 +605,8 @@ class VideoTranscriptsMixin(object):
             filename = u'{}.{}'.format(transcript_name, transcript_format)
             content = Transcript.convert(data, 'sjson', transcript_format)
         else:
-            data = Transcript.asset(self.location, None, None, self.transcripts[lang]).data
-            filename = u'{}.{}'.format(os.path.splitext(self.transcripts[lang])[0], transcript_format)
+            data = Transcript.asset(self.location, None, None, other_lang[lang]).data
+            filename = u'{}.{}'.format(os.path.splitext(other_lang[lang])[0], transcript_format)
             content = Transcript.convert(data, 'srt', transcript_format)
 
         if not content:
@@ -597,16 +615,36 @@ class VideoTranscriptsMixin(object):
 
         return content, filename, Transcript.mime_types[transcript_format]
 
-    def get_default_transcript_language(self):
+    def get_default_transcript_language(self, transcripts):
         """
         Returns the default transcript language for this video module.
+
+        Args:
+            transcripts (dict): A dict with all transcripts and a sub.
         """
-        if self.transcript_language in self.transcripts:
+        sub, other_lang = transcripts["sub"], transcripts["transcripts"]
+        if self.transcript_language in other_lang:
             transcript_language = self.transcript_language
-        elif self.sub:
+        elif sub:
             transcript_language = u'en'
-        elif len(self.transcripts) > 0:
-            transcript_language = sorted(self.transcripts)[0]
+        elif len(other_lang) > 0:
+            transcript_language = sorted(other_lang)[0]
         else:
             transcript_language = u'en'
         return transcript_language
+
+    def get_transcripts_info(self, is_bumper=False):
+        """
+        Returns a transcript dictionary for the video.
+        """
+        if is_bumper:
+            transcripts = copy.deepcopy(get_bumper_settings(self).get('transcripts', {}))
+            return {
+                "sub": transcripts.pop("en", ""),
+                "transcripts": transcripts,
+            }
+        else:
+            return {
+                "sub": self.sub,
+                "transcripts": self.transcripts,
+            }
diff --git a/common/lib/xmodule/xmodule/video_module/video_handlers.py b/common/lib/xmodule/xmodule/video_module/video_handlers.py
index 20535884446..4ed6e16e18e 100644
--- a/common/lib/xmodule/xmodule/video_module/video_handlers.py
+++ b/common/lib/xmodule/xmodule/video_module/video_handlers.py
@@ -7,6 +7,7 @@ StudioViewHandlers are handlers for video descriptor instance.
 
 import json
 import logging
+from datetime import datetime
 from webob import Response
 
 from xblock.core import XBlock
@@ -44,7 +45,8 @@ class VideoStudentViewHandlers(object):
         """
         accepted_keys = [
             'speed', 'saved_video_position', 'transcript_language',
-            'transcript_download_format', 'youtube_is_available'
+            'transcript_download_format', 'youtube_is_available',
+            'bumper_last_view_date', 'bumper_do_not_show_again'
         ]
 
         conversions = {
@@ -61,6 +63,9 @@ class VideoStudentViewHandlers(object):
                     else:
                         value = data[key]
 
+                    if key == 'bumper_last_view_date':
+                        value = datetime.utcnow()
+
                     setattr(self, key, value)
 
                     if key == 'speed':
@@ -73,16 +78,17 @@ class VideoStudentViewHandlers(object):
 
         raise NotFoundError('Unexpected dispatch type')
 
-    def translation(self, youtube_id):
+    def translation(self, youtube_id, transcripts):
         """
         This is called to get transcript file for specific language.
 
         youtube_id: str: must be one of youtube_ids or None if HTML video
+        transcripts (dict): A dict with all transcripts and a sub.
 
         Logic flow:
 
         If youtube_id doesn't exist, we have a video in HTML5 mode. Otherwise,
-        video video in Youtube or Flash modes.
+        video in Youtube or Flash modes.
 
         if youtube:
             If english -> give back youtube_id subtitles:
@@ -106,6 +112,7 @@ class VideoStudentViewHandlers(object):
             NotFoundError if for 'en' subtitles no asset is uploaded.
             NotFoundError if youtube_id does not exist / invalid youtube_id
         """
+        sub, other_lang = transcripts["sub"], transcripts["transcripts"]
         if youtube_id:
             # Youtube case:
             if self.transcript_language == 'en':
@@ -122,7 +129,7 @@ class VideoStudentViewHandlers(object):
                 log.info("Can't find content in storage for %s transcript: generating.", youtube_id)
                 generate_sjson_for_all_speeds(
                     self,
-                    self.transcripts[self.transcript_language],
+                    other_lang[self.transcript_language],
                     {speed: youtube_id for youtube_id, speed in youtube_ids.iteritems()},
                     self.transcript_language
                 )
@@ -132,11 +139,18 @@ class VideoStudentViewHandlers(object):
         else:
             # HTML5 case
             if self.transcript_language == 'en':
-                return Transcript.asset(self.location, self.sub).data
-            else:
-                return get_or_create_sjson(self)
+                if '.srt' not in sub:  # not bumper case
+                    return Transcript.asset(self.location, sub).data
+                try:
+                    return get_or_create_sjson(self, {'en': sub})
+                except TranscriptException:
+                    pass  # to raise NotFoundError and try to get data in get_static_transcript
+            elif other_lang:
+                return get_or_create_sjson(self, other_lang)
+
+        raise NotFoundError
 
-    def get_static_transcript(self, request):
+    def get_static_transcript(self, request, transcripts):
         """
         Courses that are imported with the --nostatic flag do not show
         transcripts/captions properly even if those captions are stored inside
@@ -144,6 +158,8 @@ class VideoStudentViewHandlers(object):
         the static asset path of the course if the transcript can't be found
         inside the contentstore and the course has the static_asset_path field
         set.
+
+        transcripts (dict): A dict with all transcripts and a sub.
         """
         response = Response(status=404)
         # Only do redirect for English
@@ -154,7 +170,7 @@ class VideoStudentViewHandlers(object):
         if video_id:
             transcript_name = video_id
         else:
-            transcript_name = self.sub
+            transcript_name = transcripts["sub"]
 
         if transcript_name:
             # Get the asset path for course
@@ -181,7 +197,9 @@ class VideoStudentViewHandlers(object):
         """
         Entry point for transcript handlers for student_view.
 
-        Request GET may contain `videoId` for `translation` dispatch.
+        Request GET contains:
+            (optional) `videoId` for `translation` dispatch.
+            `is_bumper=1` flag for bumper case.
 
         Dispatches, (HTTP GET):
             /translation/[language_id]
@@ -197,15 +215,16 @@ class VideoStudentViewHandlers(object):
                     Returns list of languages, for which transcript files exist.
                     For 'en' check if SJSON exists. For non-`en` check if SRT file exists.
         """
+        is_bumper = request.GET.get('is_bumper', False)
+        transcripts = self.get_transcripts_info(is_bumper)
         if dispatch.startswith('translation'):
-
             language = dispatch.replace('translation', '').strip('/')
 
             if not language:
                 log.info("Invalid /translation request: no language.")
                 return Response(status=400)
 
-            if language not in ['en'] + self.transcripts.keys():
+            if language not in ['en'] + transcripts["transcripts"].keys():
                 log.info("Video: transcript facilities are not available for given language.")
                 return Response(status=404)
 
@@ -213,12 +232,12 @@ class VideoStudentViewHandlers(object):
                 self.transcript_language = language
 
             try:
-                transcript = self.translation(request.GET.get('videoId', None))
+                transcript = self.translation(request.GET.get('videoId', None), transcripts)
             except (TypeError, NotFoundError) as ex:
                 log.info(ex.message)
                 # Try to return static URL redirection as last resort
                 # if no translation is required
-                return self.get_static_transcript(request)
+                return self.get_static_transcript(request, transcripts)
             except (
                 TranscriptException,
                 UnicodeDecodeError,
@@ -232,7 +251,9 @@ class VideoStudentViewHandlers(object):
 
         elif dispatch == 'download':
             try:
-                transcript_content, transcript_filename, transcript_mime_type = self.get_transcript(self.transcript_download_format)
+                transcript_content, transcript_filename, transcript_mime_type = self.get_transcript(
+                    transcripts, transcript_format=self.transcript_download_format
+                )
             except (NotFoundError, ValueError, KeyError, UnicodeDecodeError):
                 log.debug("Video@download exception")
                 return Response(status=404)
@@ -246,8 +267,9 @@ class VideoStudentViewHandlers(object):
                 )
                 response.content_type = transcript_mime_type
 
-        elif dispatch == 'available_translations':
-            available_translations = self.available_translations()
+        elif dispatch.startswith('available_translations'):
+
+            available_translations = self.available_translations(transcripts)
             if available_translations:
                 response = Response(json.dumps(available_translations))
                 response.content_type = 'application/json'
diff --git a/common/lib/xmodule/xmodule/video_module/video_module.py b/common/lib/xmodule/xmodule/video_module/video_module.py
index 8c80f79d14e..d424a9352ce 100644
--- a/common/lib/xmodule/xmodule/video_module/video_module.py
+++ b/common/lib/xmodule/xmodule/video_module/video_module.py
@@ -1,3 +1,4 @@
+
 # -*- coding: utf-8 -*-
 # pylint: disable=abstract-method
 """Video is ungraded Xmodule for support video content.
@@ -37,7 +38,8 @@ from xmodule.xml_module import is_pointer_tag, name_to_pathname, deserialize_fie
 from xmodule.exceptions import NotFoundError
 
 from .transcripts_utils import VideoTranscriptsMixin
-from .video_utils import create_youtube_string, get_video_from_cdn
+from .video_utils import create_youtube_string, get_video_from_cdn, get_poster
+from .bumper_utils import bumperize
 from .video_xfields import VideoFields
 from .video_handlers import VideoStudentViewHandlers, VideoStudioViewHandlers
 
@@ -117,11 +119,21 @@ class VideoModule(VideoFields, VideoTranscriptsMixin, VideoStudentViewHandlers,
             resource_string(module, 'js/src/video/03_video_player.js'),
             resource_string(module, 'js/src/video/035_video_accessible_menu.js'),
             resource_string(module, 'js/src/video/04_video_control.js'),
+            resource_string(module, 'js/src/video/04_video_full_screen.js'),
             resource_string(module, 'js/src/video/05_video_quality_control.js'),
             resource_string(module, 'js/src/video/06_video_progress_slider.js'),
             resource_string(module, 'js/src/video/07_video_volume_control.js'),
             resource_string(module, 'js/src/video/08_video_speed_control.js'),
             resource_string(module, 'js/src/video/09_video_caption.js'),
+            resource_string(module, 'js/src/video/09_play_placeholder.js'),
+            resource_string(module, 'js/src/video/09_play_pause_control.js'),
+            resource_string(module, 'js/src/video/09_play_skip_control.js'),
+            resource_string(module, 'js/src/video/09_skip_control.js'),
+            resource_string(module, 'js/src/video/09_bumper.js'),
+            resource_string(module, 'js/src/video/09_save_state_plugin.js'),
+            resource_string(module, 'js/src/video/09_events_plugin.js'),
+            resource_string(module, 'js/src/video/09_events_bumper_plugin.js'),
+            resource_string(module, 'js/src/video/09_poster.js'),
             resource_string(module, 'js/src/video/095_video_context_menu.js'),
             resource_string(module, 'js/src/video/10_commands.js'),
             resource_string(module, 'js/src/video/10_main.js')
@@ -133,9 +145,13 @@ class VideoModule(VideoFields, VideoTranscriptsMixin, VideoStudentViewHandlers,
     ]}
     js_module_name = "Video"
 
-    def get_transcripts_for_student(self):
+    def get_transcripts_for_student(self, transcripts):
         """Return transcript information necessary for rendering the XModule student view.
         This is more or less a direct extraction from `get_html`.
+
+        Args:
+            transcripts (dict): A dict with all transcripts and a sub.
+
         Returns:
             Tuple of (track_url, transcript_language, sorted_languages)
             track_url -> subtitle download url
@@ -143,31 +159,27 @@ class VideoModule(VideoFields, VideoTranscriptsMixin, VideoStudentViewHandlers,
             sorted_languages -> dictionary of available transcript languages
         """
         track_url = None
+        sub, other_lang = transcripts["sub"], transcripts["transcripts"]
         if self.download_track:
             if self.track:
                 track_url = self.track
-            elif self.sub or self.transcripts:
+            elif sub or other_lang:
                 track_url = self.runtime.handler_url(self, 'transcript', 'download').rstrip('/?')
 
-        if not self.transcripts:
-            transcript_language = u'en'
-            languages = {'en': 'English'}
-        else:
-            transcript_language = self.get_default_transcript_language()
-
-            native_languages = {lang: label for lang, label in settings.LANGUAGES if len(lang) == 2}
-            languages = {
-                lang: native_languages.get(lang, display)
-                for lang, display in settings.ALL_LANGUAGES
-                if lang in self.transcripts
-            }
+        transcript_language = self.get_default_transcript_language(transcripts)
 
-            if self.sub:
-                languages['en'] = 'English'
+        native_languages = {lang: label for lang, label in settings.LANGUAGES if len(lang) == 2}
+        languages = {
+            lang: native_languages.get(lang, display)
+            for lang, display in settings.ALL_LANGUAGES
+            if lang in other_lang
+        }
+        if not other_lang or (other_lang and sub):
+            languages['en'] = 'English'
 
         # OrderedDict for easy testing of rendered context in tests
         sorted_languages = sorted(languages.items(), key=itemgetter(1))
-        if 'table' in self.transcripts:
+        if 'table' in other_lang:
             sorted_languages.insert(0, ('table', 'Table of Contents'))
 
         sorted_languages = OrderedDict(sorted_languages)
@@ -233,7 +245,7 @@ class VideoModule(VideoFields, VideoTranscriptsMixin, VideoStudentViewHandlers,
             elif self.html5_sources:
                 download_video_link = self.html5_sources[0]
 
-        track_url, transcript_language, sorted_languages = self.get_transcripts_for_student()
+        track_url, transcript_language, sorted_languages = self.get_transcripts_for_student(self.get_transcripts_info())
 
         # CDN_VIDEO_URLS is only to be used here and will be deleted
         # TODO(ali@edx.org): Delete this after the CDN experiment has completed.
@@ -250,42 +262,73 @@ class VideoModule(VideoFields, VideoTranscriptsMixin, VideoStudentViewHandlers,
             cdn_eval = False
             cdn_exp_group = None
 
-        return self.system.render_template('video.html', {
-            'ajax_url': self.system.ajax_url + '/save_user_state',
+        self.youtube_streams = youtube_streams or create_youtube_string(self)  # pylint: disable=W0201
+        metadata = {
+            'saveStateUrl': self.system.ajax_url + '/save_user_state',
             'autoplay': settings.FEATURES.get('AUTOPLAY_VIDEOS', False),
+            'streams': self.youtube_streams,
+            'sub': self.sub,
+            'sources': sources,
+
+            # This won't work when we move to data that
+            # isn't on the filesystem
+            'captionDataDir': getattr(self, 'data_dir', None),
+
+            'showCaptions': json.dumps(self.show_captions),
+            'generalSpeed': self.global_speed,
+            'speed': self.speed,
+            'savedVideoPosition': self.saved_video_position.total_seconds(),
+            'start': self.start_time.total_seconds(),
+            'end': self.end_time.total_seconds(),
+            'transcriptLanguage': transcript_language,
+            'transcriptLanguages': sorted_languages,
+
+            # TODO: Later on the value 1500 should be taken from some global
+            # configuration setting field.
+            'ytTestTimeout': 1500,
+
+            'ytApiUrl': settings.YOUTUBE['API'],
+            'ytTestUrl': settings.YOUTUBE['TEST_URL'],
+            'transcriptTranslationUrl': self.runtime.handler_url(
+                self, 'transcript', 'translation/__lang__'
+            ).rstrip('/?'),
+            'transcriptAvailableTranslationsUrl': self.runtime.handler_url(
+                self, 'transcript', 'available_translations'
+            ).rstrip('/?'),
+
+            ## For now, the option "data-autohide-html5" is hard coded. This option
+            ## either enables or disables autohiding of controls and captions on mouse
+            ## inactivity. If set to true, controls and captions will autohide for
+            ## HTML5 sources (non-YouTube) after a period of mouse inactivity over the
+            ## whole video. When the mouse moves (or a key is pressed while any part of
+            ## the video player is focused), the captions and controls will be shown
+            ## once again.
+            ##
+            ## There is no option in the "Advanced Editor" to set this option. However,
+            ## this option will have an effect if changed to "True". The code on
+            ## front-end exists.
+            'autohideHtml5': False
+        }
+
+        bumperize(self)
+
+        context = {
+            'bumper_metadata': json.dumps(self.bumper['metadata']),  # pylint: disable=E1101
+            'metadata': json.dumps(OrderedDict(metadata)),
+            'poster': json.dumps(get_poster(self)),
             'branding_info': branding_info,
             'cdn_eval': cdn_eval,
             'cdn_exp_group': cdn_exp_group,
-            # This won't work when we move to data that
-            # isn't on the filesystem
-            'data_dir': getattr(self, 'data_dir', None),
+            'id': self.location.html_id(),
             'display_name': self.display_name_with_default,
-            'end': self.end_time.total_seconds(),
             'handout': self.handout,
-            'id': self.location.html_id(),
-            'show_captions': json.dumps(self.show_captions),
             'download_video_link': download_video_link,
-            'sources': json.dumps(sources),
-            'speed': json.dumps(self.speed),
-            'general_speed': self.global_speed,
-            'saved_video_position': self.saved_video_position.total_seconds(),
-            'start': self.start_time.total_seconds(),
-            'sub': self.sub,
             'track': track_url,
-            'youtube_streams': youtube_streams or create_youtube_string(self),
-            # TODO: Later on the value 1500 should be taken from some global
-            # configuration setting field.
-            'yt_test_timeout': 1500,
-            'yt_api_url': settings.YOUTUBE['API'],
-            'yt_test_url': settings.YOUTUBE['TEST_URL'],
             'transcript_download_format': transcript_download_format,
             'transcript_download_formats_list': self.descriptor.fields['transcript_download_format'].values,
-            'transcript_language': transcript_language,
-            'transcript_languages': json.dumps(sorted_languages),
-            'transcript_translation_url': self.runtime.handler_url(self, 'transcript', 'translation').rstrip('/?'),
-            'transcript_available_translations_url': self.runtime.handler_url(self, 'transcript', 'available_translations').rstrip('/?'),
             'license': getattr(self, "license", None),
-        })
+        }
+        return self.system.render_template('video.html', context)
 
 
 @XBlock.wants("settings")
@@ -670,7 +713,10 @@ class VideoDescriptor(VideoFields, VideoTranscriptsMixin, VideoStudioViewHandler
         def _update_transcript_for_index(language=None):
             """ Find video transcript - if not found, don't update index """
             try:
-                transcript = self.get_transcript(transcript_format='txt', lang=language)[0].replace("\n", " ")
+                transcripts = self.get_transcripts_info()
+                transcript = self.get_transcript(
+                    transcripts, transcript_format='txt', lang=language
+                )[0].replace("\n", " ")
                 transcript_index_name = "transcript_{}".format(language if language else self.transcript_language)
                 video_body.update({transcript_index_name: transcript})
             except NotFoundError:
diff --git a/common/lib/xmodule/xmodule/video_module/video_utils.py b/common/lib/xmodule/xmodule/video_module/video_utils.py
index 428e50e05e9..dc556dea690 100644
--- a/common/lib/xmodule/xmodule/video_module/video_utils.py
+++ b/common/lib/xmodule/xmodule/video_module/video_utils.py
@@ -3,9 +3,14 @@
 Module contains utils specific for video_module but not for transcripts.
 """
 import json
+from collections import OrderedDict
 import logging
 import urllib
 import requests
+from urllib import urlencode
+from urlparse import parse_qs, urlsplit, urlunsplit
+
+from django.conf import settings
 
 from requests.exceptions import RequestException
 
@@ -71,3 +76,40 @@ def get_video_from_cdn(cdn_base_url, original_video_url):
         return cdn_content['sources'][0]
     else:
         return None
+
+
+def get_poster(video):
+    """
+    Generate poster metadata.
+
+    youtube_streams is string that contains '1.00:youtube_id'
+
+    Poster metadata is dict of youtube url for image thumbnail and edx logo
+    """
+    if not video.bumper.get("enabled"):
+        return
+
+    poster = OrderedDict({"url": "", "type": ""})
+
+    if video.youtube_streams:
+        youtube_id = video.youtube_streams.split('1.00:')[1].split(',')[0]
+        poster["url"] = settings.YOUTUBE['IMAGE_API'].format(youtube_id=youtube_id)
+        poster["type"] = "youtube"
+    else:
+        poster["url"] = "https://www.edx.org/sites/default/files/theme/edx-logo-header.png"
+        poster["type"] = "html5"
+
+    return poster
+
+
+def set_query_parameter(url, param_name, param_value):
+    """
+    Given a URL, set or replace a query parameter and return the
+    modified URL.
+    """
+    scheme, netloc, path, query_string, fragment = urlsplit(url)
+    query_params = parse_qs(query_string)
+    query_params[param_name] = [param_value]
+    new_query_string = urlencode(query_params, doseq=True)
+
+    return urlunsplit((scheme, netloc, path, new_query_string, fragment))
diff --git a/common/lib/xmodule/xmodule/video_module/video_xfields.py b/common/lib/xmodule/xmodule/video_module/video_xfields.py
index 5bf2195b883..83375f23ab2 100644
--- a/common/lib/xmodule/xmodule/video_module/video_xfields.py
+++ b/common/lib/xmodule/xmodule/video_module/video_xfields.py
@@ -3,7 +3,7 @@ XFields for video module.
 """
 import datetime
 
-from xblock.fields import Scope, String, Float, Boolean, List, Dict
+from xblock.fields import Scope, String, Float, Boolean, List, Dict, DateTime
 
 from xmodule.fields import RelativeTime
 from xmodule.mixin import LicenseMixin
@@ -142,7 +142,7 @@ class VideoFields(LicenseMixin):
     )
     speed = Float(
         help=_("The last speed that the user specified for the video."),
-        scope=Scope.user_state,
+        scope=Scope.user_state
     )
     global_speed = Float(
         help=_("The default speed for the video."),
@@ -174,3 +174,12 @@ class VideoFields(LicenseMixin):
         scope=Scope.settings,
         default="",
     )
+    bumper_last_view_date = DateTime(
+        display_name=_("Date of the last view of the bumper"),
+        scope=Scope.preferences,
+    )
+    bumper_do_not_show_again = Boolean(
+        display_name=_("Do not show bumper again"),
+        scope=Scope.preferences,
+        default=False,
+    )
diff --git a/common/test/acceptance/pages/lms/video/video.py b/common/test/acceptance/pages/lms/video/video.py
index 86150798ee6..c7300234664 100644
--- a/common/test/acceptance/pages/lms/video/video.py
+++ b/common/test/acceptance/pages/lms/video/video.py
@@ -3,6 +3,7 @@ Video player in the courseware.
 """
 
 import time
+import json
 import requests
 from selenium.webdriver.common.action_chains import ActionChains
 from bok_choy.page_object import PageObject
@@ -21,10 +22,12 @@ VIDEO_BUTTONS = {
     'download_transcript': '.video-tracks > a',
     'speed': '.speeds',
     'quality': '.quality-control',
+    'do_not_show_again': '.skip-control',
+    'skip_bumper': '.play-skip-control',
 }
 
 CSS_CLASS_NAMES = {
-    'closed_captions': '.closed .subtitles',
+    'closed_captions': '.video.closed',
     'captions_rendered': '.video.is-captions-rendered',
     'captions': '.subtitles',
     'captions_text': '.subtitles > li',
@@ -37,7 +40,8 @@ CSS_CLASS_NAMES = {
     'video_time': 'div.vidtime',
     'video_display_name': '.vert h2',
     'captions_lang_list': '.langs-list li',
-    'video_speed': '.speeds .value'
+    'video_speed': '.speeds .value',
+    'poster': '.poster',
 }
 
 VIDEO_MODES = {
@@ -79,7 +83,7 @@ class VideoPage(PageObject):
         self.wait_for_element_presence(video_selector, 'Video is initialized')
 
     @wait_for_js
-    def wait_for_video_player_render(self):
+    def wait_for_video_player_render(self, autoplay=False):
         """
         Wait until Video Player Rendered Completely.
 
@@ -88,7 +92,12 @@ class VideoPage(PageObject):
         self.wait_for_element_presence(CSS_CLASS_NAMES['video_init'], 'Video Player Initialized')
         self.wait_for_element_presence(CSS_CLASS_NAMES['video_time'], 'Video Player Initialized')
 
-        video_player_buttons = ['volume', 'play', 'fullscreen', 'speed']
+        video_player_buttons = ['volume', 'fullscreen', 'speed']
+        if autoplay:
+            video_player_buttons.append('pause')
+        else:
+            video_player_buttons.append('play')
+
         for button in video_player_buttons:
             self.wait_for_element_visibility(VIDEO_BUTTONS[button], '{} button is visible'.format(button.title()))
 
@@ -106,6 +115,34 @@ class VideoPage(PageObject):
 
         self.wait_for_ajax()
 
+    @wait_for_js
+    def wait_for_video_bumper_render(self):
+        """
+        Wait until Poster, Video Pre-Roll and main Video Player are Rendered Completely.
+        """
+        self.wait_for_video_class()
+        self.wait_for_element_presence(CSS_CLASS_NAMES['video_init'], 'Video Player Initialized')
+        self.wait_for_element_presence(CSS_CLASS_NAMES['video_time'], 'Video Player Initialized')
+
+        video_player_buttons = ['do_not_show_again', 'skip_bumper', 'volume']
+        for button in video_player_buttons:
+            self.wait_for_element_visibility(VIDEO_BUTTONS[button], '{} button is visible'.format(button.title()))
+
+    @property
+    def is_poster_shown(self):
+        """
+        Check whether a poster is show.
+        """
+        selector = self.get_element_selector(CSS_CLASS_NAMES['poster'])
+        return self.q(css=selector).visible
+
+    def click_on_poster(self):
+        """
+        Click on the video poster.
+        """
+        selector = self.get_element_selector(CSS_CLASS_NAMES['poster'])
+        self.q(css=selector).click()
+
     def get_video_vertical_selector(self, video_display_name=None):
         """
         Get selector for a video vertical with display name specified by `video_display_name`.
@@ -184,19 +221,14 @@ class VideoPage(PageObject):
     @property
     def is_autoplay_enabled(self):
         """
-        Extract `data-autoplay` attribute to check video autoplay is enabled or disabled.
+        Extract autoplay value of `data-metadata` attribute to check video autoplay is enabled or disabled.
 
         Returns:
             bool: Tells if autoplay enabled/disabled.
-
         """
         selector = self.get_element_selector(CSS_CLASS_NAMES['video_container'])
-        auto_play = self.q(css=selector).attrs('data-autoplay')[0]
-
-        if auto_play.lower() == 'false':
-            return False
-
-        return True
+        auto_play = json.loads(self.q(css=selector).attrs('data-metadata')[0])['autoplay']
+        return auto_play
 
     @property
     def is_error_message_shown(self):
@@ -268,6 +300,7 @@ class VideoPage(PageObject):
             bool: True means captions are visible, False means captions are not visible
 
         """
+        self.wait_for_ajax()
         caption_state_selector = self.get_element_selector(CSS_CLASS_NAMES['closed_captions'])
         return not self.q(css=caption_state_selector).present
 
@@ -515,6 +548,7 @@ class VideoPage(PageObject):
 
         language_selector = VIDEO_MENUS["language"] + ' li[data-lang-code="{code}"]'.format(code=code)
         language_selector = self.get_element_selector(language_selector)
+
         self.wait_for_element_visibility(language_selector, 'language menu is visible')
         self.q(css=language_selector).first.click()
 
diff --git a/common/test/acceptance/pages/studio/settings_advanced.py b/common/test/acceptance/pages/studio/settings_advanced.py
index 0dfad8cf445..dfca78bd55f 100644
--- a/common/test/acceptance/pages/studio/settings_advanced.py
+++ b/common/test/acceptance/pages/studio/settings_advanced.py
@@ -200,4 +200,5 @@ class AdvancedSettingsPage(CoursePage):
             'social_sharing_url',
             'teams_configuration',
             'minimum_grade_credit',
+            'video_bumper',
         ]
diff --git a/common/test/acceptance/tests/video/test_video_events.py b/common/test/acceptance/tests/video/test_video_events.py
index dabe17bda2a..f851a02bcae 100644
--- a/common/test/acceptance/tests/video/test_video_events.py
+++ b/common/test/acceptance/tests/video/test_video_events.py
@@ -2,15 +2,62 @@
 
 import datetime
 import json
+import ddt
 
 from ..helpers import EventsTestMixin
 from .test_video_module import VideoBaseTest
+from ...pages.lms.video.video import _parse_time_str
 
 from openedx.core.lib.tests.assertions.events import assert_event_matches, assert_events_equal
 from opaque_keys.edx.keys import UsageKey, CourseKey
 
 
-class VideoEventsTest(EventsTestMixin, VideoBaseTest):
+class VideoEventsTestMixin(EventsTestMixin, VideoBaseTest):
+    """
+    Useful helper methods to test video player event emission.
+    """
+    def assert_payload_contains_ids(self, video_event):
+        """
+        Video events should all contain "id" and "code" attributes in their payload.
+
+        This function asserts that those fields are present and have correct values.
+        """
+        video_descriptors = self.course_fixture.get_nested_xblocks(category='video')
+        video_desc = video_descriptors[0]
+        video_locator = UsageKey.from_string(video_desc.locator)
+
+        expected_event = {
+            'event': {
+                'id': video_locator.html_id(),
+                'code': '3_yD_cEKoCk'
+            }
+        }
+        self.assert_events_match([expected_event], [video_event])
+
+    def assert_valid_control_event_at_time(self, video_event, time_in_seconds):
+        """
+        Video control events should contain valid ID fields and a valid "currentTime" field.
+
+        This function asserts that those fields are present and have correct values.
+        """
+        current_time = json.loads(video_event['event'])['currentTime']
+        self.assertAlmostEqual(current_time, time_in_seconds, delta=1)
+
+    def assert_field_type(self, event_dict, field, field_type):
+        """Assert that a particular `field` in the `event_dict` has a particular type"""
+        self.assertIn(field, event_dict, '{0} not found in the root of the event'.format(field))
+        self.assertTrue(
+            isinstance(event_dict[field], field_type),
+            'Expected "{key}" to be a "{field_type}", but it has the value "{value}" of type "{t}"'.format(
+                key=field,
+                value=event_dict[field],
+                t=type(event_dict[field]),
+                field_type=field_type,
+            )
+        )
+
+
+class VideoEventsTest(VideoEventsTestMixin):
     """ Test video player event emission """
 
     def test_video_control_events(self):
@@ -47,33 +94,197 @@ class VideoEventsTest(EventsTestMixin, VideoBaseTest):
                 assert_event_matches({'event_type': 'pause_video'}, video_event)
                 self.assert_valid_control_event_at_time(video_event, self.video.seconds)
 
-    def assert_payload_contains_ids(self, video_event):
+    def test_strict_event_format(self):
         """
-        Video events should all contain "id" and "code" attributes in their payload.
+        This test makes a very strong assertion about the fields present in events. The goal of it is to ensure that new
+        fields are not added to all events mistakenly. It should be the only existing test that is updated when new top
+        level fields are added to all events.
+        """
+
+        captured_events = []
+        with self.capture_events(lambda e: e['event_type'] == 'load_video', captured_events=captured_events):
+            self.navigate_to_video()
+
+        load_video_event = captured_events[0]
+
+        # Validate the event payload
+        self.assert_payload_contains_ids(load_video_event)
+
+        # We cannot predict the value of these fields so we make weaker assertions about them
+        dynamic_string_fields = (
+            'accept_language',
+            'agent',
+            'host',
+            'ip',
+            'event',
+            'session'
+        )
+        for field in dynamic_string_fields:
+            self.assert_field_type(load_video_event, field, basestring)
+            self.assertIn(field, load_video_event, '{0} not found in the root of the event'.format(field))
+            del load_video_event[field]
+
+        # A weak assertion for the timestamp as well
+        self.assert_field_type(load_video_event, 'time', datetime.datetime)
+        del load_video_event['time']
+
+        # Note that all unpredictable fields have been deleted from the event at this point
+
+        course_key = CourseKey.from_string(self.course_id)
+        static_fields_pattern = {
+            'context': {
+                'course_id': unicode(course_key),
+                'org_id': course_key.org,
+                'path': '/event',
+                'user_id': self.user_info['user_id']
+            },
+            'event_source': 'browser',
+            'event_type': 'load_video',
+            'username': self.user_info['username'],
+            'page': self.browser.current_url,
+            'referer': self.browser.current_url,
+            'name': 'load_video',
+        }
+        assert_events_equal(static_fields_pattern, load_video_event)
+
+
+@ddt.ddt
+class VideoBumperEventsTest(VideoEventsTestMixin):
+    """ Test bumper video event emission """
+
+    # helper methods
+    def watch_video_and_skip(self):
+        """
+        Wait 5 seconds and press "skip" button.
+        """
+        self.video.wait_for_position('0:05')
+        self.video.click_player_button('skip_bumper')
+
+    def watch_video_and_dismiss(self):
+        """
+        Wait 5 seconds and press "do not show again" button.
+        """
+        self.video.wait_for_position('0:05')
+        self.video.click_player_button('do_not_show_again')
+
+    def wait_for_state(self, state='finished'):
+        """
+        Wait until video will be in given state.
+
+        Finished state means that video is played to the end.
+        """
+        self.video.wait_for_state(state)
+
+    def add_bumper(self):
+        """
+        Add video bumper to the course.
+        """
+        additional_data = {
+            u'video_bumper': {
+                u'value': {
+                    "transcripts": {},
+                    "video_id": "edx_video_id"
+                }
+            }
+        }
+        self.course_fixture.add_advanced_settings(additional_data)
+
+    @ddt.data(
+        ('edx.video.bumper.skipped', watch_video_and_skip),
+        ('edx.video.bumper.dismissed', watch_video_and_dismiss),
+        ('edx.video.bumper.stopped', wait_for_state)
+    )
+    @ddt.unpack
+    def test_video_control_events(self, event_type, action):
+        """
+        Scenario: Video component with pre-roll emits events correctly
+        Given the course has a Video component in "Youtube" mode with pre-roll enabled
+        And I click on the video poster
+        And the pre-roll video start playing
+        And I watch (5 seconds/5 seconds/to the end of) it
+        And I click (skip/do not show again) video button
+
+        Then a "edx.video.bumper.loaded" event is emitted
+        And a "edx.video.bumper.played" event is emitted
+        And a "edx.video.bumper.skipped/dismissed/stopped" event is emitted
+        And a "load_video" event is emitted
+        And a "play_video" event is emitted
+        """
+
+        def is_video_event(event):
+            """Filter out anything other than the video events of interest"""
+            return event['event_type'] in (
+                'edx.video.bumper.loaded',
+                'edx.video.bumper.played',
+                'edx.video.bumper.skipped',
+                'edx.video.bumper.dismissed',
+                'edx.video.bumper.stopped',
+                'load_video',
+                'play_video',
+                'pause_video'
+            ) and self.video.state != 'buffering'
+
+        captured_events = []
+        self.add_bumper()
+        with self.capture_events(is_video_event, number_of_matches=5, captured_events=captured_events):
+            self.navigate_to_video_no_render()
+            self.video.click_on_poster()
+            self.video.wait_for_video_bumper_render()
+            sources, duration = self.video.sources[0], self.video.duration
+            action(self)
+
+        # Filter subsequent events that appear due to bufferisation: edx.video.bumper.played
+        # As bumper does not emit pause event, we filter subsequent edx.video.bumper.played events from
+        # the list, except first.
+        filtered_events = []
+        for video_event in captured_events:
+            is_played_event = video_event['event_type'] == 'edx.video.bumper.played'
+            appears_again = filtered_events and video_event['event_type'] == filtered_events[-1]['event_type']
+            if is_played_event and appears_again:
+                continue
+            filtered_events.append(video_event)
+
+        for idx, video_event in enumerate(filtered_events):
+            if idx < 3:
+                self.assert_bumper_payload_contains_ids(video_event, sources, duration)
+            else:
+                self.assert_payload_contains_ids(video_event)
+
+            if idx == 0:
+                assert_event_matches({'event_type': 'edx.video.bumper.loaded'}, video_event)
+            elif idx == 1:
+                assert_event_matches({'event_type': 'edx.video.bumper.played'}, video_event)
+                self.assert_valid_control_event_at_time(video_event, 0)
+            elif idx == 2:
+                assert_event_matches({'event_type': event_type}, video_event)
+            elif idx == 3:
+                assert_event_matches({'event_type': 'load_video'}, video_event)
+            elif idx == 4:
+                assert_event_matches({'event_type': 'play_video'}, video_event)
+                self.assert_valid_control_event_at_time(video_event, 0)
+
+    def assert_bumper_payload_contains_ids(self, video_event, sources, duration):
+        """
+        Bumper video events should all contain "host_component_id", "bumper_id",
+        "duration", "code" attributes in their payload.
 
         This function asserts that those fields are present and have correct values.
         """
+        self.add_bumper()
         video_descriptors = self.course_fixture.get_nested_xblocks(category='video')
         video_desc = video_descriptors[0]
         video_locator = UsageKey.from_string(video_desc.locator)
 
         expected_event = {
             'event': {
-                'id': video_locator.html_id(),
-                'code': '3_yD_cEKoCk'
+                'host_component_id': video_locator.html_id(),
+                'bumper_id': sources,
+                'duration': _parse_time_str(duration),
+                'code': 'html5'
             }
         }
         self.assert_events_match([expected_event], [video_event])
 
-    def assert_valid_control_event_at_time(self, video_event, time_in_seconds):
-        """
-        Video control events should contain valid ID fields and a valid "currentTime" field.
-
-        This function asserts that those fields are present and have correct values.
-        """
-        current_time = json.loads(video_event['event'])['currentTime']
-        self.assertAlmostEqual(current_time, time_in_seconds, delta=1)
-
     def test_strict_event_format(self):
         """
         This test makes a very strong assertion about the fields present in events. The goal of it is to ensure that new
@@ -82,13 +293,17 @@ class VideoEventsTest(EventsTestMixin, VideoBaseTest):
         """
 
         captured_events = []
-        with self.capture_events(lambda e: e['event_type'] == 'load_video', captured_events=captured_events):
-            self.navigate_to_video()
+        self.add_bumper()
+        filter_event = lambda e: e['event_type'] == 'edx.video.bumper.loaded'
+        with self.capture_events(filter_event, captured_events=captured_events):
+            self.navigate_to_video_no_render()
+            self.video.click_on_poster()
 
         load_video_event = captured_events[0]
 
         # Validate the event payload
-        self.assert_payload_contains_ids(load_video_event)
+        sources, duration = self.video.sources[0], self.video.duration
+        self.assert_bumper_payload_contains_ids(load_video_event, sources, duration)
 
         # We cannot predict the value of these fields so we make weaker assertions about them
         dynamic_string_fields = (
@@ -119,23 +334,10 @@ class VideoEventsTest(EventsTestMixin, VideoBaseTest):
                 'user_id': self.user_info['user_id']
             },
             'event_source': 'browser',
-            'event_type': 'load_video',
+            'event_type': 'edx.video.bumper.loaded',
             'username': self.user_info['username'],
             'page': self.browser.current_url,
             'referer': self.browser.current_url,
-            'name': 'load_video',
+            'name': 'edx.video.bumper.loaded',
         }
         assert_events_equal(static_fields_pattern, load_video_event)
-
-    def assert_field_type(self, event_dict, field, field_type):
-        """Assert that a particular `field` in the `event_dict` has a particular type"""
-        self.assertIn(field, event_dict, '{0} not found in the root of the event'.format(field))
-        self.assertTrue(
-            isinstance(event_dict[field], field_type),
-            'Expected "{key}" to be a "{field_type}", but it has the value "{value}" of type "{t}"'.format(
-                key=field,
-                value=event_dict[field],
-                t=type(event_dict[field]),
-                field_type=field_type,
-            )
-        )
diff --git a/common/test/acceptance/tests/video/test_video_module.py b/common/test/acceptance/tests/video/test_video_module.py
index 727ff7e8ba4..48ee3b5dd85 100644
--- a/common/test/acceptance/tests/video/test_video_module.py
+++ b/common/test/acceptance/tests/video/test_video_module.py
@@ -397,6 +397,7 @@ class YouTubeVideoTest(VideoBaseTest):
             'time_to_response': 2.0,
             'youtube_api_blocked': True,
         })
+
         self.metadata = self.metadata_for_mode('youtube_html5')
 
         self.navigate_to_video()
@@ -711,6 +712,84 @@ class YouTubeVideoTest(VideoBaseTest):
 
         self.assertEqual(self.video.caption_languages, {'zh_HANS': 'Simplified Chinese', 'zh_HANT': 'Traditional Chinese'})
 
+    def test_video_bumper_render(self):
+        """
+        Scenario: Multiple videos with bumper in sequentials all load and work, switching between sequentials
+        Given it has videos "A,B" in "Youtube" and "HTML5" modes in position "1" of sequential
+        And video "C" in "Youtube" mode in position "2" of sequential
+        When I open sequential position "1"
+        Then I see video "B" has a poster
+        When I click on it
+        Then I see video bumper is playing
+        When I skip the bumper
+        Then I see the main video
+        When I click on video "A"
+        Then the main video starts playing
+        When I open sequential position "2"
+        And click on the poster
+        Then the main video starts playing
+        Then I see that the main video starts playing once I go back to position "2" of sequential
+        When I reload the page
+        Then I see that the main video starts playing when I click on the poster
+        """
+        additional_data = {
+            u'video_bumper': {
+                u'value': {
+                    "transcripts": {},
+                    "video_id": "edx_video_id"
+                }
+            }
+        }
+
+        self.verticals = [
+            [{'display_name': 'A'}, {'display_name': 'B', 'metadata': self.metadata_for_mode('html5')}],
+            [{'display_name': 'C'}]
+        ]
+
+        tab1_video_names = ['A', 'B']
+        tab2_video_names = ['C']
+
+        def execute_video_steps(video_names):
+            """
+            Execute video steps
+            """
+            for video_name in video_names:
+                self.video.use_video(video_name)
+                self.assertTrue(self.video.is_poster_shown)
+                self.video.click_on_poster()
+                self.video.wait_for_video_player_render(autoplay=True)
+                self.assertIn(self.video.state, ['playing', 'buffering', 'finished'])
+
+        self.course_fixture.add_advanced_settings(additional_data)
+        self.navigate_to_video_no_render()
+
+        self.video.use_video('B')
+        self.assertTrue(self.video.is_poster_shown)
+        self.video.click_on_poster()
+        self.video.wait_for_video_bumper_render()
+        self.assertIn(self.video.state, ['playing', 'buffering', 'finished'])
+        self.video.click_player_button('skip_bumper')
+
+        # no autoplay here, maybe video is too small, so pause is not switched
+        self.video.wait_for_video_player_render()
+        self.assertIn(self.video.state, ['playing', 'buffering', 'finished'])
+
+        self.video.use_video('A')
+        execute_video_steps(['A'])
+
+        # go to second sequential position
+        self.course_nav.go_to_sequential_position(2)
+
+        execute_video_steps(tab2_video_names)
+
+        # go back to first sequential position
+        # we are again playing tab 1 videos to ensure that switching didn't broke some video functionality.
+        self.course_nav.go_to_sequential_position(1)
+        execute_video_steps(tab1_video_names)
+
+        self.video.browser.refresh()
+        execute_video_steps(tab1_video_names)
+
 
 class YouTubeHtml5VideoTest(VideoBaseTest):
     """ Test YouTube HTML5 Video Player """
diff --git a/common/test/db_cache/bok_choy_data.json b/common/test/db_cache/bok_choy_data.json
index f8500b6768a..9f41bdda53b 100644
--- a/common/test/db_cache/bok_choy_data.json
+++ b/common/test/db_cache/bok_choy_data.json
@@ -1 +1 @@
-[{"pk": 74, "model": "contenttypes.contenttype", "fields": {"model": "accesstoken", "name": "access token", "app_label": "oauth2"}}, {"pk": 151, "model": "contenttypes.contenttype", "fields": {"model": "aiclassifier", "name": "ai classifier", "app_label": "assessment"}}, {"pk": 150, "model": "contenttypes.contenttype", "fields": {"model": "aiclassifierset", "name": "ai classifier set", "app_label": "assessment"}}, {"pk": 153, "model": "contenttypes.contenttype", "fields": {"model": "aigradingworkflow", "name": "ai grading workflow", "app_label": "assessment"}}, {"pk": 152, "model": "contenttypes.contenttype", "fields": {"model": "aitrainingworkflow", "name": "ai training workflow", "app_label": "assessment"}}, {"pk": 34, "model": "contenttypes.contenttype", "fields": {"model": "anonymoususerid", "name": "anonymous user id", "app_label": "student"}}, {"pk": 77, "model": "contenttypes.contenttype", "fields": {"model": "article", "name": "article", "app_label": "wiki"}}, {"pk": 78, "model": "contenttypes.contenttype", "fields": {"model": "articleforobject", "name": "Article for object", "app_label": "wiki"}}, {"pk": 81, "model": "contenttypes.contenttype", "fields": {"model": "articleplugin", "name": "article plugin", "app_label": "wiki"}}, {"pk": 79, "model": "contenttypes.contenttype", "fields": {"model": "articlerevision", "name": "article revision", "app_label": "wiki"}}, {"pk": 86, "model": "contenttypes.contenttype", "fields": {"model": "articlesubscription", "name": "article subscription", "app_label": "wiki"}}, {"pk": 141, "model": "contenttypes.contenttype", "fields": {"model": "assessment", "name": "assessment", "app_label": "assessment"}}, {"pk": 144, "model": "contenttypes.contenttype", "fields": {"model": "assessmentfeedback", "name": "assessment feedback", "app_label": "assessment"}}, {"pk": 143, "model": "contenttypes.contenttype", "fields": {"model": "assessmentfeedbackoption", "name": "assessment feedback option", "app_label": "assessment"}}, {"pk": 142, "model": "contenttypes.contenttype", "fields": {"model": "assessmentpart", "name": "assessment part", "app_label": "assessment"}}, {"pk": 154, "model": "contenttypes.contenttype", "fields": {"model": "assessmentworkflow", "name": "assessment workflow", "app_label": "workflow"}}, {"pk": 156, "model": "contenttypes.contenttype", "fields": {"model": "assessmentworkflowcancellation", "name": "assessment workflow cancellation", "app_label": "workflow"}}, {"pk": 155, "model": "contenttypes.contenttype", "fields": {"model": "assessmentworkflowstep", "name": "assessment workflow step", "app_label": "workflow"}}, {"pk": 19, "model": "contenttypes.contenttype", "fields": {"model": "association", "name": "association", "app_label": "django_openid_auth"}}, {"pk": 25, "model": "contenttypes.contenttype", "fields": {"model": "association", "name": "association", "app_label": "default"}}, {"pk": 70, "model": "contenttypes.contenttype", "fields": {"model": "brandinginfoconfig", "name": "branding info config", "app_label": "branding"}}, {"pk": 57, "model": "contenttypes.contenttype", "fields": {"model": "certificategenerationconfiguration", "name": "certificate generation configuration", "app_label": "certificates"}}, {"pk": 56, "model": "contenttypes.contenttype", "fields": {"model": "certificategenerationcoursesetting", "name": "certificate generation course setting", "app_label": "certificates"}}, {"pk": 58, "model": "contenttypes.contenttype", "fields": {"model": "certificatehtmlviewconfiguration", "name": "certificate html view configuration", "app_label": "certificates"}}, {"pk": 113, "model": "contenttypes.contenttype", "fields": {"model": "certificateitem", "name": "certificate item", "app_label": "shoppingcart"}}, {"pk": 52, "model": "contenttypes.contenttype", "fields": {"model": "certificatewhitelist", "name": "certificate whitelist", "app_label": "certificates"}}, {"pk": 72, "model": "contenttypes.contenttype", "fields": {"model": "client", "name": "client", "app_label": "oauth2"}}, {"pk": 26, "model": "contenttypes.contenttype", "fields": {"model": "code", "name": "code", "app_label": "default"}}, {"pk": 4, "model": "contenttypes.contenttype", "fields": {"model": "contenttype", "name": "content type", "app_label": "contenttypes"}}, {"pk": 22, "model": "contenttypes.contenttype", "fields": {"model": "corsmodel", "name": "cors model", "app_label": "corsheaders"}}, {"pk": 124, "model": "contenttypes.contenttype", "fields": {"model": "country", "name": "country", "app_label": "embargo"}}, {"pk": 125, "model": "contenttypes.contenttype", "fields": {"model": "countryaccessrule", "name": "country access rule", "app_label": "embargo"}}, {"pk": 107, "model": "contenttypes.contenttype", "fields": {"model": "coupon", "name": "coupon", "app_label": "shoppingcart"}}, {"pk": 108, "model": "contenttypes.contenttype", "fields": {"model": "couponredemption", "name": "coupon redemption", "app_label": "shoppingcart"}}, {"pk": 46, "model": "contenttypes.contenttype", "fields": {"model": "courseaccessrole", "name": "course access role", "app_label": "student"}}, {"pk": 126, "model": "contenttypes.contenttype", "fields": {"model": "courseaccessrulehistory", "name": "course access rule history", "app_label": "embargo"}}, {"pk": 69, "model": "contenttypes.contenttype", "fields": {"model": "courseauthorization", "name": "course authorization", "app_label": "bulk_email"}}, {"pk": 65, "model": "contenttypes.contenttype", "fields": {"model": "coursecohort", "name": "course cohort", "app_label": "course_groups"}}, {"pk": 64, "model": "contenttypes.contenttype", "fields": {"model": "coursecohortssettings", "name": "course cohorts settings", "app_label": "course_groups"}}, {"pk": 165, "model": "contenttypes.contenttype", "fields": {"model": "coursecontentmilestone", "name": "course content milestone", "app_label": "milestones"}}, {"pk": 168, "model": "contenttypes.contenttype", "fields": {"model": "coursecreator", "name": "course creator", "app_label": "course_creators"}}, {"pk": 66, "model": "contenttypes.contenttype", "fields": {"model": "courseemail", "name": "course email", "app_label": "bulk_email"}}, {"pk": 68, "model": "contenttypes.contenttype", "fields": {"model": "courseemailtemplate", "name": "course email template", "app_label": "bulk_email"}}, {"pk": 44, "model": "contenttypes.contenttype", "fields": {"model": "courseenrollment", "name": "course enrollment", "app_label": "student"}}, {"pk": 45, "model": "contenttypes.contenttype", "fields": {"model": "courseenrollmentallowed", "name": "course enrollment allowed", "app_label": "student"}}, {"pk": 164, "model": "contenttypes.contenttype", "fields": {"model": "coursemilestone", "name": "course milestone", "app_label": "milestones"}}, {"pk": 116, "model": "contenttypes.contenttype", "fields": {"model": "coursemode", "name": "course mode", "app_label": "course_modes"}}, {"pk": 117, "model": "contenttypes.contenttype", "fields": {"model": "coursemodesarchive", "name": "course modes archive", "app_label": "course_modes"}}, {"pk": 110, "model": "contenttypes.contenttype", "fields": {"model": "courseregcodeitem", "name": "course reg code item", "app_label": "shoppingcart"}}, {"pk": 111, "model": "contenttypes.contenttype", "fields": {"model": "courseregcodeitemannotation", "name": "course reg code item annotation", "app_label": "shoppingcart"}}, {"pk": 105, "model": "contenttypes.contenttype", "fields": {"model": "courseregistrationcode", "name": "course registration code", "app_label": "shoppingcart"}}, {"pk": 103, "model": "contenttypes.contenttype", "fields": {"model": "courseregistrationcodeinvoiceitem", "name": "course registration code invoice item", "app_label": "shoppingcart"}}, {"pk": 128, "model": "contenttypes.contenttype", "fields": {"model": "coursererunstate", "name": "course rerun state", "app_label": "course_action_state"}}, {"pk": 60, "model": "contenttypes.contenttype", "fields": {"model": "coursesoftware", "name": "course software", "app_label": "licenses"}}, {"pk": 133, "model": "contenttypes.contenttype", "fields": {"model": "coursestructure", "name": "course structure", "app_label": "course_structures"}}, {"pk": 62, "model": "contenttypes.contenttype", "fields": {"model": "courseusergroup", "name": "course user group", "app_label": "course_groups"}}, {"pk": 63, "model": "contenttypes.contenttype", "fields": {"model": "courseusergrouppartitiongroup", "name": "course user group partition group", "app_label": "course_groups"}}, {"pk": 159, "model": "contenttypes.contenttype", "fields": {"model": "coursevideo", "name": "course video", "app_label": "edxval"}}, {"pk": 139, "model": "contenttypes.contenttype", "fields": {"model": "criterion", "name": "criterion", "app_label": "assessment"}}, {"pk": 140, "model": "contenttypes.contenttype", "fields": {"model": "criterionoption", "name": "criterion option", "app_label": "assessment"}}, {"pk": 10, "model": "contenttypes.contenttype", "fields": {"model": "crontabschedule", "name": "crontab", "app_label": "djcelery"}}, {"pk": 119, "model": "contenttypes.contenttype", "fields": {"model": "darklangconfig", "name": "dark lang config", "app_label": "dark_lang"}}, {"pk": 47, "model": "contenttypes.contenttype", "fields": {"model": "dashboardconfiguration", "name": "dashboard configuration", "app_label": "student"}}, {"pk": 115, "model": "contenttypes.contenttype", "fields": {"model": "donation", "name": "donation", "app_label": "shoppingcart"}}, {"pk": 114, "model": "contenttypes.contenttype", "fields": {"model": "donationconfiguration", "name": "donation configuration", "app_label": "shoppingcart"}}, {"pk": 121, "model": "contenttypes.contenttype", "fields": {"model": "embargoedcourse", "name": "embargoed course", "app_label": "embargo"}}, {"pk": 122, "model": "contenttypes.contenttype", "fields": {"model": "embargoedstate", "name": "embargoed state", "app_label": "embargo"}}, {"pk": 160, "model": "contenttypes.contenttype", "fields": {"model": "encodedvideo", "name": "encoded video", "app_label": "edxval"}}, {"pk": 49, "model": "contenttypes.contenttype", "fields": {"model": "entranceexamconfiguration", "name": "entrance exam configuration", "app_label": "student"}}, {"pk": 55, "model": "contenttypes.contenttype", "fields": {"model": "examplecertificate", "name": "example certificate", "app_label": "certificates"}}, {"pk": 54, "model": "contenttypes.contenttype", "fields": {"model": "examplecertificateset", "name": "example certificate set", "app_label": "certificates"}}, {"pk": 71, "model": "contenttypes.contenttype", "fields": {"model": "externalauthmap", "name": "external auth map", "app_label": "external_auth"}}, {"pk": 53, "model": "contenttypes.contenttype", "fields": {"model": "generatedcertificate", "name": "generated certificate", "app_label": "certificates"}}, {"pk": 73, "model": "contenttypes.contenttype", "fields": {"model": "grant", "name": "grant", "app_label": "oauth2"}}, {"pk": 2, "model": "contenttypes.contenttype", "fields": {"model": "group", "name": "group", "app_label": "auth"}}, {"pk": 59, "model": "contenttypes.contenttype", "fields": {"model": "instructortask", "name": "instructor task", "app_label": "instructor_task"}}, {"pk": 9, "model": "contenttypes.contenttype", "fields": {"model": "intervalschedule", "name": "interval", "app_label": "djcelery"}}, {"pk": 100, "model": "contenttypes.contenttype", "fields": {"model": "invoice", "name": "invoice", "app_label": "shoppingcart"}}, {"pk": 104, "model": "contenttypes.contenttype", "fields": {"model": "invoicehistory", "name": "invoice history", "app_label": "shoppingcart"}}, {"pk": 102, "model": "contenttypes.contenttype", "fields": {"model": "invoiceitem", "name": "invoice item", "app_label": "shoppingcart"}}, {"pk": 101, "model": "contenttypes.contenttype", "fields": {"model": "invoicetransaction", "name": "invoice transaction", "app_label": "shoppingcart"}}, {"pk": 127, "model": "contenttypes.contenttype", "fields": {"model": "ipfilter", "name": "ip filter", "app_label": "embargo"}}, {"pk": 48, "model": "contenttypes.contenttype", "fields": {"model": "linkedinaddtoprofileconfiguration", "name": "linked in add to profile configuration", "app_label": "student"}}, {"pk": 21, "model": "contenttypes.contenttype", "fields": {"model": "logentry", "name": "log entry", "app_label": "admin"}}, {"pk": 43, "model": "contenttypes.contenttype", "fields": {"model": "loginfailures", "name": "login failures", "app_label": "student"}}, {"pk": 120, "model": "contenttypes.contenttype", "fields": {"model": "midcoursereverificationwindow", "name": "midcourse reverification window", "app_label": "reverification"}}, {"pk": 15, "model": "contenttypes.contenttype", "fields": {"model": "migrationhistory", "name": "migration history", "app_label": "south"}}, {"pk": 162, "model": "contenttypes.contenttype", "fields": {"model": "milestone", "name": "milestone", "app_label": "milestones"}}, {"pk": 163, "model": "contenttypes.contenttype", "fields": {"model": "milestonerelationshiptype", "name": "milestone relationship type", "app_label": "milestones"}}, {"pk": 129, "model": "contenttypes.contenttype", "fields": {"model": "mobileapiconfig", "name": "mobile api config", "app_label": "mobile_api"}}, {"pk": 18, "model": "contenttypes.contenttype", "fields": {"model": "nonce", "name": "nonce", "app_label": "django_openid_auth"}}, {"pk": 24, "model": "contenttypes.contenttype", "fields": {"model": "nonce", "name": "nonce", "app_label": "default"}}, {"pk": 93, "model": "contenttypes.contenttype", "fields": {"model": "note", "name": "note", "app_label": "notes"}}, {"pk": 90, "model": "contenttypes.contenttype", "fields": {"model": "notification", "name": "notification", "app_label": "django_notify"}}, {"pk": 32, "model": "contenttypes.contenttype", "fields": {"model": "offlinecomputedgrade", "name": "offline computed grade", "app_label": "courseware"}}, {"pk": 33, "model": "contenttypes.contenttype", "fields": {"model": "offlinecomputedgradelog", "name": "offline computed grade log", "app_label": "courseware"}}, {"pk": 67, "model": "contenttypes.contenttype", "fields": {"model": "optout", "name": "optout", "app_label": "bulk_email"}}, {"pk": 98, "model": "contenttypes.contenttype", "fields": {"model": "order", "name": "order", "app_label": "shoppingcart"}}, {"pk": 99, "model": "contenttypes.contenttype", "fields": {"model": "orderitem", "name": "order item", "app_label": "shoppingcart"}}, {"pk": 109, "model": "contenttypes.contenttype", "fields": {"model": "paidcourseregistration", "name": "paid course registration", "app_label": "shoppingcart"}}, {"pk": 112, "model": "contenttypes.contenttype", "fields": {"model": "paidcourseregistrationannotation", "name": "paid course registration annotation", "app_label": "shoppingcart"}}, {"pk": 42, "model": "contenttypes.contenttype", "fields": {"model": "passwordhistory", "name": "password history", "app_label": "student"}}, {"pk": 145, "model": "contenttypes.contenttype", "fields": {"model": "peerworkflow", "name": "peer workflow", "app_label": "assessment"}}, {"pk": 146, "model": "contenttypes.contenttype", "fields": {"model": "peerworkflowitem", "name": "peer workflow item", "app_label": "assessment"}}, {"pk": 41, "model": "contenttypes.contenttype", "fields": {"model": "pendingemailchange", "name": "pending email change", "app_label": "student"}}, {"pk": 40, "model": "contenttypes.contenttype", "fields": {"model": "pendingnamechange", "name": "pending name change", "app_label": "student"}}, {"pk": 12, "model": "contenttypes.contenttype", "fields": {"model": "periodictask", "name": "periodic task", "app_label": "djcelery"}}, {"pk": 11, "model": "contenttypes.contenttype", "fields": {"model": "periodictasks", "name": "periodic tasks", "app_label": "djcelery"}}, {"pk": 1, "model": "contenttypes.contenttype", "fields": {"model": "permission", "name": "permission", "app_label": "auth"}}, {"pk": 157, "model": "contenttypes.contenttype", "fields": {"model": "profile", "name": "profile", "app_label": "edxval"}}, {"pk": 17, "model": "contenttypes.contenttype", "fields": {"model": "psychometricdata", "name": "psychometric data", "app_label": "psychometrics"}}, {"pk": 92, "model": "contenttypes.contenttype", "fields": {"model": "puzzlecomplete", "name": "puzzle complete", "app_label": "foldit"}}, {"pk": 51, "model": "contenttypes.contenttype", "fields": {"model": "ratelimitconfiguration", "name": "rate limit configuration", "app_label": "util"}}, {"pk": 75, "model": "contenttypes.contenttype", "fields": {"model": "refreshtoken", "name": "refresh token", "app_label": "oauth2"}}, {"pk": 39, "model": "contenttypes.contenttype", "fields": {"model": "registration", "name": "registration", "app_label": "student"}}, {"pk": 106, "model": "contenttypes.contenttype", "fields": {"model": "registrationcoderedemption", "name": "registration code redemption", "app_label": "shoppingcart"}}, {"pk": 123, "model": "contenttypes.contenttype", "fields": {"model": "restrictedcourse", "name": "restricted course", "app_label": "embargo"}}, {"pk": 82, "model": "contenttypes.contenttype", "fields": {"model": "reusableplugin", "name": "reusable plugin", "app_label": "wiki"}}, {"pk": 84, "model": "contenttypes.contenttype", "fields": {"model": "revisionplugin", "name": "revision plugin", "app_label": "wiki"}}, {"pk": 85, "model": "contenttypes.contenttype", "fields": {"model": "revisionpluginrevision", "name": "revision plugin revision", "app_label": "wiki"}}, {"pk": 138, "model": "contenttypes.contenttype", "fields": {"model": "rubric", "name": "rubric", "app_label": "assessment"}}, {"pk": 8, "model": "contenttypes.contenttype", "fields": {"model": "tasksetmeta", "name": "saved group result", "app_label": "djcelery"}}, {"pk": 91, "model": "contenttypes.contenttype", "fields": {"model": "score", "name": "score", "app_label": "foldit"}}, {"pk": 136, "model": "contenttypes.contenttype", "fields": {"model": "score", "name": "score", "app_label": "submissions"}}, {"pk": 137, "model": "contenttypes.contenttype", "fields": {"model": "scoresummary", "name": "score summary", "app_label": "submissions"}}, {"pk": 16, "model": "contenttypes.contenttype", "fields": {"model": "servercircuit", "name": "server circuit", "app_label": "circuit"}}, {"pk": 5, "model": "contenttypes.contenttype", "fields": {"model": "session", "name": "session", "app_label": "sessions"}}, {"pk": 88, "model": "contenttypes.contenttype", "fields": {"model": "settings", "name": "settings", "app_label": "django_notify"}}, {"pk": 83, "model": "contenttypes.contenttype", "fields": {"model": "simpleplugin", "name": "simple plugin", "app_label": "wiki"}}, {"pk": 6, "model": "contenttypes.contenttype", "fields": {"model": "site", "name": "site", "app_label": "sites"}}, {"pk": 118, "model": "contenttypes.contenttype", "fields": {"model": "softwaresecurephotoverification", "name": "software secure photo verification", "app_label": "verify_student"}}, {"pk": 94, "model": "contenttypes.contenttype", "fields": {"model": "splashconfig", "name": "splash config", "app_label": "splash"}}, {"pk": 134, "model": "contenttypes.contenttype", "fields": {"model": "studentitem", "name": "student item", "app_label": "submissions"}}, {"pk": 27, "model": "contenttypes.contenttype", "fields": {"model": "studentmodule", "name": "student module", "app_label": "courseware"}}, {"pk": 28, "model": "contenttypes.contenttype", "fields": {"model": "studentmodulehistory", "name": "student module history", "app_label": "courseware"}}, {"pk": 148, "model": "contenttypes.contenttype", "fields": {"model": "studenttrainingworkflow", "name": "student training workflow", "app_label": "assessment"}}, {"pk": 149, "model": "contenttypes.contenttype", "fields": {"model": "studenttrainingworkflowitem", "name": "student training workflow item", "app_label": "assessment"}}, {"pk": 169, "model": "contenttypes.contenttype", "fields": {"model": "studioconfig", "name": "studio config", "app_label": "xblock_config"}}, {"pk": 135, "model": "contenttypes.contenttype", "fields": {"model": "submission", "name": "submission", "app_label": "submissions"}}, {"pk": 89, "model": "contenttypes.contenttype", "fields": {"model": "subscription", "name": "subscription", "app_label": "django_notify"}}, {"pk": 161, "model": "contenttypes.contenttype", "fields": {"model": "subtitle", "name": "subtitle", "app_label": "edxval"}}, {"pk": 131, "model": "contenttypes.contenttype", "fields": {"model": "surveyanswer", "name": "survey answer", "app_label": "survey"}}, {"pk": 130, "model": "contenttypes.contenttype", "fields": {"model": "surveyform", "name": "survey form", "app_label": "survey"}}, {"pk": 14, "model": "contenttypes.contenttype", "fields": {"model": "taskstate", "name": "task", "app_label": "djcelery"}}, {"pk": 7, "model": "contenttypes.contenttype", "fields": {"model": "taskmeta", "name": "task state", "app_label": "djcelery"}}, {"pk": 50, "model": "contenttypes.contenttype", "fields": {"model": "trackinglog", "name": "tracking log", "app_label": "track"}}, {"pk": 147, "model": "contenttypes.contenttype", "fields": {"model": "trainingexample", "name": "training example", "app_label": "assessment"}}, {"pk": 76, "model": "contenttypes.contenttype", "fields": {"model": "trustedclient", "name": "trusted client", "app_label": "oauth2_provider"}}, {"pk": 87, "model": "contenttypes.contenttype", "fields": {"model": "notificationtype", "name": "type", "app_label": "django_notify"}}, {"pk": 80, "model": "contenttypes.contenttype", "fields": {"model": "urlpath", "name": "URL path", "app_label": "wiki"}}, {"pk": 3, "model": "contenttypes.contenttype", "fields": {"model": "user", "name": "user", "app_label": "auth"}}, {"pk": 96, "model": "contenttypes.contenttype", "fields": {"model": "usercoursetag", "name": "user course tag", "app_label": "user_api"}}, {"pk": 61, "model": "contenttypes.contenttype", "fields": {"model": "userlicense", "name": "user license", "app_label": "licenses"}}, {"pk": 166, "model": "contenttypes.contenttype", "fields": {"model": "usermilestone", "name": "user milestone", "app_label": "milestones"}}, {"pk": 20, "model": "contenttypes.contenttype", "fields": {"model": "useropenid", "name": "user open id", "app_label": "django_openid_auth"}}, {"pk": 97, "model": "contenttypes.contenttype", "fields": {"model": "userorgtag", "name": "user org tag", "app_label": "user_api"}}, {"pk": 95, "model": "contenttypes.contenttype", "fields": {"model": "userpreference", "name": "user preference", "app_label": "user_api"}}, {"pk": 36, "model": "contenttypes.contenttype", "fields": {"model": "userprofile", "name": "user profile", "app_label": "student"}}, {"pk": 37, "model": "contenttypes.contenttype", "fields": {"model": "usersignupsource", "name": "user signup source", "app_label": "student"}}, {"pk": 23, "model": "contenttypes.contenttype", "fields": {"model": "usersocialauth", "name": "user social auth", "app_label": "default"}}, {"pk": 35, "model": "contenttypes.contenttype", "fields": {"model": "userstanding", "name": "user standing", "app_label": "student"}}, {"pk": 38, "model": "contenttypes.contenttype", "fields": {"model": "usertestgroup", "name": "user test group", "app_label": "student"}}, {"pk": 158, "model": "contenttypes.contenttype", "fields": {"model": "video", "name": "video", "app_label": "edxval"}}, {"pk": 167, "model": "contenttypes.contenttype", "fields": {"model": "videouploadconfig", "name": "video upload config", "app_label": "contentstore"}}, {"pk": 13, "model": "contenttypes.contenttype", "fields": {"model": "workerstate", "name": "worker", "app_label": "djcelery"}}, {"pk": 132, "model": "contenttypes.contenttype", "fields": {"model": "xblockasidesconfig", "name": "x block asides config", "app_label": "lms_xblock"}}, {"pk": 31, "model": "contenttypes.contenttype", "fields": {"model": "xmodulestudentinfofield", "name": "x module student info field", "app_label": "courseware"}}, {"pk": 30, "model": "contenttypes.contenttype", "fields": {"model": "xmodulestudentprefsfield", "name": "x module student prefs field", "app_label": "courseware"}}, {"pk": 29, "model": "contenttypes.contenttype", "fields": {"model": "xmoduleuserstatesummaryfield", "name": "x module user state summary field", "app_label": "courseware"}}, {"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}, {"pk": 1, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:39Z", "app_name": "courseware", "migration": "0001_initial"}}, {"pk": 2, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:39Z", "app_name": "courseware", "migration": "0002_add_indexes"}}, {"pk": 3, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:39Z", "app_name": "courseware", "migration": "0003_done_grade_cache"}}, {"pk": 4, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:39Z", "app_name": "courseware", "migration": "0004_add_field_studentmodule_course_id"}}, {"pk": 5, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:39Z", "app_name": "courseware", "migration": "0005_auto__add_offlinecomputedgrade__add_unique_offlinecomputedgrade_user_c"}}, {"pk": 6, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:39Z", "app_name": "courseware", "migration": "0006_create_student_module_history"}}, {"pk": 7, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:39Z", "app_name": "courseware", "migration": "0007_allow_null_version_in_history"}}, {"pk": 8, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:40Z", "app_name": "courseware", "migration": "0008_add_xmodule_storage"}}, {"pk": 9, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:40Z", "app_name": "courseware", "migration": "0009_add_field_default"}}, {"pk": 10, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:40Z", "app_name": "courseware", "migration": "0010_rename_xblock_field_content_to_user_state_summary"}}, {"pk": 11, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:40Z", "app_name": "student", "migration": "0001_initial"}}, {"pk": 12, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:40Z", "app_name": "student", "migration": "0002_text_to_varchar_and_indexes"}}, {"pk": 13, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:40Z", "app_name": "student", "migration": "0003_auto__add_usertestgroup"}}, {"pk": 14, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0004_add_email_index"}}, {"pk": 15, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0005_name_change"}}, {"pk": 16, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0006_expand_meta_field"}}, {"pk": 17, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0007_convert_to_utf8"}}, {"pk": 18, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0008__auto__add_courseregistration"}}, {"pk": 19, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0009_auto__del_courseregistration__add_courseenrollment"}}, {"pk": 20, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0010_auto__chg_field_courseenrollment_course_id"}}, {"pk": 21, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0011_auto__chg_field_courseenrollment_user__del_unique_courseenrollment_use"}}, {"pk": 22, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0012_auto__add_field_userprofile_gender__add_field_userprofile_date_of_birt"}}, {"pk": 23, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0013_auto__chg_field_userprofile_meta"}}, {"pk": 24, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0014_auto__del_courseenrollment"}}, {"pk": 25, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0015_auto__add_courseenrollment__add_unique_courseenrollment_user_course_id"}}, {"pk": 26, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0016_auto__add_field_courseenrollment_date__chg_field_userprofile_country"}}, {"pk": 27, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0017_rename_date_to_created"}}, {"pk": 28, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0018_auto"}}, {"pk": 29, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0019_create_approved_demographic_fields_fall_2012"}}, {"pk": 30, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0020_add_test_center_user"}}, {"pk": 31, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0021_remove_askbot"}}, {"pk": 32, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0022_auto__add_courseenrollmentallowed__add_unique_courseenrollmentallowed_"}}, {"pk": 33, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0023_add_test_center_registration"}}, {"pk": 34, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0024_add_allow_certificate"}}, {"pk": 35, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0025_auto__add_field_courseenrollmentallowed_auto_enroll"}}, {"pk": 36, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0026_auto__remove_index_student_testcenterregistration_accommodation_request"}}, {"pk": 37, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0027_add_active_flag_and_mode_to_courseware_enrollment"}}, {"pk": 38, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0028_auto__add_userstanding"}}, {"pk": 39, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0029_add_lookup_table_between_user_and_anonymous_student_id"}}, {"pk": 40, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0029_remove_pearson"}}, {"pk": 41, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0030_auto__chg_field_anonymoususerid_anonymous_user_id"}}, {"pk": 42, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0031_drop_student_anonymoususerid_temp_archive"}}, {"pk": 43, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0032_add_field_UserProfile_country_add_field_UserProfile_city"}}, {"pk": 44, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:43Z", "app_name": "student", "migration": "0032_auto__add_loginfailures"}}, {"pk": 45, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:43Z", "app_name": "student", "migration": "0033_auto__add_passwordhistory"}}, {"pk": 46, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:43Z", "app_name": "student", "migration": "0034_auto__add_courseaccessrole"}}, {"pk": 47, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:44Z", "app_name": "student", "migration": "0035_access_roles"}}, {"pk": 48, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:44Z", "app_name": "student", "migration": "0036_access_roles_orgless"}}, {"pk": 49, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:44Z", "app_name": "student", "migration": "0037_auto__add_courseregistrationcode"}}, {"pk": 50, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:44Z", "app_name": "student", "migration": "0038_auto__add_usersignupsource"}}, {"pk": 51, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:44Z", "app_name": "student", "migration": "0039_auto__del_courseregistrationcode"}}, {"pk": 52, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:44Z", "app_name": "student", "migration": "0040_auto__del_field_usersignupsource_user_id__add_field_usersignupsource_u"}}, {"pk": 53, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:44Z", "app_name": "student", "migration": "0041_add_dashboard_config"}}, {"pk": 54, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:44Z", "app_name": "student", "migration": "0042_grant_sales_admin_roles"}}, {"pk": 55, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:44Z", "app_name": "student", "migration": "0043_auto__add_linkedinaddtoprofileconfiguration"}}, {"pk": 56, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:44Z", "app_name": "student", "migration": "0044_linkedin_add_company_identifier"}}, {"pk": 57, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:45Z", "app_name": "student", "migration": "0045_add_trk_partner_to_linkedin_config"}}, {"pk": 58, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:45Z", "app_name": "student", "migration": "0046_auto__add_entranceexamconfiguration__add_unique_entranceexamconfigurat"}}, {"pk": 59, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:45Z", "app_name": "track", "migration": "0001_initial"}}, {"pk": 60, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:45Z", "app_name": "track", "migration": "0002_auto__add_field_trackinglog_host__chg_field_trackinglog_event_type__ch"}}, {"pk": 61, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:45Z", "app_name": "util", "migration": "0001_initial"}}, {"pk": 62, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:45Z", "app_name": "util", "migration": "0002_default_rate_limit_config"}}, {"pk": 63, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0001_added_generatedcertificates"}}, {"pk": 64, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0002_auto__add_field_generatedcertificate_download_url"}}, {"pk": 65, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0003_auto__add_field_generatedcertificate_enabled"}}, {"pk": 66, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0004_auto__add_field_generatedcertificate_graded_certificate_id__add_field_"}}, {"pk": 67, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0005_auto__add_field_generatedcertificate_name"}}, {"pk": 68, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0006_auto__chg_field_generatedcertificate_certificate_id"}}, {"pk": 69, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0007_auto__add_revokedcertificate"}}, {"pk": 70, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0008_auto__del_revokedcertificate__del_field_generatedcertificate_name__add"}}, {"pk": 71, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0009_auto__del_field_generatedcertificate_graded_download_url__del_field_ge"}}, {"pk": 72, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0010_auto__del_field_generatedcertificate_enabled__add_field_generatedcerti"}}, {"pk": 73, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0011_auto__del_field_generatedcertificate_certificate_id__add_field_generat"}}, {"pk": 74, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0012_auto__add_field_generatedcertificate_name__add_field_generatedcertific"}}, {"pk": 75, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0013_auto__add_field_generatedcertificate_error_reason"}}, {"pk": 76, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0014_adding_whitelist"}}, {"pk": 77, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:47Z", "app_name": "certificates", "migration": "0015_adding_mode_for_verified_certs"}}, {"pk": 78, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:47Z", "app_name": "certificates", "migration": "0016_change_course_key_fields"}}, {"pk": 79, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:47Z", "app_name": "certificates", "migration": "0017_auto__add_certificategenerationconfiguration"}}, {"pk": 80, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:47Z", "app_name": "certificates", "migration": "0018_add_example_cert_models"}}, {"pk": 81, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:47Z", "app_name": "certificates", "migration": "0019_auto__add_certificatehtmlviewconfiguration"}}, {"pk": 82, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:47Z", "app_name": "certificates", "migration": "0020_certificatehtmlviewconfiguration_data"}}, {"pk": 83, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:47Z", "app_name": "instructor_task", "migration": "0001_initial"}}, {"pk": 84, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:47Z", "app_name": "instructor_task", "migration": "0002_add_subtask_field"}}, {"pk": 85, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:48Z", "app_name": "licenses", "migration": "0001_initial"}}, {"pk": 86, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:48Z", "app_name": "course_groups", "migration": "0001_initial"}}, {"pk": 87, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:48Z", "app_name": "course_groups", "migration": "0002_add_model_CourseUserGroupPartitionGroup"}}, {"pk": 88, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:48Z", "app_name": "course_groups", "migration": "0003_auto__add_coursecohort__add_coursecohortssettings"}}, {"pk": 89, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:48Z", "app_name": "course_groups", "migration": "0004_auto__del_field_coursecohortssettings_cohorted_discussions__add_field_"}}, {"pk": 90, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:49Z", "app_name": "bulk_email", "migration": "0001_initial"}}, {"pk": 91, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:49Z", "app_name": "bulk_email", "migration": "0002_change_field_names"}}, {"pk": 92, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:49Z", "app_name": "bulk_email", "migration": "0003_add_optout_user"}}, {"pk": 93, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:49Z", "app_name": "bulk_email", "migration": "0004_migrate_optout_user"}}, {"pk": 94, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:49Z", "app_name": "bulk_email", "migration": "0005_remove_optout_email"}}, {"pk": 95, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:49Z", "app_name": "bulk_email", "migration": "0006_add_course_email_template"}}, {"pk": 96, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:49Z", "app_name": "bulk_email", "migration": "0007_load_course_email_template"}}, {"pk": 97, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:49Z", "app_name": "bulk_email", "migration": "0008_add_course_authorizations"}}, {"pk": 98, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:49Z", "app_name": "bulk_email", "migration": "0009_force_unique_course_ids"}}, {"pk": 99, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:49Z", "app_name": "bulk_email", "migration": "0010_auto__chg_field_optout_course_id__add_field_courseemail_template_name_"}}, {"pk": 100, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:50Z", "app_name": "branding", "migration": "0001_initial"}}, {"pk": 101, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:50Z", "app_name": "external_auth", "migration": "0001_initial"}}, {"pk": 102, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:50Z", "app_name": "oauth2", "migration": "0001_initial"}}, {"pk": 103, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:50Z", "app_name": "oauth2", "migration": "0002_auto__chg_field_client_user"}}, {"pk": 104, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:51Z", "app_name": "oauth2", "migration": "0003_auto__add_field_client_name"}}, {"pk": 105, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:51Z", "app_name": "oauth2", "migration": "0004_auto__add_index_accesstoken_token"}}, {"pk": 106, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:51Z", "app_name": "oauth2_provider", "migration": "0001_initial"}}, {"pk": 107, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:52Z", "app_name": "wiki", "migration": "0001_initial"}}, {"pk": 108, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:52Z", "app_name": "wiki", "migration": "0002_auto__add_field_articleplugin_created"}}, {"pk": 109, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:52Z", "app_name": "wiki", "migration": "0003_auto__add_field_urlpath_article"}}, {"pk": 110, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:52Z", "app_name": "wiki", "migration": "0004_populate_urlpath__article"}}, {"pk": 111, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:52Z", "app_name": "wiki", "migration": "0005_auto__chg_field_urlpath_article"}}, {"pk": 112, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:52Z", "app_name": "wiki", "migration": "0006_auto__add_attachmentrevision__add_image__add_attachment"}}, {"pk": 113, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:52Z", "app_name": "wiki", "migration": "0007_auto__add_articlesubscription"}}, {"pk": 114, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:52Z", "app_name": "wiki", "migration": "0008_auto__add_simpleplugin__add_revisionpluginrevision__add_imagerevision_"}}, {"pk": 115, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:52Z", "app_name": "wiki", "migration": "0009_auto__add_field_imagerevision_width__add_field_imagerevision_height"}}, {"pk": 116, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:53Z", "app_name": "wiki", "migration": "0010_auto__chg_field_imagerevision_image"}}, {"pk": 117, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:53Z", "app_name": "wiki", "migration": "0011_auto__chg_field_imagerevision_width__chg_field_imagerevision_height"}}, {"pk": 118, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:53Z", "app_name": "django_notify", "migration": "0001_initial"}}, {"pk": 119, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:53Z", "app_name": "notifications", "migration": "0001_initial"}}, {"pk": 120, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:54Z", "app_name": "foldit", "migration": "0001_initial"}}, {"pk": 121, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:54Z", "app_name": "django_comment_client", "migration": "0001_initial"}}, {"pk": 122, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:54Z", "app_name": "django_comment_common", "migration": "0001_initial"}}, {"pk": 123, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:55Z", "app_name": "notes", "migration": "0001_initial"}}, {"pk": 124, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:55Z", "app_name": "splash", "migration": "0001_initial"}}, {"pk": 125, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:55Z", "app_name": "splash", "migration": "0002_auto__add_field_splashconfig_unaffected_url_paths"}}, {"pk": 126, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:56Z", "app_name": "user_api", "migration": "0001_initial"}}, {"pk": 127, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:56Z", "app_name": "user_api", "migration": "0002_auto__add_usercoursetags__add_unique_usercoursetags_user_course_id_key"}}, {"pk": 128, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:56Z", "app_name": "user_api", "migration": "0003_rename_usercoursetags"}}, {"pk": 129, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:56Z", "app_name": "user_api", "migration": "0004_auto__add_userorgtag__add_unique_userorgtag_user_org_key__chg_field_us"}}, {"pk": 130, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:56Z", "app_name": "shoppingcart", "migration": "0001_initial"}}, {"pk": 131, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:56Z", "app_name": "shoppingcart", "migration": "0002_auto__add_field_paidcourseregistration_mode"}}, {"pk": 132, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:56Z", "app_name": "shoppingcart", "migration": "0003_auto__del_field_orderitem_line_cost"}}, {"pk": 133, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:56Z", "app_name": "shoppingcart", "migration": "0004_auto__add_field_orderitem_fulfilled_time"}}, {"pk": 134, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:57Z", "app_name": "shoppingcart", "migration": "0005_auto__add_paidcourseregistrationannotation__add_field_orderitem_report"}}, {"pk": 135, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:57Z", "app_name": "shoppingcart", "migration": "0006_auto__add_field_order_refunded_time__add_field_orderitem_refund_reques"}}, {"pk": 136, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:57Z", "app_name": "shoppingcart", "migration": "0007_auto__add_field_orderitem_service_fee"}}, {"pk": 137, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:57Z", "app_name": "shoppingcart", "migration": "0008_auto__add_coupons__add_couponredemption__chg_field_certificateitem_cou"}}, {"pk": 138, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:57Z", "app_name": "shoppingcart", "migration": "0009_auto__del_coupons__add_courseregistrationcode__add_coupon__chg_field_c"}}, {"pk": 139, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:57Z", "app_name": "shoppingcart", "migration": "0010_auto__add_registrationcoderedemption__del_field_courseregistrationcode"}}, {"pk": 140, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:57Z", "app_name": "shoppingcart", "migration": "0011_auto__add_invoice__add_field_courseregistrationcode_invoice"}}, {"pk": 141, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:58Z", "app_name": "shoppingcart", "migration": "0012_auto__del_field_courseregistrationcode_transaction_group_name__del_fie"}}, {"pk": 142, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:58Z", "app_name": "shoppingcart", "migration": "0013_auto__add_field_invoice_is_valid"}}, {"pk": 143, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:58Z", "app_name": "shoppingcart", "migration": "0014_auto__del_field_invoice_tax_id__add_field_invoice_address_line_1__add_"}}, {"pk": 144, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:58Z", "app_name": "shoppingcart", "migration": "0015_auto__del_field_invoice_purchase_order_number__del_field_invoice_compa"}}, {"pk": 145, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:58Z", "app_name": "shoppingcart", "migration": "0016_auto__del_field_invoice_company_email__del_field_invoice_company_refer"}}, {"pk": 146, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:58Z", "app_name": "shoppingcart", "migration": "0017_auto__add_field_courseregistrationcode_order__chg_field_registrationco"}}, {"pk": 147, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:58Z", "app_name": "shoppingcart", "migration": "0018_auto__add_donation"}}, {"pk": 148, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:58Z", "app_name": "shoppingcart", "migration": "0019_auto__add_donationconfiguration"}}, {"pk": 149, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:58Z", "app_name": "shoppingcart", "migration": "0020_auto__add_courseregcodeitem__add_courseregcodeitemannotation__add_fiel"}}, {"pk": 150, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:58Z", "app_name": "shoppingcart", "migration": "0021_auto__add_field_orderitem_created__add_field_orderitem_modified"}}, {"pk": 151, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:59Z", "app_name": "shoppingcart", "migration": "0022_auto__add_field_registrationcoderedemption_course_enrollment__add_fiel"}}, {"pk": 152, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:59Z", "app_name": "shoppingcart", "migration": "0023_auto__add_field_coupon_expiration_date"}}, {"pk": 153, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:59Z", "app_name": "shoppingcart", "migration": "0024_auto__add_field_courseregistrationcode_mode_slug"}}, {"pk": 154, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:59Z", "app_name": "shoppingcart", "migration": "0025_update_invoice_models"}}, {"pk": 155, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:59Z", "app_name": "shoppingcart", "migration": "0026_migrate_invoices"}}, {"pk": 156, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:59Z", "app_name": "shoppingcart", "migration": "0027_add_invoice_history"}}, {"pk": 157, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "course_modes", "migration": "0001_initial"}}, {"pk": 158, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "course_modes", "migration": "0002_auto__add_field_coursemode_currency"}}, {"pk": 159, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "course_modes", "migration": "0003_auto__add_unique_coursemode_course_id_currency_mode_slug"}}, {"pk": 160, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "course_modes", "migration": "0004_auto__add_field_coursemode_expiration_date"}}, {"pk": 161, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "course_modes", "migration": "0005_auto__add_field_coursemode_expiration_datetime"}}, {"pk": 162, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "course_modes", "migration": "0006_expiration_date_to_datetime"}}, {"pk": 163, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "course_modes", "migration": "0007_add_description"}}, {"pk": 164, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "course_modes", "migration": "0007_auto__add_coursemodesarchive__chg_field_coursemode_course_id"}}, {"pk": 165, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "course_modes", "migration": "0008_auto__del_field_coursemodesarchive_description__add_field_coursemode_s"}}, {"pk": 166, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "verify_student", "migration": "0001_initial"}}, {"pk": 167, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "verify_student", "migration": "0002_auto__add_field_softwaresecurephotoverification_window"}}, {"pk": 168, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "verify_student", "migration": "0003_auto__add_field_softwaresecurephotoverification_display"}}, {"pk": 169, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:01Z", "app_name": "dark_lang", "migration": "0001_initial"}}, {"pk": 170, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:01Z", "app_name": "dark_lang", "migration": "0002_enable_on_install"}}, {"pk": 171, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:01Z", "app_name": "reverification", "migration": "0001_initial"}}, {"pk": 172, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:01Z", "app_name": "embargo", "migration": "0001_initial"}}, {"pk": 173, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:02Z", "app_name": "embargo", "migration": "0002_add_country_access_models"}}, {"pk": 174, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:02Z", "app_name": "embargo", "migration": "0003_add_countries"}}, {"pk": 175, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:02Z", "app_name": "embargo", "migration": "0004_migrate_embargo_config"}}, {"pk": 176, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:02Z", "app_name": "embargo", "migration": "0005_add_courseaccessrulehistory"}}, {"pk": 177, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:03Z", "app_name": "course_action_state", "migration": "0001_initial"}}, {"pk": 178, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:03Z", "app_name": "course_action_state", "migration": "0002_add_rerun_display_name"}}, {"pk": 179, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:03Z", "app_name": "mobile_api", "migration": "0001_initial"}}, {"pk": 180, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:04Z", "app_name": "survey", "migration": "0001_initial"}}, {"pk": 181, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:04Z", "app_name": "lms_xblock", "migration": "0001_initial"}}, {"pk": 182, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:04Z", "app_name": "course_structures", "migration": "0001_initial"}}, {"pk": 183, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:05Z", "app_name": "submissions", "migration": "0001_initial"}}, {"pk": 184, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:05Z", "app_name": "submissions", "migration": "0002_auto__add_scoresummary"}}, {"pk": 185, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:05Z", "app_name": "submissions", "migration": "0003_auto__del_field_submission_answer__add_field_submission_raw_answer"}}, {"pk": 186, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:05Z", "app_name": "submissions", "migration": "0004_auto__add_field_score_reset"}}, {"pk": 187, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:06Z", "app_name": "assessment", "migration": "0001_initial"}}, {"pk": 188, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:06Z", "app_name": "assessment", "migration": "0002_auto__add_assessmentfeedbackoption__del_field_assessmentfeedback_feedb"}}, {"pk": 189, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:06Z", "app_name": "assessment", "migration": "0003_add_index_pw_course_item_student"}}, {"pk": 190, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:06Z", "app_name": "assessment", "migration": "0004_auto__add_field_peerworkflow_graded_count"}}, {"pk": 191, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:06Z", "app_name": "assessment", "migration": "0005_auto__del_field_peerworkflow_graded_count__add_field_peerworkflow_grad"}}, {"pk": 192, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:06Z", "app_name": "assessment", "migration": "0006_auto__add_field_assessmentpart_feedback"}}, {"pk": 193, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:06Z", "app_name": "assessment", "migration": "0007_auto__chg_field_assessmentpart_feedback"}}, {"pk": 194, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:06Z", "app_name": "assessment", "migration": "0008_student_training"}}, {"pk": 195, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:06Z", "app_name": "assessment", "migration": "0009_auto__add_unique_studenttrainingworkflowitem_order_num_workflow"}}, {"pk": 196, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:06Z", "app_name": "assessment", "migration": "0010_auto__add_unique_studenttrainingworkflow_submission_uuid"}}, {"pk": 197, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0011_ai_training"}}, {"pk": 198, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0012_move_algorithm_id_to_classifier_set"}}, {"pk": 199, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0013_auto__add_field_aigradingworkflow_essay_text"}}, {"pk": 200, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0014_auto__add_field_aitrainingworkflow_item_id__add_field_aitrainingworkfl"}}, {"pk": 201, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0015_auto__add_unique_aitrainingworkflow_uuid__add_unique_aigradingworkflow"}}, {"pk": 202, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0016_auto__add_field_aiclassifierset_course_id__add_field_aiclassifierset_i"}}, {"pk": 203, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0016_auto__add_field_rubric_structure_hash"}}, {"pk": 204, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0017_rubric_structure_hash"}}, {"pk": 205, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0018_auto__add_field_assessmentpart_criterion"}}, {"pk": 206, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0019_assessmentpart_criterion_field"}}, {"pk": 207, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0020_assessmentpart_criterion_not_null"}}, {"pk": 208, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0021_assessmentpart_option_nullable"}}, {"pk": 209, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:08Z", "app_name": "assessment", "migration": "0022__add_label_fields"}}, {"pk": 210, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:08Z", "app_name": "assessment", "migration": "0023_assign_criteria_and_option_labels"}}, {"pk": 211, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:08Z", "app_name": "assessment", "migration": "0024_auto__chg_field_assessmentpart_criterion"}}, {"pk": 212, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:08Z", "app_name": "assessment", "migration": "0025_auto__add_field_peerworkflow_cancelled_at"}}, {"pk": 213, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:08Z", "app_name": "workflow", "migration": "0001_initial"}}, {"pk": 214, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:08Z", "app_name": "workflow", "migration": "0002_auto__add_field_assessmentworkflow_course_id__add_field_assessmentwork"}}, {"pk": 215, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:08Z", "app_name": "workflow", "migration": "0003_auto__add_assessmentworkflowstep"}}, {"pk": 216, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:08Z", "app_name": "workflow", "migration": "0004_auto__add_assessmentworkflowcancellation"}}, {"pk": 217, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:09Z", "app_name": "edxval", "migration": "0001_initial"}}, {"pk": 218, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:09Z", "app_name": "edxval", "migration": "0002_default_profiles"}}, {"pk": 219, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:09Z", "app_name": "edxval", "migration": "0003_status_and_created_fields"}}, {"pk": 220, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:09Z", "app_name": "edxval", "migration": "0004_remove_profile_fields"}}, {"pk": 221, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:10Z", "app_name": "milestones", "migration": "0001_initial"}}, {"pk": 222, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:10Z", "app_name": "milestones", "migration": "0002_seed_relationship_types"}}, {"pk": 223, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:10Z", "app_name": "django_extensions", "migration": "0001_empty"}}, {"pk": 224, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:15Z", "app_name": "contentstore", "migration": "0001_initial"}}, {"pk": 225, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:15Z", "app_name": "contentstore", "migration": "0002_auto__del_field_videouploadconfig_status_whitelist"}}, {"pk": 226, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:15Z", "app_name": "course_creators", "migration": "0001_initial"}}, {"pk": 227, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:16Z", "app_name": "xblock_config", "migration": "0001_initial"}}, {"pk": 5, "model": "embargo.country", "fields": {"country": "AD"}}, {"pk": 233, "model": "embargo.country", "fields": {"country": "AE"}}, {"pk": 1, "model": "embargo.country", "fields": {"country": "AF"}}, {"pk": 9, "model": "embargo.country", "fields": {"country": "AG"}}, {"pk": 7, "model": "embargo.country", "fields": {"country": "AI"}}, {"pk": 2, "model": "embargo.country", "fields": {"country": "AL"}}, {"pk": 11, "model": "embargo.country", "fields": {"country": "AM"}}, {"pk": 6, "model": "embargo.country", "fields": {"country": "AO"}}, {"pk": 8, "model": "embargo.country", "fields": {"country": "AQ"}}, {"pk": 10, "model": "embargo.country", "fields": {"country": "AR"}}, {"pk": 4, "model": "embargo.country", "fields": {"country": "AS"}}, {"pk": 14, "model": "embargo.country", "fields": {"country": "AT"}}, {"pk": 13, "model": "embargo.country", "fields": {"country": "AU"}}, {"pk": 12, "model": "embargo.country", "fields": {"country": "AW"}}, {"pk": 249, "model": "embargo.country", "fields": {"country": "AX"}}, {"pk": 15, "model": "embargo.country", "fields": {"country": "AZ"}}, {"pk": 28, "model": "embargo.country", "fields": {"country": "BA"}}, {"pk": 19, "model": "embargo.country", "fields": {"country": "BB"}}, {"pk": 18, "model": "embargo.country", "fields": {"country": "BD"}}, {"pk": 21, "model": "embargo.country", "fields": {"country": "BE"}}, {"pk": 35, "model": "embargo.country", "fields": {"country": "BF"}}, {"pk": 34, "model": "embargo.country", "fields": {"country": "BG"}}, {"pk": 17, "model": "embargo.country", "fields": {"country": "BH"}}, {"pk": 36, "model": "embargo.country", "fields": {"country": "BI"}}, {"pk": 23, "model": "embargo.country", "fields": {"country": "BJ"}}, {"pk": 184, "model": "embargo.country", "fields": {"country": "BL"}}, {"pk": 24, "model": "embargo.country", "fields": {"country": "BM"}}, {"pk": 33, "model": "embargo.country", "fields": {"country": "BN"}}, {"pk": 26, "model": "embargo.country", "fields": {"country": "BO"}}, {"pk": 27, "model": "embargo.country", "fields": {"country": "BQ"}}, {"pk": 31, "model": "embargo.country", "fields": {"country": "BR"}}, {"pk": 16, "model": "embargo.country", "fields": {"country": "BS"}}, {"pk": 25, "model": "embargo.country", "fields": {"country": "BT"}}, {"pk": 30, "model": "embargo.country", "fields": {"country": "BV"}}, {"pk": 29, "model": "embargo.country", "fields": {"country": "BW"}}, {"pk": 20, "model": "embargo.country", "fields": {"country": "BY"}}, {"pk": 22, "model": "embargo.country", "fields": {"country": "BZ"}}, {"pk": 39, "model": "embargo.country", "fields": {"country": "CA"}}, {"pk": 47, "model": "embargo.country", "fields": {"country": "CC"}}, {"pk": 51, "model": "embargo.country", "fields": {"country": "CD"}}, {"pk": 42, "model": "embargo.country", "fields": {"country": "CF"}}, {"pk": 50, "model": "embargo.country", "fields": {"country": "CG"}}, {"pk": 215, "model": "embargo.country", "fields": {"country": "CH"}}, {"pk": 59, "model": "embargo.country", "fields": {"country": "CI"}}, {"pk": 52, "model": "embargo.country", "fields": {"country": "CK"}}, {"pk": 44, "model": "embargo.country", "fields": {"country": "CL"}}, {"pk": 38, "model": "embargo.country", "fields": {"country": "CM"}}, {"pk": 45, "model": "embargo.country", "fields": {"country": "CN"}}, {"pk": 48, "model": "embargo.country", "fields": {"country": "CO"}}, {"pk": 53, "model": "embargo.country", "fields": {"country": "CR"}}, {"pk": 55, "model": "embargo.country", "fields": {"country": "CU"}}, {"pk": 40, "model": "embargo.country", "fields": {"country": "CV"}}, {"pk": 56, "model": "embargo.country", "fields": {"country": "CW"}}, {"pk": 46, "model": "embargo.country", "fields": {"country": "CX"}}, {"pk": 57, "model": "embargo.country", "fields": {"country": "CY"}}, {"pk": 58, "model": "embargo.country", "fields": {"country": "CZ"}}, {"pk": 82, "model": "embargo.country", "fields": {"country": "DE"}}, {"pk": 61, "model": "embargo.country", "fields": {"country": "DJ"}}, {"pk": 60, "model": "embargo.country", "fields": {"country": "DK"}}, {"pk": 62, "model": "embargo.country", "fields": {"country": "DM"}}, {"pk": 63, "model": "embargo.country", "fields": {"country": "DO"}}, {"pk": 3, "model": "embargo.country", "fields": {"country": "DZ"}}, {"pk": 64, "model": "embargo.country", "fields": {"country": "EC"}}, {"pk": 69, "model": "embargo.country", "fields": {"country": "EE"}}, {"pk": 65, "model": "embargo.country", "fields": {"country": "EG"}}, {"pk": 245, "model": "embargo.country", "fields": {"country": "EH"}}, {"pk": 68, "model": "embargo.country", "fields": {"country": "ER"}}, {"pk": 208, "model": "embargo.country", "fields": {"country": "ES"}}, {"pk": 70, "model": "embargo.country", "fields": {"country": "ET"}}, {"pk": 74, "model": "embargo.country", "fields": {"country": "FI"}}, {"pk": 73, "model": "embargo.country", "fields": {"country": "FJ"}}, {"pk": 71, "model": "embargo.country", "fields": {"country": "FK"}}, {"pk": 144, "model": "embargo.country", "fields": {"country": "FM"}}, {"pk": 72, "model": "embargo.country", "fields": {"country": "FO"}}, {"pk": 75, "model": "embargo.country", "fields": {"country": "FR"}}, {"pk": 79, "model": "embargo.country", "fields": {"country": "GA"}}, {"pk": 234, "model": "embargo.country", "fields": {"country": "GB"}}, {"pk": 87, "model": "embargo.country", "fields": {"country": "GD"}}, {"pk": 81, "model": "embargo.country", "fields": {"country": "GE"}}, {"pk": 76, "model": "embargo.country", "fields": {"country": "GF"}}, {"pk": 91, "model": "embargo.country", "fields": {"country": "GG"}}, {"pk": 83, "model": "embargo.country", "fields": {"country": "GH"}}, {"pk": 84, "model": "embargo.country", "fields": {"country": "GI"}}, {"pk": 86, "model": "embargo.country", "fields": {"country": "GL"}}, {"pk": 80, "model": "embargo.country", "fields": {"country": "GM"}}, {"pk": 92, "model": "embargo.country", "fields": {"country": "GN"}}, {"pk": 88, "model": "embargo.country", "fields": {"country": "GP"}}, {"pk": 67, "model": "embargo.country", "fields": {"country": "GQ"}}, {"pk": 85, "model": "embargo.country", "fields": {"country": "GR"}}, {"pk": 206, "model": "embargo.country", "fields": {"country": "GS"}}, {"pk": 90, "model": "embargo.country", "fields": {"country": "GT"}}, {"pk": 89, "model": "embargo.country", "fields": {"country": "GU"}}, {"pk": 93, "model": "embargo.country", "fields": {"country": "GW"}}, {"pk": 94, "model": "embargo.country", "fields": {"country": "GY"}}, {"pk": 99, "model": "embargo.country", "fields": {"country": "HK"}}, {"pk": 96, "model": "embargo.country", "fields": {"country": "HM"}}, {"pk": 98, "model": "embargo.country", "fields": {"country": "HN"}}, {"pk": 54, "model": "embargo.country", "fields": {"country": "HR"}}, {"pk": 95, "model": "embargo.country", "fields": {"country": "HT"}}, {"pk": 100, "model": "embargo.country", "fields": {"country": "HU"}}, {"pk": 103, "model": "embargo.country", "fields": {"country": "ID"}}, {"pk": 106, "model": "embargo.country", "fields": {"country": "IE"}}, {"pk": 108, "model": "embargo.country", "fields": {"country": "IL"}}, {"pk": 107, "model": "embargo.country", "fields": {"country": "IM"}}, {"pk": 102, "model": "embargo.country", "fields": {"country": "IN"}}, {"pk": 32, "model": "embargo.country", "fields": {"country": "IO"}}, {"pk": 105, "model": "embargo.country", "fields": {"country": "IQ"}}, {"pk": 104, "model": "embargo.country", "fields": {"country": "IR"}}, {"pk": 101, "model": "embargo.country", "fields": {"country": "IS"}}, {"pk": 109, "model": "embargo.country", "fields": {"country": "IT"}}, {"pk": 112, "model": "embargo.country", "fields": {"country": "JE"}}, {"pk": 110, "model": "embargo.country", "fields": {"country": "JM"}}, {"pk": 113, "model": "embargo.country", "fields": {"country": "JO"}}, {"pk": 111, "model": "embargo.country", "fields": {"country": "JP"}}, {"pk": 115, "model": "embargo.country", "fields": {"country": "KE"}}, {"pk": 120, "model": "embargo.country", "fields": {"country": "KG"}}, {"pk": 37, "model": "embargo.country", "fields": {"country": "KH"}}, {"pk": 116, "model": "embargo.country", "fields": {"country": "KI"}}, {"pk": 49, "model": "embargo.country", "fields": {"country": "KM"}}, {"pk": 186, "model": "embargo.country", "fields": {"country": "KN"}}, {"pk": 117, "model": "embargo.country", "fields": {"country": "KP"}}, {"pk": 118, "model": "embargo.country", "fields": {"country": "KR"}}, {"pk": 119, "model": "embargo.country", "fields": {"country": "KW"}}, {"pk": 41, "model": "embargo.country", "fields": {"country": "KY"}}, {"pk": 114, "model": "embargo.country", "fields": {"country": "KZ"}}, {"pk": 121, "model": "embargo.country", "fields": {"country": "LA"}}, {"pk": 123, "model": "embargo.country", "fields": {"country": "LB"}}, {"pk": 187, "model": "embargo.country", "fields": {"country": "LC"}}, {"pk": 127, "model": "embargo.country", "fields": {"country": "LI"}}, {"pk": 209, "model": "embargo.country", "fields": {"country": "LK"}}, {"pk": 125, "model": "embargo.country", "fields": {"country": "LR"}}, {"pk": 124, "model": "embargo.country", "fields": {"country": "LS"}}, {"pk": 128, "model": "embargo.country", "fields": {"country": "LT"}}, {"pk": 129, "model": "embargo.country", "fields": {"country": "LU"}}, {"pk": 122, "model": "embargo.country", "fields": {"country": "LV"}}, {"pk": 126, "model": "embargo.country", "fields": {"country": "LY"}}, {"pk": 150, "model": "embargo.country", "fields": {"country": "MA"}}, {"pk": 146, "model": "embargo.country", "fields": {"country": "MC"}}, {"pk": 145, "model": "embargo.country", "fields": {"country": "MD"}}, {"pk": 148, "model": "embargo.country", "fields": {"country": "ME"}}, {"pk": 188, "model": "embargo.country", "fields": {"country": "MF"}}, {"pk": 132, "model": "embargo.country", "fields": {"country": "MG"}}, {"pk": 138, "model": "embargo.country", "fields": {"country": "MH"}}, {"pk": 131, "model": "embargo.country", "fields": {"country": "MK"}}, {"pk": 136, "model": "embargo.country", "fields": {"country": "ML"}}, {"pk": 152, "model": "embargo.country", "fields": {"country": "MM"}}, {"pk": 147, "model": "embargo.country", "fields": {"country": "MN"}}, {"pk": 130, "model": "embargo.country", "fields": {"country": "MO"}}, {"pk": 164, "model": "embargo.country", "fields": {"country": "MP"}}, {"pk": 139, "model": "embargo.country", "fields": {"country": "MQ"}}, {"pk": 140, "model": "embargo.country", "fields": {"country": "MR"}}, {"pk": 149, "model": "embargo.country", "fields": {"country": "MS"}}, {"pk": 137, "model": "embargo.country", "fields": {"country": "MT"}}, {"pk": 141, "model": "embargo.country", "fields": {"country": "MU"}}, {"pk": 135, "model": "embargo.country", "fields": {"country": "MV"}}, {"pk": 133, "model": "embargo.country", "fields": {"country": "MW"}}, {"pk": 143, "model": "embargo.country", "fields": {"country": "MX"}}, {"pk": 134, "model": "embargo.country", "fields": {"country": "MY"}}, {"pk": 151, "model": "embargo.country", "fields": {"country": "MZ"}}, {"pk": 153, "model": "embargo.country", "fields": {"country": "NA"}}, {"pk": 157, "model": "embargo.country", "fields": {"country": "NC"}}, {"pk": 160, "model": "embargo.country", "fields": {"country": "NE"}}, {"pk": 163, "model": "embargo.country", "fields": {"country": "NF"}}, {"pk": 161, "model": "embargo.country", "fields": {"country": "NG"}}, {"pk": 159, "model": "embargo.country", "fields": {"country": "NI"}}, {"pk": 156, "model": "embargo.country", "fields": {"country": "NL"}}, {"pk": 165, "model": "embargo.country", "fields": {"country": "NO"}}, {"pk": 155, "model": "embargo.country", "fields": {"country": "NP"}}, {"pk": 154, "model": "embargo.country", "fields": {"country": "NR"}}, {"pk": 162, "model": "embargo.country", "fields": {"country": "NU"}}, {"pk": 158, "model": "embargo.country", "fields": {"country": "NZ"}}, {"pk": 166, "model": "embargo.country", "fields": {"country": "OM"}}, {"pk": 170, "model": "embargo.country", "fields": {"country": "PA"}}, {"pk": 173, "model": "embargo.country", "fields": {"country": "PE"}}, {"pk": 77, "model": "embargo.country", "fields": {"country": "PF"}}, {"pk": 171, "model": "embargo.country", "fields": {"country": "PG"}}, {"pk": 174, "model": "embargo.country", "fields": {"country": "PH"}}, {"pk": 167, "model": "embargo.country", "fields": {"country": "PK"}}, {"pk": 176, "model": "embargo.country", "fields": {"country": "PL"}}, {"pk": 189, "model": "embargo.country", "fields": {"country": "PM"}}, {"pk": 175, "model": "embargo.country", "fields": {"country": "PN"}}, {"pk": 178, "model": "embargo.country", "fields": {"country": "PR"}}, {"pk": 169, "model": "embargo.country", "fields": {"country": "PS"}}, {"pk": 177, "model": "embargo.country", "fields": {"country": "PT"}}, {"pk": 168, "model": "embargo.country", "fields": {"country": "PW"}}, {"pk": 172, "model": "embargo.country", "fields": {"country": "PY"}}, {"pk": 179, "model": "embargo.country", "fields": {"country": "QA"}}, {"pk": 183, "model": "embargo.country", "fields": {"country": "RE"}}, {"pk": 180, "model": "embargo.country", "fields": {"country": "RO"}}, {"pk": 196, "model": "embargo.country", "fields": {"country": "RS"}}, {"pk": 181, "model": "embargo.country", "fields": {"country": "RU"}}, {"pk": 182, "model": "embargo.country", "fields": {"country": "RW"}}, {"pk": 194, "model": "embargo.country", "fields": {"country": "SA"}}, {"pk": 203, "model": "embargo.country", "fields": {"country": "SB"}}, {"pk": 197, "model": "embargo.country", "fields": {"country": "SC"}}, {"pk": 210, "model": "embargo.country", "fields": {"country": "SD"}}, {"pk": 214, "model": "embargo.country", "fields": {"country": "SE"}}, {"pk": 199, "model": "embargo.country", "fields": {"country": "SG"}}, {"pk": 185, "model": "embargo.country", "fields": {"country": "SH"}}, {"pk": 202, "model": "embargo.country", "fields": {"country": "SI"}}, {"pk": 212, "model": "embargo.country", "fields": {"country": "SJ"}}, {"pk": 201, "model": "embargo.country", "fields": {"country": "SK"}}, {"pk": 198, "model": "embargo.country", "fields": {"country": "SL"}}, {"pk": 192, "model": "embargo.country", "fields": {"country": "SM"}}, {"pk": 195, "model": "embargo.country", "fields": {"country": "SN"}}, {"pk": 204, "model": "embargo.country", "fields": {"country": "SO"}}, {"pk": 211, "model": "embargo.country", "fields": {"country": "SR"}}, {"pk": 207, "model": "embargo.country", "fields": {"country": "SS"}}, {"pk": 193, "model": "embargo.country", "fields": {"country": "ST"}}, {"pk": 66, "model": "embargo.country", "fields": {"country": "SV"}}, {"pk": 200, "model": "embargo.country", "fields": {"country": "SX"}}, {"pk": 216, "model": "embargo.country", "fields": {"country": "SY"}}, {"pk": 213, "model": "embargo.country", "fields": {"country": "SZ"}}, {"pk": 229, "model": "embargo.country", "fields": {"country": "TC"}}, {"pk": 43, "model": "embargo.country", "fields": {"country": "TD"}}, {"pk": 78, "model": "embargo.country", "fields": {"country": "TF"}}, {"pk": 222, "model": "embargo.country", "fields": {"country": "TG"}}, {"pk": 220, "model": "embargo.country", "fields": {"country": "TH"}}, {"pk": 218, "model": "embargo.country", "fields": {"country": "TJ"}}, {"pk": 223, "model": "embargo.country", "fields": {"country": "TK"}}, {"pk": 221, "model": "embargo.country", "fields": {"country": "TL"}}, {"pk": 228, "model": "embargo.country", "fields": {"country": "TM"}}, {"pk": 226, "model": "embargo.country", "fields": {"country": "TN"}}, {"pk": 224, "model": "embargo.country", "fields": {"country": "TO"}}, {"pk": 227, "model": "embargo.country", "fields": {"country": "TR"}}, {"pk": 225, "model": "embargo.country", "fields": {"country": "TT"}}, {"pk": 230, "model": "embargo.country", "fields": {"country": "TV"}}, {"pk": 217, "model": "embargo.country", "fields": {"country": "TW"}}, {"pk": 219, "model": "embargo.country", "fields": {"country": "TZ"}}, {"pk": 232, "model": "embargo.country", "fields": {"country": "UA"}}, {"pk": 231, "model": "embargo.country", "fields": {"country": "UG"}}, {"pk": 236, "model": "embargo.country", "fields": {"country": "UM"}}, {"pk": 235, "model": "embargo.country", "fields": {"country": "US"}}, {"pk": 237, "model": "embargo.country", "fields": {"country": "UY"}}, {"pk": 238, "model": "embargo.country", "fields": {"country": "UZ"}}, {"pk": 97, "model": "embargo.country", "fields": {"country": "VA"}}, {"pk": 190, "model": "embargo.country", "fields": {"country": "VC"}}, {"pk": 240, "model": "embargo.country", "fields": {"country": "VE"}}, {"pk": 242, "model": "embargo.country", "fields": {"country": "VG"}}, {"pk": 243, "model": "embargo.country", "fields": {"country": "VI"}}, {"pk": 241, "model": "embargo.country", "fields": {"country": "VN"}}, {"pk": 239, "model": "embargo.country", "fields": {"country": "VU"}}, {"pk": 244, "model": "embargo.country", "fields": {"country": "WF"}}, {"pk": 191, "model": "embargo.country", "fields": {"country": "WS"}}, {"pk": 246, "model": "embargo.country", "fields": {"country": "YE"}}, {"pk": 142, "model": "embargo.country", "fields": {"country": "YT"}}, {"pk": 205, "model": "embargo.country", "fields": {"country": "ZA"}}, {"pk": 247, "model": "embargo.country", "fields": {"country": "ZM"}}, {"pk": 248, "model": "embargo.country", "fields": {"country": "ZW"}}, {"pk": 1, "model": "edxval.profile", "fields": {"profile_name": "desktop_mp4"}}, {"pk": 2, "model": "edxval.profile", "fields": {"profile_name": "desktop_webm"}}, {"pk": 3, "model": "edxval.profile", "fields": {"profile_name": "mobile_high"}}, {"pk": 4, "model": "edxval.profile", "fields": {"profile_name": "mobile_low"}}, {"pk": 5, "model": "edxval.profile", "fields": {"profile_name": "youtube"}}, {"pk": 1, "model": "milestones.milestonerelationshiptype", "fields": {"active": true, "description": "Autogenerated milestone relationship type \"fulfills\"", "modified": "2015-03-31T06:26:10Z", "name": "fulfills", "created": "2015-03-31T06:26:10Z"}}, {"pk": 2, "model": "milestones.milestonerelationshiptype", "fields": {"active": true, "description": "Autogenerated milestone relationship type \"requires\"", "modified": "2015-03-31T06:26:10Z", "name": "requires", "created": "2015-03-31T06:26:10Z"}}, {"pk": 61, "model": "auth.permission", "fields": {"codename": "add_logentry", "name": "Can add log entry", "content_type": 21}}, {"pk": 62, "model": "auth.permission", "fields": {"codename": "change_logentry", "name": "Can change log entry", "content_type": 21}}, {"pk": 63, "model": "auth.permission", "fields": {"codename": "delete_logentry", "name": "Can delete log entry", "content_type": 21}}, {"pk": 454, "model": "auth.permission", "fields": {"codename": "add_aiclassifier", "name": "Can add ai classifier", "content_type": 151}}, {"pk": 455, "model": "auth.permission", "fields": {"codename": "change_aiclassifier", "name": "Can change ai classifier", "content_type": 151}}, {"pk": 456, "model": "auth.permission", "fields": {"codename": "delete_aiclassifier", "name": "Can delete ai classifier", "content_type": 151}}, {"pk": 451, "model": "auth.permission", "fields": {"codename": "add_aiclassifierset", "name": "Can add ai classifier set", "content_type": 150}}, {"pk": 452, "model": "auth.permission", "fields": {"codename": "change_aiclassifierset", "name": "Can change ai classifier set", "content_type": 150}}, {"pk": 453, "model": "auth.permission", "fields": {"codename": "delete_aiclassifierset", "name": "Can delete ai classifier set", "content_type": 150}}, {"pk": 460, "model": "auth.permission", "fields": {"codename": "add_aigradingworkflow", "name": "Can add ai grading workflow", "content_type": 153}}, {"pk": 461, "model": "auth.permission", "fields": {"codename": "change_aigradingworkflow", "name": "Can change ai grading workflow", "content_type": 153}}, {"pk": 462, "model": "auth.permission", "fields": {"codename": "delete_aigradingworkflow", "name": "Can delete ai grading workflow", "content_type": 153}}, {"pk": 457, "model": "auth.permission", "fields": {"codename": "add_aitrainingworkflow", "name": "Can add ai training workflow", "content_type": 152}}, {"pk": 458, "model": "auth.permission", "fields": {"codename": "change_aitrainingworkflow", "name": "Can change ai training workflow", "content_type": 152}}, {"pk": 459, "model": "auth.permission", "fields": {"codename": "delete_aitrainingworkflow", "name": "Can delete ai training workflow", "content_type": 152}}, {"pk": 424, "model": "auth.permission", "fields": {"codename": "add_assessment", "name": "Can add assessment", "content_type": 141}}, {"pk": 425, "model": "auth.permission", "fields": {"codename": "change_assessment", "name": "Can change assessment", "content_type": 141}}, {"pk": 426, "model": "auth.permission", "fields": {"codename": "delete_assessment", "name": "Can delete assessment", "content_type": 141}}, {"pk": 433, "model": "auth.permission", "fields": {"codename": "add_assessmentfeedback", "name": "Can add assessment feedback", "content_type": 144}}, {"pk": 434, "model": "auth.permission", "fields": {"codename": "change_assessmentfeedback", "name": "Can change assessment feedback", "content_type": 144}}, {"pk": 435, "model": "auth.permission", "fields": {"codename": "delete_assessmentfeedback", "name": "Can delete assessment feedback", "content_type": 144}}, {"pk": 430, "model": "auth.permission", "fields": {"codename": "add_assessmentfeedbackoption", "name": "Can add assessment feedback option", "content_type": 143}}, {"pk": 431, "model": "auth.permission", "fields": {"codename": "change_assessmentfeedbackoption", "name": "Can change assessment feedback option", "content_type": 143}}, {"pk": 432, "model": "auth.permission", "fields": {"codename": "delete_assessmentfeedbackoption", "name": "Can delete assessment feedback option", "content_type": 143}}, {"pk": 427, "model": "auth.permission", "fields": {"codename": "add_assessmentpart", "name": "Can add assessment part", "content_type": 142}}, {"pk": 428, "model": "auth.permission", "fields": {"codename": "change_assessmentpart", "name": "Can change assessment part", "content_type": 142}}, {"pk": 429, "model": "auth.permission", "fields": {"codename": "delete_assessmentpart", "name": "Can delete assessment part", "content_type": 142}}, {"pk": 418, "model": "auth.permission", "fields": {"codename": "add_criterion", "name": "Can add criterion", "content_type": 139}}, {"pk": 419, "model": "auth.permission", "fields": {"codename": "change_criterion", "name": "Can change criterion", "content_type": 139}}, {"pk": 420, "model": "auth.permission", "fields": {"codename": "delete_criterion", "name": "Can delete criterion", "content_type": 139}}, {"pk": 421, "model": "auth.permission", "fields": {"codename": "add_criterionoption", "name": "Can add criterion option", "content_type": 140}}, {"pk": 422, "model": "auth.permission", "fields": {"codename": "change_criterionoption", "name": "Can change criterion option", "content_type": 140}}, {"pk": 423, "model": "auth.permission", "fields": {"codename": "delete_criterionoption", "name": "Can delete criterion option", "content_type": 140}}, {"pk": 436, "model": "auth.permission", "fields": {"codename": "add_peerworkflow", "name": "Can add peer workflow", "content_type": 145}}, {"pk": 437, "model": "auth.permission", "fields": {"codename": "change_peerworkflow", "name": "Can change peer workflow", "content_type": 145}}, {"pk": 438, "model": "auth.permission", "fields": {"codename": "delete_peerworkflow", "name": "Can delete peer workflow", "content_type": 145}}, {"pk": 439, "model": "auth.permission", "fields": {"codename": "add_peerworkflowitem", "name": "Can add peer workflow item", "content_type": 146}}, {"pk": 440, "model": "auth.permission", "fields": {"codename": "change_peerworkflowitem", "name": "Can change peer workflow item", "content_type": 146}}, {"pk": 441, "model": "auth.permission", "fields": {"codename": "delete_peerworkflowitem", "name": "Can delete peer workflow item", "content_type": 146}}, {"pk": 415, "model": "auth.permission", "fields": {"codename": "add_rubric", "name": "Can add rubric", "content_type": 138}}, {"pk": 416, "model": "auth.permission", "fields": {"codename": "change_rubric", "name": "Can change rubric", "content_type": 138}}, {"pk": 417, "model": "auth.permission", "fields": {"codename": "delete_rubric", "name": "Can delete rubric", "content_type": 138}}, {"pk": 445, "model": "auth.permission", "fields": {"codename": "add_studenttrainingworkflow", "name": "Can add student training workflow", "content_type": 148}}, {"pk": 446, "model": "auth.permission", "fields": {"codename": "change_studenttrainingworkflow", "name": "Can change student training workflow", "content_type": 148}}, {"pk": 447, "model": "auth.permission", "fields": {"codename": "delete_studenttrainingworkflow", "name": "Can delete student training workflow", "content_type": 148}}, {"pk": 448, "model": "auth.permission", "fields": {"codename": "add_studenttrainingworkflowitem", "name": "Can add student training workflow item", "content_type": 149}}, {"pk": 449, "model": "auth.permission", "fields": {"codename": "change_studenttrainingworkflowitem", "name": "Can change student training workflow item", "content_type": 149}}, {"pk": 450, "model": "auth.permission", "fields": {"codename": "delete_studenttrainingworkflowitem", "name": "Can delete student training workflow item", "content_type": 149}}, {"pk": 442, "model": "auth.permission", "fields": {"codename": "add_trainingexample", "name": "Can add training example", "content_type": 147}}, {"pk": 443, "model": "auth.permission", "fields": {"codename": "change_trainingexample", "name": "Can change training example", "content_type": 147}}, {"pk": 444, "model": "auth.permission", "fields": {"codename": "delete_trainingexample", "name": "Can delete training example", "content_type": 147}}, {"pk": 4, "model": "auth.permission", "fields": {"codename": "add_group", "name": "Can add group", "content_type": 2}}, {"pk": 5, "model": "auth.permission", "fields": {"codename": "change_group", "name": "Can change group", "content_type": 2}}, {"pk": 6, "model": "auth.permission", "fields": {"codename": "delete_group", "name": "Can delete group", "content_type": 2}}, {"pk": 1, "model": "auth.permission", "fields": {"codename": "add_permission", "name": "Can add permission", "content_type": 1}}, {"pk": 2, "model": "auth.permission", "fields": {"codename": "change_permission", "name": "Can change permission", "content_type": 1}}, {"pk": 3, "model": "auth.permission", "fields": {"codename": "delete_permission", "name": "Can delete permission", "content_type": 1}}, {"pk": 7, "model": "auth.permission", "fields": {"codename": "add_user", "name": "Can add user", "content_type": 3}}, {"pk": 8, "model": "auth.permission", "fields": {"codename": "change_user", "name": "Can change user", "content_type": 3}}, {"pk": 9, "model": "auth.permission", "fields": {"codename": "delete_user", "name": "Can delete user", "content_type": 3}}, {"pk": 208, "model": "auth.permission", "fields": {"codename": "add_brandinginfoconfig", "name": "Can add branding info config", "content_type": 70}}, {"pk": 209, "model": "auth.permission", "fields": {"codename": "change_brandinginfoconfig", "name": "Can change branding info config", "content_type": 70}}, {"pk": 210, "model": "auth.permission", "fields": {"codename": "delete_brandinginfoconfig", "name": "Can delete branding info config", "content_type": 70}}, {"pk": 205, "model": "auth.permission", "fields": {"codename": "add_courseauthorization", "name": "Can add course authorization", "content_type": 69}}, {"pk": 206, "model": "auth.permission", "fields": {"codename": "change_courseauthorization", "name": "Can change course authorization", "content_type": 69}}, {"pk": 207, "model": "auth.permission", "fields": {"codename": "delete_courseauthorization", "name": "Can delete course authorization", "content_type": 69}}, {"pk": 196, "model": "auth.permission", "fields": {"codename": "add_courseemail", "name": "Can add course email", "content_type": 66}}, {"pk": 197, "model": "auth.permission", "fields": {"codename": "change_courseemail", "name": "Can change course email", "content_type": 66}}, {"pk": 198, "model": "auth.permission", "fields": {"codename": "delete_courseemail", "name": "Can delete course email", "content_type": 66}}, {"pk": 202, "model": "auth.permission", "fields": {"codename": "add_courseemailtemplate", "name": "Can add course email template", "content_type": 68}}, {"pk": 203, "model": "auth.permission", "fields": {"codename": "change_courseemailtemplate", "name": "Can change course email template", "content_type": 68}}, {"pk": 204, "model": "auth.permission", "fields": {"codename": "delete_courseemailtemplate", "name": "Can delete course email template", "content_type": 68}}, {"pk": 199, "model": "auth.permission", "fields": {"codename": "add_optout", "name": "Can add optout", "content_type": 67}}, {"pk": 200, "model": "auth.permission", "fields": {"codename": "change_optout", "name": "Can change optout", "content_type": 67}}, {"pk": 201, "model": "auth.permission", "fields": {"codename": "delete_optout", "name": "Can delete optout", "content_type": 67}}, {"pk": 169, "model": "auth.permission", "fields": {"codename": "add_certificategenerationconfiguration", "name": "Can add certificate generation configuration", "content_type": 57}}, {"pk": 170, "model": "auth.permission", "fields": {"codename": "change_certificategenerationconfiguration", "name": "Can change certificate generation configuration", "content_type": 57}}, {"pk": 171, "model": "auth.permission", "fields": {"codename": "delete_certificategenerationconfiguration", "name": "Can delete certificate generation configuration", "content_type": 57}}, {"pk": 166, "model": "auth.permission", "fields": {"codename": "add_certificategenerationcoursesetting", "name": "Can add certificate generation course setting", "content_type": 56}}, {"pk": 167, "model": "auth.permission", "fields": {"codename": "change_certificategenerationcoursesetting", "name": "Can change certificate generation course setting", "content_type": 56}}, {"pk": 168, "model": "auth.permission", "fields": {"codename": "delete_certificategenerationcoursesetting", "name": "Can delete certificate generation course setting", "content_type": 56}}, {"pk": 172, "model": "auth.permission", "fields": {"codename": "add_certificatehtmlviewconfiguration", "name": "Can add certificate html view configuration", "content_type": 58}}, {"pk": 173, "model": "auth.permission", "fields": {"codename": "change_certificatehtmlviewconfiguration", "name": "Can change certificate html view configuration", "content_type": 58}}, {"pk": 174, "model": "auth.permission", "fields": {"codename": "delete_certificatehtmlviewconfiguration", "name": "Can delete certificate html view configuration", "content_type": 58}}, {"pk": 154, "model": "auth.permission", "fields": {"codename": "add_certificatewhitelist", "name": "Can add certificate whitelist", "content_type": 52}}, {"pk": 155, "model": "auth.permission", "fields": {"codename": "change_certificatewhitelist", "name": "Can change certificate whitelist", "content_type": 52}}, {"pk": 156, "model": "auth.permission", "fields": {"codename": "delete_certificatewhitelist", "name": "Can delete certificate whitelist", "content_type": 52}}, {"pk": 163, "model": "auth.permission", "fields": {"codename": "add_examplecertificate", "name": "Can add example certificate", "content_type": 55}}, {"pk": 164, "model": "auth.permission", "fields": {"codename": "change_examplecertificate", "name": "Can change example certificate", "content_type": 55}}, {"pk": 165, "model": "auth.permission", "fields": {"codename": "delete_examplecertificate", "name": "Can delete example certificate", "content_type": 55}}, {"pk": 160, "model": "auth.permission", "fields": {"codename": "add_examplecertificateset", "name": "Can add example certificate set", "content_type": 54}}, {"pk": 161, "model": "auth.permission", "fields": {"codename": "change_examplecertificateset", "name": "Can change example certificate set", "content_type": 54}}, {"pk": 162, "model": "auth.permission", "fields": {"codename": "delete_examplecertificateset", "name": "Can delete example certificate set", "content_type": 54}}, {"pk": 157, "model": "auth.permission", "fields": {"codename": "add_generatedcertificate", "name": "Can add generated certificate", "content_type": 53}}, {"pk": 158, "model": "auth.permission", "fields": {"codename": "change_generatedcertificate", "name": "Can change generated certificate", "content_type": 53}}, {"pk": 159, "model": "auth.permission", "fields": {"codename": "delete_generatedcertificate", "name": "Can delete generated certificate", "content_type": 53}}, {"pk": 46, "model": "auth.permission", "fields": {"codename": "add_servercircuit", "name": "Can add server circuit", "content_type": 16}}, {"pk": 47, "model": "auth.permission", "fields": {"codename": "change_servercircuit", "name": "Can change server circuit", "content_type": 16}}, {"pk": 48, "model": "auth.permission", "fields": {"codename": "delete_servercircuit", "name": "Can delete server circuit", "content_type": 16}}, {"pk": 502, "model": "auth.permission", "fields": {"codename": "add_videouploadconfig", "name": "Can add video upload config", "content_type": 167}}, {"pk": 503, "model": "auth.permission", "fields": {"codename": "change_videouploadconfig", "name": "Can change video upload config", "content_type": 167}}, {"pk": 504, "model": "auth.permission", "fields": {"codename": "delete_videouploadconfig", "name": "Can delete video upload config", "content_type": 167}}, {"pk": 10, "model": "auth.permission", "fields": {"codename": "add_contenttype", "name": "Can add content type", "content_type": 4}}, {"pk": 11, "model": "auth.permission", "fields": {"codename": "change_contenttype", "name": "Can change content type", "content_type": 4}}, {"pk": 12, "model": "auth.permission", "fields": {"codename": "delete_contenttype", "name": "Can delete content type", "content_type": 4}}, {"pk": 64, "model": "auth.permission", "fields": {"codename": "add_corsmodel", "name": "Can add cors model", "content_type": 22}}, {"pk": 65, "model": "auth.permission", "fields": {"codename": "change_corsmodel", "name": "Can change cors model", "content_type": 22}}, {"pk": 66, "model": "auth.permission", "fields": {"codename": "delete_corsmodel", "name": "Can delete cors model", "content_type": 22}}, {"pk": 94, "model": "auth.permission", "fields": {"codename": "add_offlinecomputedgrade", "name": "Can add offline computed grade", "content_type": 32}}, {"pk": 95, "model": "auth.permission", "fields": {"codename": "change_offlinecomputedgrade", "name": "Can change offline computed grade", "content_type": 32}}, {"pk": 96, "model": "auth.permission", "fields": {"codename": "delete_offlinecomputedgrade", "name": "Can delete offline computed grade", "content_type": 32}}, {"pk": 97, "model": "auth.permission", "fields": {"codename": "add_offlinecomputedgradelog", "name": "Can add offline computed grade log", "content_type": 33}}, {"pk": 98, "model": "auth.permission", "fields": {"codename": "change_offlinecomputedgradelog", "name": "Can change offline computed grade log", "content_type": 33}}, {"pk": 99, "model": "auth.permission", "fields": {"codename": "delete_offlinecomputedgradelog", "name": "Can delete offline computed grade log", "content_type": 33}}, {"pk": 79, "model": "auth.permission", "fields": {"codename": "add_studentmodule", "name": "Can add student module", "content_type": 27}}, {"pk": 80, "model": "auth.permission", "fields": {"codename": "change_studentmodule", "name": "Can change student module", "content_type": 27}}, {"pk": 81, "model": "auth.permission", "fields": {"codename": "delete_studentmodule", "name": "Can delete student module", "content_type": 27}}, {"pk": 82, "model": "auth.permission", "fields": {"codename": "add_studentmodulehistory", "name": "Can add student module history", "content_type": 28}}, {"pk": 83, "model": "auth.permission", "fields": {"codename": "change_studentmodulehistory", "name": "Can change student module history", "content_type": 28}}, {"pk": 84, "model": "auth.permission", "fields": {"codename": "delete_studentmodulehistory", "name": "Can delete student module history", "content_type": 28}}, {"pk": 91, "model": "auth.permission", "fields": {"codename": "add_xmodulestudentinfofield", "name": "Can add x module student info field", "content_type": 31}}, {"pk": 92, "model": "auth.permission", "fields": {"codename": "change_xmodulestudentinfofield", "name": "Can change x module student info field", "content_type": 31}}, {"pk": 93, "model": "auth.permission", "fields": {"codename": "delete_xmodulestudentinfofield", "name": "Can delete x module student info field", "content_type": 31}}, {"pk": 88, "model": "auth.permission", "fields": {"codename": "add_xmodulestudentprefsfield", "name": "Can add x module student prefs field", "content_type": 30}}, {"pk": 89, "model": "auth.permission", "fields": {"codename": "change_xmodulestudentprefsfield", "name": "Can change x module student prefs field", "content_type": 30}}, {"pk": 90, "model": "auth.permission", "fields": {"codename": "delete_xmodulestudentprefsfield", "name": "Can delete x module student prefs field", "content_type": 30}}, {"pk": 85, "model": "auth.permission", "fields": {"codename": "add_xmoduleuserstatesummaryfield", "name": "Can add x module user state summary field", "content_type": 29}}, {"pk": 86, "model": "auth.permission", "fields": {"codename": "change_xmoduleuserstatesummaryfield", "name": "Can change x module user state summary field", "content_type": 29}}, {"pk": 87, "model": "auth.permission", "fields": {"codename": "delete_xmoduleuserstatesummaryfield", "name": "Can delete x module user state summary field", "content_type": 29}}, {"pk": 385, "model": "auth.permission", "fields": {"codename": "add_coursererunstate", "name": "Can add course rerun state", "content_type": 128}}, {"pk": 386, "model": "auth.permission", "fields": {"codename": "change_coursererunstate", "name": "Can change course rerun state", "content_type": 128}}, {"pk": 387, "model": "auth.permission", "fields": {"codename": "delete_coursererunstate", "name": "Can delete course rerun state", "content_type": 128}}, {"pk": 505, "model": "auth.permission", "fields": {"codename": "add_coursecreator", "name": "Can add course creator", "content_type": 168}}, {"pk": 506, "model": "auth.permission", "fields": {"codename": "change_coursecreator", "name": "Can change course creator", "content_type": 168}}, {"pk": 507, "model": "auth.permission", "fields": {"codename": "delete_coursecreator", "name": "Can delete course creator", "content_type": 168}}, {"pk": 193, "model": "auth.permission", "fields": {"codename": "add_coursecohort", "name": "Can add course cohort", "content_type": 65}}, {"pk": 194, "model": "auth.permission", "fields": {"codename": "change_coursecohort", "name": "Can change course cohort", "content_type": 65}}, {"pk": 195, "model": "auth.permission", "fields": {"codename": "delete_coursecohort", "name": "Can delete course cohort", "content_type": 65}}, {"pk": 190, "model": "auth.permission", "fields": {"codename": "add_coursecohortssettings", "name": "Can add course cohorts settings", "content_type": 64}}, {"pk": 191, "model": "auth.permission", "fields": {"codename": "change_coursecohortssettings", "name": "Can change course cohorts settings", "content_type": 64}}, {"pk": 192, "model": "auth.permission", "fields": {"codename": "delete_coursecohortssettings", "name": "Can delete course cohorts settings", "content_type": 64}}, {"pk": 184, "model": "auth.permission", "fields": {"codename": "add_courseusergroup", "name": "Can add course user group", "content_type": 62}}, {"pk": 185, "model": "auth.permission", "fields": {"codename": "change_courseusergroup", "name": "Can change course user group", "content_type": 62}}, {"pk": 186, "model": "auth.permission", "fields": {"codename": "delete_courseusergroup", "name": "Can delete course user group", "content_type": 62}}, {"pk": 187, "model": "auth.permission", "fields": {"codename": "add_courseusergrouppartitiongroup", "name": "Can add course user group partition group", "content_type": 63}}, {"pk": 188, "model": "auth.permission", "fields": {"codename": "change_courseusergrouppartitiongroup", "name": "Can change course user group partition group", "content_type": 63}}, {"pk": 189, "model": "auth.permission", "fields": {"codename": "delete_courseusergrouppartitiongroup", "name": "Can delete course user group partition group", "content_type": 63}}, {"pk": 349, "model": "auth.permission", "fields": {"codename": "add_coursemode", "name": "Can add course mode", "content_type": 116}}, {"pk": 350, "model": "auth.permission", "fields": {"codename": "change_coursemode", "name": "Can change course mode", "content_type": 116}}, {"pk": 351, "model": "auth.permission", "fields": {"codename": "delete_coursemode", "name": "Can delete course mode", "content_type": 116}}, {"pk": 352, "model": "auth.permission", "fields": {"codename": "add_coursemodesarchive", "name": "Can add course modes archive", "content_type": 117}}, {"pk": 353, "model": "auth.permission", "fields": {"codename": "change_coursemodesarchive", "name": "Can change course modes archive", "content_type": 117}}, {"pk": 354, "model": "auth.permission", "fields": {"codename": "delete_coursemodesarchive", "name": "Can delete course modes archive", "content_type": 117}}, {"pk": 400, "model": "auth.permission", "fields": {"codename": "add_coursestructure", "name": "Can add course structure", "content_type": 133}}, {"pk": 401, "model": "auth.permission", "fields": {"codename": "change_coursestructure", "name": "Can change course structure", "content_type": 133}}, {"pk": 402, "model": "auth.permission", "fields": {"codename": "delete_coursestructure", "name": "Can delete course structure", "content_type": 133}}, {"pk": 358, "model": "auth.permission", "fields": {"codename": "add_darklangconfig", "name": "Can add dark lang config", "content_type": 119}}, {"pk": 359, "model": "auth.permission", "fields": {"codename": "change_darklangconfig", "name": "Can change dark lang config", "content_type": 119}}, {"pk": 360, "model": "auth.permission", "fields": {"codename": "delete_darklangconfig", "name": "Can delete dark lang config", "content_type": 119}}, {"pk": 73, "model": "auth.permission", "fields": {"codename": "add_association", "name": "Can add association", "content_type": 25}}, {"pk": 74, "model": "auth.permission", "fields": {"codename": "change_association", "name": "Can change association", "content_type": 25}}, {"pk": 75, "model": "auth.permission", "fields": {"codename": "delete_association", "name": "Can delete association", "content_type": 25}}, {"pk": 76, "model": "auth.permission", "fields": {"codename": "add_code", "name": "Can add code", "content_type": 26}}, {"pk": 77, "model": "auth.permission", "fields": {"codename": "change_code", "name": "Can change code", "content_type": 26}}, {"pk": 78, "model": "auth.permission", "fields": {"codename": "delete_code", "name": "Can delete code", "content_type": 26}}, {"pk": 70, "model": "auth.permission", "fields": {"codename": "add_nonce", "name": "Can add nonce", "content_type": 24}}, {"pk": 71, "model": "auth.permission", "fields": {"codename": "change_nonce", "name": "Can change nonce", "content_type": 24}}, {"pk": 72, "model": "auth.permission", "fields": {"codename": "delete_nonce", "name": "Can delete nonce", "content_type": 24}}, {"pk": 67, "model": "auth.permission", "fields": {"codename": "add_usersocialauth", "name": "Can add user social auth", "content_type": 23}}, {"pk": 68, "model": "auth.permission", "fields": {"codename": "change_usersocialauth", "name": "Can change user social auth", "content_type": 23}}, {"pk": 69, "model": "auth.permission", "fields": {"codename": "delete_usersocialauth", "name": "Can delete user social auth", "content_type": 23}}, {"pk": 271, "model": "auth.permission", "fields": {"codename": "add_notification", "name": "Can add notification", "content_type": 90}}, {"pk": 272, "model": "auth.permission", "fields": {"codename": "change_notification", "name": "Can change notification", "content_type": 90}}, {"pk": 273, "model": "auth.permission", "fields": {"codename": "delete_notification", "name": "Can delete notification", "content_type": 90}}, {"pk": 262, "model": "auth.permission", "fields": {"codename": "add_notificationtype", "name": "Can add type", "content_type": 87}}, {"pk": 263, "model": "auth.permission", "fields": {"codename": "change_notificationtype", "name": "Can change type", "content_type": 87}}, {"pk": 264, "model": "auth.permission", "fields": {"codename": "delete_notificationtype", "name": "Can delete type", "content_type": 87}}, {"pk": 265, "model": "auth.permission", "fields": {"codename": "add_settings", "name": "Can add settings", "content_type": 88}}, {"pk": 266, "model": "auth.permission", "fields": {"codename": "change_settings", "name": "Can change settings", "content_type": 88}}, {"pk": 267, "model": "auth.permission", "fields": {"codename": "delete_settings", "name": "Can delete settings", "content_type": 88}}, {"pk": 268, "model": "auth.permission", "fields": {"codename": "add_subscription", "name": "Can add subscription", "content_type": 89}}, {"pk": 269, "model": "auth.permission", "fields": {"codename": "change_subscription", "name": "Can change subscription", "content_type": 89}}, {"pk": 270, "model": "auth.permission", "fields": {"codename": "delete_subscription", "name": "Can delete subscription", "content_type": 89}}, {"pk": 55, "model": "auth.permission", "fields": {"codename": "add_association", "name": "Can add association", "content_type": 19}}, {"pk": 56, "model": "auth.permission", "fields": {"codename": "change_association", "name": "Can change association", "content_type": 19}}, {"pk": 57, "model": "auth.permission", "fields": {"codename": "delete_association", "name": "Can delete association", "content_type": 19}}, {"pk": 52, "model": "auth.permission", "fields": {"codename": "add_nonce", "name": "Can add nonce", "content_type": 18}}, {"pk": 53, "model": "auth.permission", "fields": {"codename": "change_nonce", "name": "Can change nonce", "content_type": 18}}, {"pk": 54, "model": "auth.permission", "fields": {"codename": "delete_nonce", "name": "Can delete nonce", "content_type": 18}}, {"pk": 58, "model": "auth.permission", "fields": {"codename": "add_useropenid", "name": "Can add user open id", "content_type": 20}}, {"pk": 59, "model": "auth.permission", "fields": {"codename": "change_useropenid", "name": "Can change user open id", "content_type": 20}}, {"pk": 60, "model": "auth.permission", "fields": {"codename": "delete_useropenid", "name": "Can delete user open id", "content_type": 20}}, {"pk": 28, "model": "auth.permission", "fields": {"codename": "add_crontabschedule", "name": "Can add crontab", "content_type": 10}}, {"pk": 29, "model": "auth.permission", "fields": {"codename": "change_crontabschedule", "name": "Can change crontab", "content_type": 10}}, {"pk": 30, "model": "auth.permission", "fields": {"codename": "delete_crontabschedule", "name": "Can delete crontab", "content_type": 10}}, {"pk": 25, "model": "auth.permission", "fields": {"codename": "add_intervalschedule", "name": "Can add interval", "content_type": 9}}, {"pk": 26, "model": "auth.permission", "fields": {"codename": "change_intervalschedule", "name": "Can change interval", "content_type": 9}}, {"pk": 27, "model": "auth.permission", "fields": {"codename": "delete_intervalschedule", "name": "Can delete interval", "content_type": 9}}, {"pk": 34, "model": "auth.permission", "fields": {"codename": "add_periodictask", "name": "Can add periodic task", "content_type": 12}}, {"pk": 35, "model": "auth.permission", "fields": {"codename": "change_periodictask", "name": "Can change periodic task", "content_type": 12}}, {"pk": 36, "model": "auth.permission", "fields": {"codename": "delete_periodictask", "name": "Can delete periodic task", "content_type": 12}}, {"pk": 31, "model": "auth.permission", "fields": {"codename": "add_periodictasks", "name": "Can add periodic tasks", "content_type": 11}}, {"pk": 32, "model": "auth.permission", "fields": {"codename": "change_periodictasks", "name": "Can change periodic tasks", "content_type": 11}}, {"pk": 33, "model": "auth.permission", "fields": {"codename": "delete_periodictasks", "name": "Can delete periodic tasks", "content_type": 11}}, {"pk": 19, "model": "auth.permission", "fields": {"codename": "add_taskmeta", "name": "Can add task state", "content_type": 7}}, {"pk": 20, "model": "auth.permission", "fields": {"codename": "change_taskmeta", "name": "Can change task state", "content_type": 7}}, {"pk": 21, "model": "auth.permission", "fields": {"codename": "delete_taskmeta", "name": "Can delete task state", "content_type": 7}}, {"pk": 22, "model": "auth.permission", "fields": {"codename": "add_tasksetmeta", "name": "Can add saved group result", "content_type": 8}}, {"pk": 23, "model": "auth.permission", "fields": {"codename": "change_tasksetmeta", "name": "Can change saved group result", "content_type": 8}}, {"pk": 24, "model": "auth.permission", "fields": {"codename": "delete_tasksetmeta", "name": "Can delete saved group result", "content_type": 8}}, {"pk": 40, "model": "auth.permission", "fields": {"codename": "add_taskstate", "name": "Can add task", "content_type": 14}}, {"pk": 41, "model": "auth.permission", "fields": {"codename": "change_taskstate", "name": "Can change task", "content_type": 14}}, {"pk": 42, "model": "auth.permission", "fields": {"codename": "delete_taskstate", "name": "Can delete task", "content_type": 14}}, {"pk": 37, "model": "auth.permission", "fields": {"codename": "add_workerstate", "name": "Can add worker", "content_type": 13}}, {"pk": 38, "model": "auth.permission", "fields": {"codename": "change_workerstate", "name": "Can change worker", "content_type": 13}}, {"pk": 39, "model": "auth.permission", "fields": {"codename": "delete_workerstate", "name": "Can delete worker", "content_type": 13}}, {"pk": 478, "model": "auth.permission", "fields": {"codename": "add_coursevideo", "name": "Can add course video", "content_type": 159}}, {"pk": 479, "model": "auth.permission", "fields": {"codename": "change_coursevideo", "name": "Can change course video", "content_type": 159}}, {"pk": 480, "model": "auth.permission", "fields": {"codename": "delete_coursevideo", "name": "Can delete course video", "content_type": 159}}, {"pk": 481, "model": "auth.permission", "fields": {"codename": "add_encodedvideo", "name": "Can add encoded video", "content_type": 160}}, {"pk": 482, "model": "auth.permission", "fields": {"codename": "change_encodedvideo", "name": "Can change encoded video", "content_type": 160}}, {"pk": 483, "model": "auth.permission", "fields": {"codename": "delete_encodedvideo", "name": "Can delete encoded video", "content_type": 160}}, {"pk": 472, "model": "auth.permission", "fields": {"codename": "add_profile", "name": "Can add profile", "content_type": 157}}, {"pk": 473, "model": "auth.permission", "fields": {"codename": "change_profile", "name": "Can change profile", "content_type": 157}}, {"pk": 474, "model": "auth.permission", "fields": {"codename": "delete_profile", "name": "Can delete profile", "content_type": 157}}, {"pk": 484, "model": "auth.permission", "fields": {"codename": "add_subtitle", "name": "Can add subtitle", "content_type": 161}}, {"pk": 485, "model": "auth.permission", "fields": {"codename": "change_subtitle", "name": "Can change subtitle", "content_type": 161}}, {"pk": 486, "model": "auth.permission", "fields": {"codename": "delete_subtitle", "name": "Can delete subtitle", "content_type": 161}}, {"pk": 475, "model": "auth.permission", "fields": {"codename": "add_video", "name": "Can add video", "content_type": 158}}, {"pk": 476, "model": "auth.permission", "fields": {"codename": "change_video", "name": "Can change video", "content_type": 158}}, {"pk": 477, "model": "auth.permission", "fields": {"codename": "delete_video", "name": "Can delete video", "content_type": 158}}, {"pk": 373, "model": "auth.permission", "fields": {"codename": "add_country", "name": "Can add country", "content_type": 124}}, {"pk": 374, "model": "auth.permission", "fields": {"codename": "change_country", "name": "Can change country", "content_type": 124}}, {"pk": 375, "model": "auth.permission", "fields": {"codename": "delete_country", "name": "Can delete country", "content_type": 124}}, {"pk": 376, "model": "auth.permission", "fields": {"codename": "add_countryaccessrule", "name": "Can add country access rule", "content_type": 125}}, {"pk": 377, "model": "auth.permission", "fields": {"codename": "change_countryaccessrule", "name": "Can change country access rule", "content_type": 125}}, {"pk": 378, "model": "auth.permission", "fields": {"codename": "delete_countryaccessrule", "name": "Can delete country access rule", "content_type": 125}}, {"pk": 379, "model": "auth.permission", "fields": {"codename": "add_courseaccessrulehistory", "name": "Can add course access rule history", "content_type": 126}}, {"pk": 380, "model": "auth.permission", "fields": {"codename": "change_courseaccessrulehistory", "name": "Can change course access rule history", "content_type": 126}}, {"pk": 381, "model": "auth.permission", "fields": {"codename": "delete_courseaccessrulehistory", "name": "Can delete course access rule history", "content_type": 126}}, {"pk": 364, "model": "auth.permission", "fields": {"codename": "add_embargoedcourse", "name": "Can add embargoed course", "content_type": 121}}, {"pk": 365, "model": "auth.permission", "fields": {"codename": "change_embargoedcourse", "name": "Can change embargoed course", "content_type": 121}}, {"pk": 366, "model": "auth.permission", "fields": {"codename": "delete_embargoedcourse", "name": "Can delete embargoed course", "content_type": 121}}, {"pk": 367, "model": "auth.permission", "fields": {"codename": "add_embargoedstate", "name": "Can add embargoed state", "content_type": 122}}, {"pk": 368, "model": "auth.permission", "fields": {"codename": "change_embargoedstate", "name": "Can change embargoed state", "content_type": 122}}, {"pk": 369, "model": "auth.permission", "fields": {"codename": "delete_embargoedstate", "name": "Can delete embargoed state", "content_type": 122}}, {"pk": 382, "model": "auth.permission", "fields": {"codename": "add_ipfilter", "name": "Can add ip filter", "content_type": 127}}, {"pk": 383, "model": "auth.permission", "fields": {"codename": "change_ipfilter", "name": "Can change ip filter", "content_type": 127}}, {"pk": 384, "model": "auth.permission", "fields": {"codename": "delete_ipfilter", "name": "Can delete ip filter", "content_type": 127}}, {"pk": 370, "model": "auth.permission", "fields": {"codename": "add_restrictedcourse", "name": "Can add restricted course", "content_type": 123}}, {"pk": 371, "model": "auth.permission", "fields": {"codename": "change_restrictedcourse", "name": "Can change restricted course", "content_type": 123}}, {"pk": 372, "model": "auth.permission", "fields": {"codename": "delete_restrictedcourse", "name": "Can delete restricted course", "content_type": 123}}, {"pk": 211, "model": "auth.permission", "fields": {"codename": "add_externalauthmap", "name": "Can add external auth map", "content_type": 71}}, {"pk": 212, "model": "auth.permission", "fields": {"codename": "change_externalauthmap", "name": "Can change external auth map", "content_type": 71}}, {"pk": 213, "model": "auth.permission", "fields": {"codename": "delete_externalauthmap", "name": "Can delete external auth map", "content_type": 71}}, {"pk": 277, "model": "auth.permission", "fields": {"codename": "add_puzzlecomplete", "name": "Can add puzzle complete", "content_type": 92}}, {"pk": 278, "model": "auth.permission", "fields": {"codename": "change_puzzlecomplete", "name": "Can change puzzle complete", "content_type": 92}}, {"pk": 279, "model": "auth.permission", "fields": {"codename": "delete_puzzlecomplete", "name": "Can delete puzzle complete", "content_type": 92}}, {"pk": 274, "model": "auth.permission", "fields": {"codename": "add_score", "name": "Can add score", "content_type": 91}}, {"pk": 275, "model": "auth.permission", "fields": {"codename": "change_score", "name": "Can change score", "content_type": 91}}, {"pk": 276, "model": "auth.permission", "fields": {"codename": "delete_score", "name": "Can delete score", "content_type": 91}}, {"pk": 175, "model": "auth.permission", "fields": {"codename": "add_instructortask", "name": "Can add instructor task", "content_type": 59}}, {"pk": 176, "model": "auth.permission", "fields": {"codename": "change_instructortask", "name": "Can change instructor task", "content_type": 59}}, {"pk": 177, "model": "auth.permission", "fields": {"codename": "delete_instructortask", "name": "Can delete instructor task", "content_type": 59}}, {"pk": 178, "model": "auth.permission", "fields": {"codename": "add_coursesoftware", "name": "Can add course software", "content_type": 60}}, {"pk": 179, "model": "auth.permission", "fields": {"codename": "change_coursesoftware", "name": "Can change course software", "content_type": 60}}, {"pk": 180, "model": "auth.permission", "fields": {"codename": "delete_coursesoftware", "name": "Can delete course software", "content_type": 60}}, {"pk": 181, "model": "auth.permission", "fields": {"codename": "add_userlicense", "name": "Can add user license", "content_type": 61}}, {"pk": 182, "model": "auth.permission", "fields": {"codename": "change_userlicense", "name": "Can change user license", "content_type": 61}}, {"pk": 183, "model": "auth.permission", "fields": {"codename": "delete_userlicense", "name": "Can delete user license", "content_type": 61}}, {"pk": 397, "model": "auth.permission", "fields": {"codename": "add_xblockasidesconfig", "name": "Can add x block asides config", "content_type": 132}}, {"pk": 398, "model": "auth.permission", "fields": {"codename": "change_xblockasidesconfig", "name": "Can change x block asides config", "content_type": 132}}, {"pk": 399, "model": "auth.permission", "fields": {"codename": "delete_xblockasidesconfig", "name": "Can delete x block asides config", "content_type": 132}}, {"pk": 496, "model": "auth.permission", "fields": {"codename": "add_coursecontentmilestone", "name": "Can add course content milestone", "content_type": 165}}, {"pk": 497, "model": "auth.permission", "fields": {"codename": "change_coursecontentmilestone", "name": "Can change course content milestone", "content_type": 165}}, {"pk": 498, "model": "auth.permission", "fields": {"codename": "delete_coursecontentmilestone", "name": "Can delete course content milestone", "content_type": 165}}, {"pk": 493, "model": "auth.permission", "fields": {"codename": "add_coursemilestone", "name": "Can add course milestone", "content_type": 164}}, {"pk": 494, "model": "auth.permission", "fields": {"codename": "change_coursemilestone", "name": "Can change course milestone", "content_type": 164}}, {"pk": 495, "model": "auth.permission", "fields": {"codename": "delete_coursemilestone", "name": "Can delete course milestone", "content_type": 164}}, {"pk": 487, "model": "auth.permission", "fields": {"codename": "add_milestone", "name": "Can add milestone", "content_type": 162}}, {"pk": 488, "model": "auth.permission", "fields": {"codename": "change_milestone", "name": "Can change milestone", "content_type": 162}}, {"pk": 489, "model": "auth.permission", "fields": {"codename": "delete_milestone", "name": "Can delete milestone", "content_type": 162}}, {"pk": 490, "model": "auth.permission", "fields": {"codename": "add_milestonerelationshiptype", "name": "Can add milestone relationship type", "content_type": 163}}, {"pk": 491, "model": "auth.permission", "fields": {"codename": "change_milestonerelationshiptype", "name": "Can change milestone relationship type", "content_type": 163}}, {"pk": 492, "model": "auth.permission", "fields": {"codename": "delete_milestonerelationshiptype", "name": "Can delete milestone relationship type", "content_type": 163}}, {"pk": 499, "model": "auth.permission", "fields": {"codename": "add_usermilestone", "name": "Can add user milestone", "content_type": 166}}, {"pk": 500, "model": "auth.permission", "fields": {"codename": "change_usermilestone", "name": "Can change user milestone", "content_type": 166}}, {"pk": 501, "model": "auth.permission", "fields": {"codename": "delete_usermilestone", "name": "Can delete user milestone", "content_type": 166}}, {"pk": 388, "model": "auth.permission", "fields": {"codename": "add_mobileapiconfig", "name": "Can add mobile api config", "content_type": 129}}, {"pk": 389, "model": "auth.permission", "fields": {"codename": "change_mobileapiconfig", "name": "Can change mobile api config", "content_type": 129}}, {"pk": 390, "model": "auth.permission", "fields": {"codename": "delete_mobileapiconfig", "name": "Can delete mobile api config", "content_type": 129}}, {"pk": 280, "model": "auth.permission", "fields": {"codename": "add_note", "name": "Can add note", "content_type": 93}}, {"pk": 281, "model": "auth.permission", "fields": {"codename": "change_note", "name": "Can change note", "content_type": 93}}, {"pk": 282, "model": "auth.permission", "fields": {"codename": "delete_note", "name": "Can delete note", "content_type": 93}}, {"pk": 220, "model": "auth.permission", "fields": {"codename": "add_accesstoken", "name": "Can add access token", "content_type": 74}}, {"pk": 221, "model": "auth.permission", "fields": {"codename": "change_accesstoken", "name": "Can change access token", "content_type": 74}}, {"pk": 222, "model": "auth.permission", "fields": {"codename": "delete_accesstoken", "name": "Can delete access token", "content_type": 74}}, {"pk": 214, "model": "auth.permission", "fields": {"codename": "add_client", "name": "Can add client", "content_type": 72}}, {"pk": 215, "model": "auth.permission", "fields": {"codename": "change_client", "name": "Can change client", "content_type": 72}}, {"pk": 216, "model": "auth.permission", "fields": {"codename": "delete_client", "name": "Can delete client", "content_type": 72}}, {"pk": 217, "model": "auth.permission", "fields": {"codename": "add_grant", "name": "Can add grant", "content_type": 73}}, {"pk": 218, "model": "auth.permission", "fields": {"codename": "change_grant", "name": "Can change grant", "content_type": 73}}, {"pk": 219, "model": "auth.permission", "fields": {"codename": "delete_grant", "name": "Can delete grant", "content_type": 73}}, {"pk": 223, "model": "auth.permission", "fields": {"codename": "add_refreshtoken", "name": "Can add refresh token", "content_type": 75}}, {"pk": 224, "model": "auth.permission", "fields": {"codename": "change_refreshtoken", "name": "Can change refresh token", "content_type": 75}}, {"pk": 225, "model": "auth.permission", "fields": {"codename": "delete_refreshtoken", "name": "Can delete refresh token", "content_type": 75}}, {"pk": 226, "model": "auth.permission", "fields": {"codename": "add_trustedclient", "name": "Can add trusted client", "content_type": 76}}, {"pk": 227, "model": "auth.permission", "fields": {"codename": "change_trustedclient", "name": "Can change trusted client", "content_type": 76}}, {"pk": 228, "model": "auth.permission", "fields": {"codename": "delete_trustedclient", "name": "Can delete trusted client", "content_type": 76}}, {"pk": 49, "model": "auth.permission", "fields": {"codename": "add_psychometricdata", "name": "Can add psychometric data", "content_type": 17}}, {"pk": 50, "model": "auth.permission", "fields": {"codename": "change_psychometricdata", "name": "Can change psychometric data", "content_type": 17}}, {"pk": 51, "model": "auth.permission", "fields": {"codename": "delete_psychometricdata", "name": "Can delete psychometric data", "content_type": 17}}, {"pk": 361, "model": "auth.permission", "fields": {"codename": "add_midcoursereverificationwindow", "name": "Can add midcourse reverification window", "content_type": 120}}, {"pk": 362, "model": "auth.permission", "fields": {"codename": "change_midcoursereverificationwindow", "name": "Can change midcourse reverification window", "content_type": 120}}, {"pk": 363, "model": "auth.permission", "fields": {"codename": "delete_midcoursereverificationwindow", "name": "Can delete midcourse reverification window", "content_type": 120}}, {"pk": 13, "model": "auth.permission", "fields": {"codename": "add_session", "name": "Can add session", "content_type": 5}}, {"pk": 14, "model": "auth.permission", "fields": {"codename": "change_session", "name": "Can change session", "content_type": 5}}, {"pk": 15, "model": "auth.permission", "fields": {"codename": "delete_session", "name": "Can delete session", "content_type": 5}}, {"pk": 340, "model": "auth.permission", "fields": {"codename": "add_certificateitem", "name": "Can add certificate item", "content_type": 113}}, {"pk": 341, "model": "auth.permission", "fields": {"codename": "change_certificateitem", "name": "Can change certificate item", "content_type": 113}}, {"pk": 342, "model": "auth.permission", "fields": {"codename": "delete_certificateitem", "name": "Can delete certificate item", "content_type": 113}}, {"pk": 322, "model": "auth.permission", "fields": {"codename": "add_coupon", "name": "Can add coupon", "content_type": 107}}, {"pk": 323, "model": "auth.permission", "fields": {"codename": "change_coupon", "name": "Can change coupon", "content_type": 107}}, {"pk": 324, "model": "auth.permission", "fields": {"codename": "delete_coupon", "name": "Can delete coupon", "content_type": 107}}, {"pk": 325, "model": "auth.permission", "fields": {"codename": "add_couponredemption", "name": "Can add coupon redemption", "content_type": 108}}, {"pk": 326, "model": "auth.permission", "fields": {"codename": "change_couponredemption", "name": "Can change coupon redemption", "content_type": 108}}, {"pk": 327, "model": "auth.permission", "fields": {"codename": "delete_couponredemption", "name": "Can delete coupon redemption", "content_type": 108}}, {"pk": 331, "model": "auth.permission", "fields": {"codename": "add_courseregcodeitem", "name": "Can add course reg code item", "content_type": 110}}, {"pk": 332, "model": "auth.permission", "fields": {"codename": "change_courseregcodeitem", "name": "Can change course reg code item", "content_type": 110}}, {"pk": 333, "model": "auth.permission", "fields": {"codename": "delete_courseregcodeitem", "name": "Can delete course reg code item", "content_type": 110}}, {"pk": 334, "model": "auth.permission", "fields": {"codename": "add_courseregcodeitemannotation", "name": "Can add course reg code item annotation", "content_type": 111}}, {"pk": 335, "model": "auth.permission", "fields": {"codename": "change_courseregcodeitemannotation", "name": "Can change course reg code item annotation", "content_type": 111}}, {"pk": 336, "model": "auth.permission", "fields": {"codename": "delete_courseregcodeitemannotation", "name": "Can delete course reg code item annotation", "content_type": 111}}, {"pk": 316, "model": "auth.permission", "fields": {"codename": "add_courseregistrationcode", "name": "Can add course registration code", "content_type": 105}}, {"pk": 317, "model": "auth.permission", "fields": {"codename": "change_courseregistrationcode", "name": "Can change course registration code", "content_type": 105}}, {"pk": 318, "model": "auth.permission", "fields": {"codename": "delete_courseregistrationcode", "name": "Can delete course registration code", "content_type": 105}}, {"pk": 310, "model": "auth.permission", "fields": {"codename": "add_courseregistrationcodeinvoiceitem", "name": "Can add course registration code invoice item", "content_type": 103}}, {"pk": 311, "model": "auth.permission", "fields": {"codename": "change_courseregistrationcodeinvoiceitem", "name": "Can change course registration code invoice item", "content_type": 103}}, {"pk": 312, "model": "auth.permission", "fields": {"codename": "delete_courseregistrationcodeinvoiceitem", "name": "Can delete course registration code invoice item", "content_type": 103}}, {"pk": 346, "model": "auth.permission", "fields": {"codename": "add_donation", "name": "Can add donation", "content_type": 115}}, {"pk": 347, "model": "auth.permission", "fields": {"codename": "change_donation", "name": "Can change donation", "content_type": 115}}, {"pk": 348, "model": "auth.permission", "fields": {"codename": "delete_donation", "name": "Can delete donation", "content_type": 115}}, {"pk": 343, "model": "auth.permission", "fields": {"codename": "add_donationconfiguration", "name": "Can add donation configuration", "content_type": 114}}, {"pk": 344, "model": "auth.permission", "fields": {"codename": "change_donationconfiguration", "name": "Can change donation configuration", "content_type": 114}}, {"pk": 345, "model": "auth.permission", "fields": {"codename": "delete_donationconfiguration", "name": "Can delete donation configuration", "content_type": 114}}, {"pk": 301, "model": "auth.permission", "fields": {"codename": "add_invoice", "name": "Can add invoice", "content_type": 100}}, {"pk": 302, "model": "auth.permission", "fields": {"codename": "change_invoice", "name": "Can change invoice", "content_type": 100}}, {"pk": 303, "model": "auth.permission", "fields": {"codename": "delete_invoice", "name": "Can delete invoice", "content_type": 100}}, {"pk": 313, "model": "auth.permission", "fields": {"codename": "add_invoicehistory", "name": "Can add invoice history", "content_type": 104}}, {"pk": 314, "model": "auth.permission", "fields": {"codename": "change_invoicehistory", "name": "Can change invoice history", "content_type": 104}}, {"pk": 315, "model": "auth.permission", "fields": {"codename": "delete_invoicehistory", "name": "Can delete invoice history", "content_type": 104}}, {"pk": 307, "model": "auth.permission", "fields": {"codename": "add_invoiceitem", "name": "Can add invoice item", "content_type": 102}}, {"pk": 308, "model": "auth.permission", "fields": {"codename": "change_invoiceitem", "name": "Can change invoice item", "content_type": 102}}, {"pk": 309, "model": "auth.permission", "fields": {"codename": "delete_invoiceitem", "name": "Can delete invoice item", "content_type": 102}}, {"pk": 304, "model": "auth.permission", "fields": {"codename": "add_invoicetransaction", "name": "Can add invoice transaction", "content_type": 101}}, {"pk": 305, "model": "auth.permission", "fields": {"codename": "change_invoicetransaction", "name": "Can change invoice transaction", "content_type": 101}}, {"pk": 306, "model": "auth.permission", "fields": {"codename": "delete_invoicetransaction", "name": "Can delete invoice transaction", "content_type": 101}}, {"pk": 295, "model": "auth.permission", "fields": {"codename": "add_order", "name": "Can add order", "content_type": 98}}, {"pk": 296, "model": "auth.permission", "fields": {"codename": "change_order", "name": "Can change order", "content_type": 98}}, {"pk": 297, "model": "auth.permission", "fields": {"codename": "delete_order", "name": "Can delete order", "content_type": 98}}, {"pk": 298, "model": "auth.permission", "fields": {"codename": "add_orderitem", "name": "Can add order item", "content_type": 99}}, {"pk": 299, "model": "auth.permission", "fields": {"codename": "change_orderitem", "name": "Can change order item", "content_type": 99}}, {"pk": 300, "model": "auth.permission", "fields": {"codename": "delete_orderitem", "name": "Can delete order item", "content_type": 99}}, {"pk": 328, "model": "auth.permission", "fields": {"codename": "add_paidcourseregistration", "name": "Can add paid course registration", "content_type": 109}}, {"pk": 329, "model": "auth.permission", "fields": {"codename": "change_paidcourseregistration", "name": "Can change paid course registration", "content_type": 109}}, {"pk": 330, "model": "auth.permission", "fields": {"codename": "delete_paidcourseregistration", "name": "Can delete paid course registration", "content_type": 109}}, {"pk": 337, "model": "auth.permission", "fields": {"codename": "add_paidcourseregistrationannotation", "name": "Can add paid course registration annotation", "content_type": 112}}, {"pk": 338, "model": "auth.permission", "fields": {"codename": "change_paidcourseregistrationannotation", "name": "Can change paid course registration annotation", "content_type": 112}}, {"pk": 339, "model": "auth.permission", "fields": {"codename": "delete_paidcourseregistrationannotation", "name": "Can delete paid course registration annotation", "content_type": 112}}, {"pk": 319, "model": "auth.permission", "fields": {"codename": "add_registrationcoderedemption", "name": "Can add registration code redemption", "content_type": 106}}, {"pk": 320, "model": "auth.permission", "fields": {"codename": "change_registrationcoderedemption", "name": "Can change registration code redemption", "content_type": 106}}, {"pk": 321, "model": "auth.permission", "fields": {"codename": "delete_registrationcoderedemption", "name": "Can delete registration code redemption", "content_type": 106}}, {"pk": 16, "model": "auth.permission", "fields": {"codename": "add_site", "name": "Can add site", "content_type": 6}}, {"pk": 17, "model": "auth.permission", "fields": {"codename": "change_site", "name": "Can change site", "content_type": 6}}, {"pk": 18, "model": "auth.permission", "fields": {"codename": "delete_site", "name": "Can delete site", "content_type": 6}}, {"pk": 43, "model": "auth.permission", "fields": {"codename": "add_migrationhistory", "name": "Can add migration history", "content_type": 15}}, {"pk": 44, "model": "auth.permission", "fields": {"codename": "change_migrationhistory", "name": "Can change migration history", "content_type": 15}}, {"pk": 45, "model": "auth.permission", "fields": {"codename": "delete_migrationhistory", "name": "Can delete migration history", "content_type": 15}}, {"pk": 283, "model": "auth.permission", "fields": {"codename": "add_splashconfig", "name": "Can add splash config", "content_type": 94}}, {"pk": 284, "model": "auth.permission", "fields": {"codename": "change_splashconfig", "name": "Can change splash config", "content_type": 94}}, {"pk": 285, "model": "auth.permission", "fields": {"codename": "delete_splashconfig", "name": "Can delete splash config", "content_type": 94}}, {"pk": 100, "model": "auth.permission", "fields": {"codename": "add_anonymoususerid", "name": "Can add anonymous user id", "content_type": 34}}, {"pk": 101, "model": "auth.permission", "fields": {"codename": "change_anonymoususerid", "name": "Can change anonymous user id", "content_type": 34}}, {"pk": 102, "model": "auth.permission", "fields": {"codename": "delete_anonymoususerid", "name": "Can delete anonymous user id", "content_type": 34}}, {"pk": 136, "model": "auth.permission", "fields": {"codename": "add_courseaccessrole", "name": "Can add course access role", "content_type": 46}}, {"pk": 137, "model": "auth.permission", "fields": {"codename": "change_courseaccessrole", "name": "Can change course access role", "content_type": 46}}, {"pk": 138, "model": "auth.permission", "fields": {"codename": "delete_courseaccessrole", "name": "Can delete course access role", "content_type": 46}}, {"pk": 130, "model": "auth.permission", "fields": {"codename": "add_courseenrollment", "name": "Can add course enrollment", "content_type": 44}}, {"pk": 131, "model": "auth.permission", "fields": {"codename": "change_courseenrollment", "name": "Can change course enrollment", "content_type": 44}}, {"pk": 132, "model": "auth.permission", "fields": {"codename": "delete_courseenrollment", "name": "Can delete course enrollment", "content_type": 44}}, {"pk": 133, "model": "auth.permission", "fields": {"codename": "add_courseenrollmentallowed", "name": "Can add course enrollment allowed", "content_type": 45}}, {"pk": 134, "model": "auth.permission", "fields": {"codename": "change_courseenrollmentallowed", "name": "Can change course enrollment allowed", "content_type": 45}}, {"pk": 135, "model": "auth.permission", "fields": {"codename": "delete_courseenrollmentallowed", "name": "Can delete course enrollment allowed", "content_type": 45}}, {"pk": 139, "model": "auth.permission", "fields": {"codename": "add_dashboardconfiguration", "name": "Can add dashboard configuration", "content_type": 47}}, {"pk": 140, "model": "auth.permission", "fields": {"codename": "change_dashboardconfiguration", "name": "Can change dashboard configuration", "content_type": 47}}, {"pk": 141, "model": "auth.permission", "fields": {"codename": "delete_dashboardconfiguration", "name": "Can delete dashboard configuration", "content_type": 47}}, {"pk": 145, "model": "auth.permission", "fields": {"codename": "add_entranceexamconfiguration", "name": "Can add entrance exam configuration", "content_type": 49}}, {"pk": 146, "model": "auth.permission", "fields": {"codename": "change_entranceexamconfiguration", "name": "Can change entrance exam configuration", "content_type": 49}}, {"pk": 147, "model": "auth.permission", "fields": {"codename": "delete_entranceexamconfiguration", "name": "Can delete entrance exam configuration", "content_type": 49}}, {"pk": 142, "model": "auth.permission", "fields": {"codename": "add_linkedinaddtoprofileconfiguration", "name": "Can add linked in add to profile configuration", "content_type": 48}}, {"pk": 143, "model": "auth.permission", "fields": {"codename": "change_linkedinaddtoprofileconfiguration", "name": "Can change linked in add to profile configuration", "content_type": 48}}, {"pk": 144, "model": "auth.permission", "fields": {"codename": "delete_linkedinaddtoprofileconfiguration", "name": "Can delete linked in add to profile configuration", "content_type": 48}}, {"pk": 127, "model": "auth.permission", "fields": {"codename": "add_loginfailures", "name": "Can add login failures", "content_type": 43}}, {"pk": 128, "model": "auth.permission", "fields": {"codename": "change_loginfailures", "name": "Can change login failures", "content_type": 43}}, {"pk": 129, "model": "auth.permission", "fields": {"codename": "delete_loginfailures", "name": "Can delete login failures", "content_type": 43}}, {"pk": 124, "model": "auth.permission", "fields": {"codename": "add_passwordhistory", "name": "Can add password history", "content_type": 42}}, {"pk": 125, "model": "auth.permission", "fields": {"codename": "change_passwordhistory", "name": "Can change password history", "content_type": 42}}, {"pk": 126, "model": "auth.permission", "fields": {"codename": "delete_passwordhistory", "name": "Can delete password history", "content_type": 42}}, {"pk": 121, "model": "auth.permission", "fields": {"codename": "add_pendingemailchange", "name": "Can add pending email change", "content_type": 41}}, {"pk": 122, "model": "auth.permission", "fields": {"codename": "change_pendingemailchange", "name": "Can change pending email change", "content_type": 41}}, {"pk": 123, "model": "auth.permission", "fields": {"codename": "delete_pendingemailchange", "name": "Can delete pending email change", "content_type": 41}}, {"pk": 118, "model": "auth.permission", "fields": {"codename": "add_pendingnamechange", "name": "Can add pending name change", "content_type": 40}}, {"pk": 119, "model": "auth.permission", "fields": {"codename": "change_pendingnamechange", "name": "Can change pending name change", "content_type": 40}}, {"pk": 120, "model": "auth.permission", "fields": {"codename": "delete_pendingnamechange", "name": "Can delete pending name change", "content_type": 40}}, {"pk": 115, "model": "auth.permission", "fields": {"codename": "add_registration", "name": "Can add registration", "content_type": 39}}, {"pk": 116, "model": "auth.permission", "fields": {"codename": "change_registration", "name": "Can change registration", "content_type": 39}}, {"pk": 117, "model": "auth.permission", "fields": {"codename": "delete_registration", "name": "Can delete registration", "content_type": 39}}, {"pk": 106, "model": "auth.permission", "fields": {"codename": "add_userprofile", "name": "Can add user profile", "content_type": 36}}, {"pk": 107, "model": "auth.permission", "fields": {"codename": "change_userprofile", "name": "Can change user profile", "content_type": 36}}, {"pk": 108, "model": "auth.permission", "fields": {"codename": "delete_userprofile", "name": "Can delete user profile", "content_type": 36}}, {"pk": 109, "model": "auth.permission", "fields": {"codename": "add_usersignupsource", "name": "Can add user signup source", "content_type": 37}}, {"pk": 110, "model": "auth.permission", "fields": {"codename": "change_usersignupsource", "name": "Can change user signup source", "content_type": 37}}, {"pk": 111, "model": "auth.permission", "fields": {"codename": "delete_usersignupsource", "name": "Can delete user signup source", "content_type": 37}}, {"pk": 103, "model": "auth.permission", "fields": {"codename": "add_userstanding", "name": "Can add user standing", "content_type": 35}}, {"pk": 104, "model": "auth.permission", "fields": {"codename": "change_userstanding", "name": "Can change user standing", "content_type": 35}}, {"pk": 105, "model": "auth.permission", "fields": {"codename": "delete_userstanding", "name": "Can delete user standing", "content_type": 35}}, {"pk": 112, "model": "auth.permission", "fields": {"codename": "add_usertestgroup", "name": "Can add user test group", "content_type": 38}}, {"pk": 113, "model": "auth.permission", "fields": {"codename": "change_usertestgroup", "name": "Can change user test group", "content_type": 38}}, {"pk": 114, "model": "auth.permission", "fields": {"codename": "delete_usertestgroup", "name": "Can delete user test group", "content_type": 38}}, {"pk": 409, "model": "auth.permission", "fields": {"codename": "add_score", "name": "Can add score", "content_type": 136}}, {"pk": 410, "model": "auth.permission", "fields": {"codename": "change_score", "name": "Can change score", "content_type": 136}}, {"pk": 411, "model": "auth.permission", "fields": {"codename": "delete_score", "name": "Can delete score", "content_type": 136}}, {"pk": 412, "model": "auth.permission", "fields": {"codename": "add_scoresummary", "name": "Can add score summary", "content_type": 137}}, {"pk": 413, "model": "auth.permission", "fields": {"codename": "change_scoresummary", "name": "Can change score summary", "content_type": 137}}, {"pk": 414, "model": "auth.permission", "fields": {"codename": "delete_scoresummary", "name": "Can delete score summary", "content_type": 137}}, {"pk": 403, "model": "auth.permission", "fields": {"codename": "add_studentitem", "name": "Can add student item", "content_type": 134}}, {"pk": 404, "model": "auth.permission", "fields": {"codename": "change_studentitem", "name": "Can change student item", "content_type": 134}}, {"pk": 405, "model": "auth.permission", "fields": {"codename": "delete_studentitem", "name": "Can delete student item", "content_type": 134}}, {"pk": 406, "model": "auth.permission", "fields": {"codename": "add_submission", "name": "Can add submission", "content_type": 135}}, {"pk": 407, "model": "auth.permission", "fields": {"codename": "change_submission", "name": "Can change submission", "content_type": 135}}, {"pk": 408, "model": "auth.permission", "fields": {"codename": "delete_submission", "name": "Can delete submission", "content_type": 135}}, {"pk": 394, "model": "auth.permission", "fields": {"codename": "add_surveyanswer", "name": "Can add survey answer", "content_type": 131}}, {"pk": 395, "model": "auth.permission", "fields": {"codename": "change_surveyanswer", "name": "Can change survey answer", "content_type": 131}}, {"pk": 396, "model": "auth.permission", "fields": {"codename": "delete_surveyanswer", "name": "Can delete survey answer", "content_type": 131}}, {"pk": 391, "model": "auth.permission", "fields": {"codename": "add_surveyform", "name": "Can add survey form", "content_type": 130}}, {"pk": 392, "model": "auth.permission", "fields": {"codename": "change_surveyform", "name": "Can change survey form", "content_type": 130}}, {"pk": 393, "model": "auth.permission", "fields": {"codename": "delete_surveyform", "name": "Can delete survey form", "content_type": 130}}, {"pk": 148, "model": "auth.permission", "fields": {"codename": "add_trackinglog", "name": "Can add tracking log", "content_type": 50}}, {"pk": 149, "model": "auth.permission", "fields": {"codename": "change_trackinglog", "name": "Can change tracking log", "content_type": 50}}, {"pk": 150, "model": "auth.permission", "fields": {"codename": "delete_trackinglog", "name": "Can delete tracking log", "content_type": 50}}, {"pk": 289, "model": "auth.permission", "fields": {"codename": "add_usercoursetag", "name": "Can add user course tag", "content_type": 96}}, {"pk": 290, "model": "auth.permission", "fields": {"codename": "change_usercoursetag", "name": "Can change user course tag", "content_type": 96}}, {"pk": 291, "model": "auth.permission", "fields": {"codename": "delete_usercoursetag", "name": "Can delete user course tag", "content_type": 96}}, {"pk": 292, "model": "auth.permission", "fields": {"codename": "add_userorgtag", "name": "Can add user org tag", "content_type": 97}}, {"pk": 293, "model": "auth.permission", "fields": {"codename": "change_userorgtag", "name": "Can change user org tag", "content_type": 97}}, {"pk": 294, "model": "auth.permission", "fields": {"codename": "delete_userorgtag", "name": "Can delete user org tag", "content_type": 97}}, {"pk": 286, "model": "auth.permission", "fields": {"codename": "add_userpreference", "name": "Can add user preference", "content_type": 95}}, {"pk": 287, "model": "auth.permission", "fields": {"codename": "change_userpreference", "name": "Can change user preference", "content_type": 95}}, {"pk": 288, "model": "auth.permission", "fields": {"codename": "delete_userpreference", "name": "Can delete user preference", "content_type": 95}}, {"pk": 151, "model": "auth.permission", "fields": {"codename": "add_ratelimitconfiguration", "name": "Can add rate limit configuration", "content_type": 51}}, {"pk": 152, "model": "auth.permission", "fields": {"codename": "change_ratelimitconfiguration", "name": "Can change rate limit configuration", "content_type": 51}}, {"pk": 153, "model": "auth.permission", "fields": {"codename": "delete_ratelimitconfiguration", "name": "Can delete rate limit configuration", "content_type": 51}}, {"pk": 355, "model": "auth.permission", "fields": {"codename": "add_softwaresecurephotoverification", "name": "Can add software secure photo verification", "content_type": 118}}, {"pk": 356, "model": "auth.permission", "fields": {"codename": "change_softwaresecurephotoverification", "name": "Can change software secure photo verification", "content_type": 118}}, {"pk": 357, "model": "auth.permission", "fields": {"codename": "delete_softwaresecurephotoverification", "name": "Can delete software secure photo verification", "content_type": 118}}, {"pk": 229, "model": "auth.permission", "fields": {"codename": "add_article", "name": "Can add article", "content_type": 77}}, {"pk": 233, "model": "auth.permission", "fields": {"codename": "assign", "name": "Can change ownership of any article", "content_type": 77}}, {"pk": 230, "model": "auth.permission", "fields": {"codename": "change_article", "name": "Can change article", "content_type": 77}}, {"pk": 231, "model": "auth.permission", "fields": {"codename": "delete_article", "name": "Can delete article", "content_type": 77}}, {"pk": 234, "model": "auth.permission", "fields": {"codename": "grant", "name": "Can assign permissions to other users", "content_type": 77}}, {"pk": 232, "model": "auth.permission", "fields": {"codename": "moderate", "name": "Can edit all articles and lock/unlock/restore", "content_type": 77}}, {"pk": 235, "model": "auth.permission", "fields": {"codename": "add_articleforobject", "name": "Can add Article for object", "content_type": 78}}, {"pk": 236, "model": "auth.permission", "fields": {"codename": "change_articleforobject", "name": "Can change Article for object", "content_type": 78}}, {"pk": 237, "model": "auth.permission", "fields": {"codename": "delete_articleforobject", "name": "Can delete Article for object", "content_type": 78}}, {"pk": 244, "model": "auth.permission", "fields": {"codename": "add_articleplugin", "name": "Can add article plugin", "content_type": 81}}, {"pk": 245, "model": "auth.permission", "fields": {"codename": "change_articleplugin", "name": "Can change article plugin", "content_type": 81}}, {"pk": 246, "model": "auth.permission", "fields": {"codename": "delete_articleplugin", "name": "Can delete article plugin", "content_type": 81}}, {"pk": 238, "model": "auth.permission", "fields": {"codename": "add_articlerevision", "name": "Can add article revision", "content_type": 79}}, {"pk": 239, "model": "auth.permission", "fields": {"codename": "change_articlerevision", "name": "Can change article revision", "content_type": 79}}, {"pk": 240, "model": "auth.permission", "fields": {"codename": "delete_articlerevision", "name": "Can delete article revision", "content_type": 79}}, {"pk": 259, "model": "auth.permission", "fields": {"codename": "add_articlesubscription", "name": "Can add article subscription", "content_type": 86}}, {"pk": 260, "model": "auth.permission", "fields": {"codename": "change_articlesubscription", "name": "Can change article subscription", "content_type": 86}}, {"pk": 261, "model": "auth.permission", "fields": {"codename": "delete_articlesubscription", "name": "Can delete article subscription", "content_type": 86}}, {"pk": 247, "model": "auth.permission", "fields": {"codename": "add_reusableplugin", "name": "Can add reusable plugin", "content_type": 82}}, {"pk": 248, "model": "auth.permission", "fields": {"codename": "change_reusableplugin", "name": "Can change reusable plugin", "content_type": 82}}, {"pk": 249, "model": "auth.permission", "fields": {"codename": "delete_reusableplugin", "name": "Can delete reusable plugin", "content_type": 82}}, {"pk": 253, "model": "auth.permission", "fields": {"codename": "add_revisionplugin", "name": "Can add revision plugin", "content_type": 84}}, {"pk": 254, "model": "auth.permission", "fields": {"codename": "change_revisionplugin", "name": "Can change revision plugin", "content_type": 84}}, {"pk": 255, "model": "auth.permission", "fields": {"codename": "delete_revisionplugin", "name": "Can delete revision plugin", "content_type": 84}}, {"pk": 256, "model": "auth.permission", "fields": {"codename": "add_revisionpluginrevision", "name": "Can add revision plugin revision", "content_type": 85}}, {"pk": 257, "model": "auth.permission", "fields": {"codename": "change_revisionpluginrevision", "name": "Can change revision plugin revision", "content_type": 85}}, {"pk": 258, "model": "auth.permission", "fields": {"codename": "delete_revisionpluginrevision", "name": "Can delete revision plugin revision", "content_type": 85}}, {"pk": 250, "model": "auth.permission", "fields": {"codename": "add_simpleplugin", "name": "Can add simple plugin", "content_type": 83}}, {"pk": 251, "model": "auth.permission", "fields": {"codename": "change_simpleplugin", "name": "Can change simple plugin", "content_type": 83}}, {"pk": 252, "model": "auth.permission", "fields": {"codename": "delete_simpleplugin", "name": "Can delete simple plugin", "content_type": 83}}, {"pk": 241, "model": "auth.permission", "fields": {"codename": "add_urlpath", "name": "Can add URL path", "content_type": 80}}, {"pk": 242, "model": "auth.permission", "fields": {"codename": "change_urlpath", "name": "Can change URL path", "content_type": 80}}, {"pk": 243, "model": "auth.permission", "fields": {"codename": "delete_urlpath", "name": "Can delete URL path", "content_type": 80}}, {"pk": 463, "model": "auth.permission", "fields": {"codename": "add_assessmentworkflow", "name": "Can add assessment workflow", "content_type": 154}}, {"pk": 464, "model": "auth.permission", "fields": {"codename": "change_assessmentworkflow", "name": "Can change assessment workflow", "content_type": 154}}, {"pk": 465, "model": "auth.permission", "fields": {"codename": "delete_assessmentworkflow", "name": "Can delete assessment workflow", "content_type": 154}}, {"pk": 469, "model": "auth.permission", "fields": {"codename": "add_assessmentworkflowcancellation", "name": "Can add assessment workflow cancellation", "content_type": 156}}, {"pk": 470, "model": "auth.permission", "fields": {"codename": "change_assessmentworkflowcancellation", "name": "Can change assessment workflow cancellation", "content_type": 156}}, {"pk": 471, "model": "auth.permission", "fields": {"codename": "delete_assessmentworkflowcancellation", "name": "Can delete assessment workflow cancellation", "content_type": 156}}, {"pk": 466, "model": "auth.permission", "fields": {"codename": "add_assessmentworkflowstep", "name": "Can add assessment workflow step", "content_type": 155}}, {"pk": 467, "model": "auth.permission", "fields": {"codename": "change_assessmentworkflowstep", "name": "Can change assessment workflow step", "content_type": 155}}, {"pk": 468, "model": "auth.permission", "fields": {"codename": "delete_assessmentworkflowstep", "name": "Can delete assessment workflow step", "content_type": 155}}, {"pk": 508, "model": "auth.permission", "fields": {"codename": "add_studioconfig", "name": "Can add studio config", "content_type": 169}}, {"pk": 509, "model": "auth.permission", "fields": {"codename": "change_studioconfig", "name": "Can change studio config", "content_type": 169}}, {"pk": 510, "model": "auth.permission", "fields": {"codename": "delete_studioconfig", "name": "Can delete studio config", "content_type": 169}}, {"pk": 1, "model": "util.ratelimitconfiguration", "fields": {"change_date": "2015-03-31T06:25:45Z", "changed_by": null, "enabled": true}}, {"pk": 1, "model": "certificates.certificatehtmlviewconfiguration", "fields": {"change_date": "2015-03-31T06:25:47Z", "changed_by": null, "configuration": "{\n            {\n    \"default\": {\n        \"accomplishment_class_append\": \"accomplishment-certificate\",\n        \"platform_name\": \"edX\",\n        \"company_privacy_url\": \"http://www.edx.org/edx-privacy-policy\",\n        \"company_tos_url\": \"http://www.edx.org/edx-terms-service\",\n        \"company_verified_certificate_url\": \"http://www.edx.org/verified-certificate\",\n        \"document_stylesheet_url_application\": \"/static/certificates/sass/main-ltr.css\",\n        \"logo_src\": \"/static/certificates/images/logo-edx.svg\",\n        \"logo_url\": \"http://www.edx.org\"\n    },\n    \"honor\": {\n        \"certificate_type\": \"Honor Code\",\n        \"document_body_class_append\": \"is-honorcode\"\n    },\n    \"verified\": {\n        \"certificate_type\": \"Verified\",\n        \"document_body_class_append\": \"is-idverified\"\n    },\n    \"xseries\": {\n        \"certificate_type\": \"XSeries\",\n        \"document_body_class_append\": \"is-xseries\"\n    }\n}\n        }", "enabled": false}}, {"pk": 1, "model": "dark_lang.darklangconfig", "fields": {"change_date": "2015-03-31T06:26:01Z", "changed_by": null, "enabled": true, "released_languages": ""}}, {"pk": 1, "model": "mobile_api.mobileapiconfig", "fields": {"change_date": "2015-03-31T06:26:03Z", "video_profiles": "mobile_low,mobile_high,youtube", "changed_by": null, "enabled": false}}]
\ No newline at end of file
+[{"pk": 74, "model": "contenttypes.contenttype", "fields": {"model": "accesstoken", "name": "access token", "app_label": "oauth2"}}, {"pk": 151, "model": "contenttypes.contenttype", "fields": {"model": "aiclassifier", "name": "ai classifier", "app_label": "assessment"}}, {"pk": 150, "model": "contenttypes.contenttype", "fields": {"model": "aiclassifierset", "name": "ai classifier set", "app_label": "assessment"}}, {"pk": 153, "model": "contenttypes.contenttype", "fields": {"model": "aigradingworkflow", "name": "ai grading workflow", "app_label": "assessment"}}, {"pk": 152, "model": "contenttypes.contenttype", "fields": {"model": "aitrainingworkflow", "name": "ai training workflow", "app_label": "assessment"}}, {"pk": 34, "model": "contenttypes.contenttype", "fields": {"model": "anonymoususerid", "name": "anonymous user id", "app_label": "student"}}, {"pk": 77, "model": "contenttypes.contenttype", "fields": {"model": "article", "name": "article", "app_label": "wiki"}}, {"pk": 78, "model": "contenttypes.contenttype", "fields": {"model": "articleforobject", "name": "Article for object", "app_label": "wiki"}}, {"pk": 81, "model": "contenttypes.contenttype", "fields": {"model": "articleplugin", "name": "article plugin", "app_label": "wiki"}}, {"pk": 79, "model": "contenttypes.contenttype", "fields": {"model": "articlerevision", "name": "article revision", "app_label": "wiki"}}, {"pk": 86, "model": "contenttypes.contenttype", "fields": {"model": "articlesubscription", "name": "article subscription", "app_label": "wiki"}}, {"pk": 141, "model": "contenttypes.contenttype", "fields": {"model": "assessment", "name": "assessment", "app_label": "assessment"}}, {"pk": 144, "model": "contenttypes.contenttype", "fields": {"model": "assessmentfeedback", "name": "assessment feedback", "app_label": "assessment"}}, {"pk": 143, "model": "contenttypes.contenttype", "fields": {"model": "assessmentfeedbackoption", "name": "assessment feedback option", "app_label": "assessment"}}, {"pk": 142, "model": "contenttypes.contenttype", "fields": {"model": "assessmentpart", "name": "assessment part", "app_label": "assessment"}}, {"pk": 154, "model": "contenttypes.contenttype", "fields": {"model": "assessmentworkflow", "name": "assessment workflow", "app_label": "workflow"}}, {"pk": 156, "model": "contenttypes.contenttype", "fields": {"model": "assessmentworkflowcancellation", "name": "assessment workflow cancellation", "app_label": "workflow"}}, {"pk": 155, "model": "contenttypes.contenttype", "fields": {"model": "assessmentworkflowstep", "name": "assessment workflow step", "app_label": "workflow"}}, {"pk": 19, "model": "contenttypes.contenttype", "fields": {"model": "association", "name": "association", "app_label": "django_openid_auth"}}, {"pk": 25, "model": "contenttypes.contenttype", "fields": {"model": "association", "name": "association", "app_label": "default"}}, {"pk": 70, "model": "contenttypes.contenttype", "fields": {"model": "brandinginfoconfig", "name": "branding info config", "app_label": "branding"}}, {"pk": 57, "model": "contenttypes.contenttype", "fields": {"model": "certificategenerationconfiguration", "name": "certificate generation configuration", "app_label": "certificates"}}, {"pk": 56, "model": "contenttypes.contenttype", "fields": {"model": "certificategenerationcoursesetting", "name": "certificate generation course setting", "app_label": "certificates"}}, {"pk": 58, "model": "contenttypes.contenttype", "fields": {"model": "certificatehtmlviewconfiguration", "name": "certificate html view configuration", "app_label": "certificates"}}, {"pk": 113, "model": "contenttypes.contenttype", "fields": {"model": "certificateitem", "name": "certificate item", "app_label": "shoppingcart"}}, {"pk": 52, "model": "contenttypes.contenttype", "fields": {"model": "certificatewhitelist", "name": "certificate whitelist", "app_label": "certificates"}}, {"pk": 72, "model": "contenttypes.contenttype", "fields": {"model": "client", "name": "client", "app_label": "oauth2"}}, {"pk": 26, "model": "contenttypes.contenttype", "fields": {"model": "code", "name": "code", "app_label": "default"}}, {"pk": 4, "model": "contenttypes.contenttype", "fields": {"model": "contenttype", "name": "content type", "app_label": "contenttypes"}}, {"pk": 22, "model": "contenttypes.contenttype", "fields": {"model": "corsmodel", "name": "cors model", "app_label": "corsheaders"}}, {"pk": 124, "model": "contenttypes.contenttype", "fields": {"model": "country", "name": "country", "app_label": "embargo"}}, {"pk": 125, "model": "contenttypes.contenttype", "fields": {"model": "countryaccessrule", "name": "country access rule", "app_label": "embargo"}}, {"pk": 107, "model": "contenttypes.contenttype", "fields": {"model": "coupon", "name": "coupon", "app_label": "shoppingcart"}}, {"pk": 108, "model": "contenttypes.contenttype", "fields": {"model": "couponredemption", "name": "coupon redemption", "app_label": "shoppingcart"}}, {"pk": 46, "model": "contenttypes.contenttype", "fields": {"model": "courseaccessrole", "name": "course access role", "app_label": "student"}}, {"pk": 126, "model": "contenttypes.contenttype", "fields": {"model": "courseaccessrulehistory", "name": "course access rule history", "app_label": "embargo"}}, {"pk": 69, "model": "contenttypes.contenttype", "fields": {"model": "courseauthorization", "name": "course authorization", "app_label": "bulk_email"}}, {"pk": 65, "model": "contenttypes.contenttype", "fields": {"model": "coursecohort", "name": "course cohort", "app_label": "course_groups"}}, {"pk": 64, "model": "contenttypes.contenttype", "fields": {"model": "coursecohortssettings", "name": "course cohorts settings", "app_label": "course_groups"}}, {"pk": 165, "model": "contenttypes.contenttype", "fields": {"model": "coursecontentmilestone", "name": "course content milestone", "app_label": "milestones"}}, {"pk": 168, "model": "contenttypes.contenttype", "fields": {"model": "coursecreator", "name": "course creator", "app_label": "course_creators"}}, {"pk": 66, "model": "contenttypes.contenttype", "fields": {"model": "courseemail", "name": "course email", "app_label": "bulk_email"}}, {"pk": 68, "model": "contenttypes.contenttype", "fields": {"model": "courseemailtemplate", "name": "course email template", "app_label": "bulk_email"}}, {"pk": 44, "model": "contenttypes.contenttype", "fields": {"model": "courseenrollment", "name": "course enrollment", "app_label": "student"}}, {"pk": 45, "model": "contenttypes.contenttype", "fields": {"model": "courseenrollmentallowed", "name": "course enrollment allowed", "app_label": "student"}}, {"pk": 164, "model": "contenttypes.contenttype", "fields": {"model": "coursemilestone", "name": "course milestone", "app_label": "milestones"}}, {"pk": 116, "model": "contenttypes.contenttype", "fields": {"model": "coursemode", "name": "course mode", "app_label": "course_modes"}}, {"pk": 117, "model": "contenttypes.contenttype", "fields": {"model": "coursemodesarchive", "name": "course modes archive", "app_label": "course_modes"}}, {"pk": 110, "model": "contenttypes.contenttype", "fields": {"model": "courseregcodeitem", "name": "course reg code item", "app_label": "shoppingcart"}}, {"pk": 111, "model": "contenttypes.contenttype", "fields": {"model": "courseregcodeitemannotation", "name": "course reg code item annotation", "app_label": "shoppingcart"}}, {"pk": 105, "model": "contenttypes.contenttype", "fields": {"model": "courseregistrationcode", "name": "course registration code", "app_label": "shoppingcart"}}, {"pk": 103, "model": "contenttypes.contenttype", "fields": {"model": "courseregistrationcodeinvoiceitem", "name": "course registration code invoice item", "app_label": "shoppingcart"}}, {"pk": 128, "model": "contenttypes.contenttype", "fields": {"model": "coursererunstate", "name": "course rerun state", "app_label": "course_action_state"}}, {"pk": 60, "model": "contenttypes.contenttype", "fields": {"model": "coursesoftware", "name": "course software", "app_label": "licenses"}}, {"pk": 133, "model": "contenttypes.contenttype", "fields": {"model": "coursestructure", "name": "course structure", "app_label": "course_structures"}}, {"pk": 62, "model": "contenttypes.contenttype", "fields": {"model": "courseusergroup", "name": "course user group", "app_label": "course_groups"}}, {"pk": 63, "model": "contenttypes.contenttype", "fields": {"model": "courseusergrouppartitiongroup", "name": "course user group partition group", "app_label": "course_groups"}}, {"pk": 159, "model": "contenttypes.contenttype", "fields": {"model": "coursevideo", "name": "course video", "app_label": "edxval"}}, {"pk": 139, "model": "contenttypes.contenttype", "fields": {"model": "criterion", "name": "criterion", "app_label": "assessment"}}, {"pk": 140, "model": "contenttypes.contenttype", "fields": {"model": "criterionoption", "name": "criterion option", "app_label": "assessment"}}, {"pk": 10, "model": "contenttypes.contenttype", "fields": {"model": "crontabschedule", "name": "crontab", "app_label": "djcelery"}}, {"pk": 119, "model": "contenttypes.contenttype", "fields": {"model": "darklangconfig", "name": "dark lang config", "app_label": "dark_lang"}}, {"pk": 47, "model": "contenttypes.contenttype", "fields": {"model": "dashboardconfiguration", "name": "dashboard configuration", "app_label": "student"}}, {"pk": 115, "model": "contenttypes.contenttype", "fields": {"model": "donation", "name": "donation", "app_label": "shoppingcart"}}, {"pk": 114, "model": "contenttypes.contenttype", "fields": {"model": "donationconfiguration", "name": "donation configuration", "app_label": "shoppingcart"}}, {"pk": 121, "model": "contenttypes.contenttype", "fields": {"model": "embargoedcourse", "name": "embargoed course", "app_label": "embargo"}}, {"pk": 122, "model": "contenttypes.contenttype", "fields": {"model": "embargoedstate", "name": "embargoed state", "app_label": "embargo"}}, {"pk": 160, "model": "contenttypes.contenttype", "fields": {"model": "encodedvideo", "name": "encoded video", "app_label": "edxval"}}, {"pk": 49, "model": "contenttypes.contenttype", "fields": {"model": "entranceexamconfiguration", "name": "entrance exam configuration", "app_label": "student"}}, {"pk": 55, "model": "contenttypes.contenttype", "fields": {"model": "examplecertificate", "name": "example certificate", "app_label": "certificates"}}, {"pk": 54, "model": "contenttypes.contenttype", "fields": {"model": "examplecertificateset", "name": "example certificate set", "app_label": "certificates"}}, {"pk": 71, "model": "contenttypes.contenttype", "fields": {"model": "externalauthmap", "name": "external auth map", "app_label": "external_auth"}}, {"pk": 53, "model": "contenttypes.contenttype", "fields": {"model": "generatedcertificate", "name": "generated certificate", "app_label": "certificates"}}, {"pk": 73, "model": "contenttypes.contenttype", "fields": {"model": "grant", "name": "grant", "app_label": "oauth2"}}, {"pk": 2, "model": "contenttypes.contenttype", "fields": {"model": "group", "name": "group", "app_label": "auth"}}, {"pk": 59, "model": "contenttypes.contenttype", "fields": {"model": "instructortask", "name": "instructor task", "app_label": "instructor_task"}}, {"pk": 9, "model": "contenttypes.contenttype", "fields": {"model": "intervalschedule", "name": "interval", "app_label": "djcelery"}}, {"pk": 100, "model": "contenttypes.contenttype", "fields": {"model": "invoice", "name": "invoice", "app_label": "shoppingcart"}}, {"pk": 104, "model": "contenttypes.contenttype", "fields": {"model": "invoicehistory", "name": "invoice history", "app_label": "shoppingcart"}}, {"pk": 102, "model": "contenttypes.contenttype", "fields": {"model": "invoiceitem", "name": "invoice item", "app_label": "shoppingcart"}}, {"pk": 101, "model": "contenttypes.contenttype", "fields": {"model": "invoicetransaction", "name": "invoice transaction", "app_label": "shoppingcart"}}, {"pk": 127, "model": "contenttypes.contenttype", "fields": {"model": "ipfilter", "name": "ip filter", "app_label": "embargo"}}, {"pk": 48, "model": "contenttypes.contenttype", "fields": {"model": "linkedinaddtoprofileconfiguration", "name": "linked in add to profile configuration", "app_label": "student"}}, {"pk": 21, "model": "contenttypes.contenttype", "fields": {"model": "logentry", "name": "log entry", "app_label": "admin"}}, {"pk": 43, "model": "contenttypes.contenttype", "fields": {"model": "loginfailures", "name": "login failures", "app_label": "student"}}, {"pk": 120, "model": "contenttypes.contenttype", "fields": {"model": "midcoursereverificationwindow", "name": "midcourse reverification window", "app_label": "reverification"}}, {"pk": 15, "model": "contenttypes.contenttype", "fields": {"model": "migrationhistory", "name": "migration history", "app_label": "south"}}, {"pk": 162, "model": "contenttypes.contenttype", "fields": {"model": "milestone", "name": "milestone", "app_label": "milestones"}}, {"pk": 163, "model": "contenttypes.contenttype", "fields": {"model": "milestonerelationshiptype", "name": "milestone relationship type", "app_label": "milestones"}}, {"pk": 129, "model": "contenttypes.contenttype", "fields": {"model": "mobileapiconfig", "name": "mobile api config", "app_label": "mobile_api"}}, {"pk": 18, "model": "contenttypes.contenttype", "fields": {"model": "nonce", "name": "nonce", "app_label": "django_openid_auth"}}, {"pk": 24, "model": "contenttypes.contenttype", "fields": {"model": "nonce", "name": "nonce", "app_label": "default"}}, {"pk": 93, "model": "contenttypes.contenttype", "fields": {"model": "note", "name": "note", "app_label": "notes"}}, {"pk": 90, "model": "contenttypes.contenttype", "fields": {"model": "notification", "name": "notification", "app_label": "django_notify"}}, {"pk": 32, "model": "contenttypes.contenttype", "fields": {"model": "offlinecomputedgrade", "name": "offline computed grade", "app_label": "courseware"}}, {"pk": 33, "model": "contenttypes.contenttype", "fields": {"model": "offlinecomputedgradelog", "name": "offline computed grade log", "app_label": "courseware"}}, {"pk": 67, "model": "contenttypes.contenttype", "fields": {"model": "optout", "name": "optout", "app_label": "bulk_email"}}, {"pk": 98, "model": "contenttypes.contenttype", "fields": {"model": "order", "name": "order", "app_label": "shoppingcart"}}, {"pk": 99, "model": "contenttypes.contenttype", "fields": {"model": "orderitem", "name": "order item", "app_label": "shoppingcart"}}, {"pk": 109, "model": "contenttypes.contenttype", "fields": {"model": "paidcourseregistration", "name": "paid course registration", "app_label": "shoppingcart"}}, {"pk": 112, "model": "contenttypes.contenttype", "fields": {"model": "paidcourseregistrationannotation", "name": "paid course registration annotation", "app_label": "shoppingcart"}}, {"pk": 42, "model": "contenttypes.contenttype", "fields": {"model": "passwordhistory", "name": "password history", "app_label": "student"}}, {"pk": 145, "model": "contenttypes.contenttype", "fields": {"model": "peerworkflow", "name": "peer workflow", "app_label": "assessment"}}, {"pk": 146, "model": "contenttypes.contenttype", "fields": {"model": "peerworkflowitem", "name": "peer workflow item", "app_label": "assessment"}}, {"pk": 41, "model": "contenttypes.contenttype", "fields": {"model": "pendingemailchange", "name": "pending email change", "app_label": "student"}}, {"pk": 40, "model": "contenttypes.contenttype", "fields": {"model": "pendingnamechange", "name": "pending name change", "app_label": "student"}}, {"pk": 12, "model": "contenttypes.contenttype", "fields": {"model": "periodictask", "name": "periodic task", "app_label": "djcelery"}}, {"pk": 11, "model": "contenttypes.contenttype", "fields": {"model": "periodictasks", "name": "periodic tasks", "app_label": "djcelery"}}, {"pk": 1, "model": "contenttypes.contenttype", "fields": {"model": "permission", "name": "permission", "app_label": "auth"}}, {"pk": 157, "model": "contenttypes.contenttype", "fields": {"model": "profile", "name": "profile", "app_label": "edxval"}}, {"pk": 17, "model": "contenttypes.contenttype", "fields": {"model": "psychometricdata", "name": "psychometric data", "app_label": "psychometrics"}}, {"pk": 92, "model": "contenttypes.contenttype", "fields": {"model": "puzzlecomplete", "name": "puzzle complete", "app_label": "foldit"}}, {"pk": 51, "model": "contenttypes.contenttype", "fields": {"model": "ratelimitconfiguration", "name": "rate limit configuration", "app_label": "util"}}, {"pk": 75, "model": "contenttypes.contenttype", "fields": {"model": "refreshtoken", "name": "refresh token", "app_label": "oauth2"}}, {"pk": 39, "model": "contenttypes.contenttype", "fields": {"model": "registration", "name": "registration", "app_label": "student"}}, {"pk": 106, "model": "contenttypes.contenttype", "fields": {"model": "registrationcoderedemption", "name": "registration code redemption", "app_label": "shoppingcart"}}, {"pk": 123, "model": "contenttypes.contenttype", "fields": {"model": "restrictedcourse", "name": "restricted course", "app_label": "embargo"}}, {"pk": 82, "model": "contenttypes.contenttype", "fields": {"model": "reusableplugin", "name": "reusable plugin", "app_label": "wiki"}}, {"pk": 84, "model": "contenttypes.contenttype", "fields": {"model": "revisionplugin", "name": "revision plugin", "app_label": "wiki"}}, {"pk": 85, "model": "contenttypes.contenttype", "fields": {"model": "revisionpluginrevision", "name": "revision plugin revision", "app_label": "wiki"}}, {"pk": 138, "model": "contenttypes.contenttype", "fields": {"model": "rubric", "name": "rubric", "app_label": "assessment"}}, {"pk": 8, "model": "contenttypes.contenttype", "fields": {"model": "tasksetmeta", "name": "saved group result", "app_label": "djcelery"}}, {"pk": 91, "model": "contenttypes.contenttype", "fields": {"model": "score", "name": "score", "app_label": "foldit"}}, {"pk": 136, "model": "contenttypes.contenttype", "fields": {"model": "score", "name": "score", "app_label": "submissions"}}, {"pk": 137, "model": "contenttypes.contenttype", "fields": {"model": "scoresummary", "name": "score summary", "app_label": "submissions"}}, {"pk": 16, "model": "contenttypes.contenttype", "fields": {"model": "servercircuit", "name": "server circuit", "app_label": "circuit"}}, {"pk": 5, "model": "contenttypes.contenttype", "fields": {"model": "session", "name": "session", "app_label": "sessions"}}, {"pk": 88, "model": "contenttypes.contenttype", "fields": {"model": "settings", "name": "settings", "app_label": "django_notify"}}, {"pk": 83, "model": "contenttypes.contenttype", "fields": {"model": "simpleplugin", "name": "simple plugin", "app_label": "wiki"}}, {"pk": 6, "model": "contenttypes.contenttype", "fields": {"model": "site", "name": "site", "app_label": "sites"}}, {"pk": 118, "model": "contenttypes.contenttype", "fields": {"model": "softwaresecurephotoverification", "name": "software secure photo verification", "app_label": "verify_student"}}, {"pk": 94, "model": "contenttypes.contenttype", "fields": {"model": "splashconfig", "name": "splash config", "app_label": "splash"}}, {"pk": 134, "model": "contenttypes.contenttype", "fields": {"model": "studentitem", "name": "student item", "app_label": "submissions"}}, {"pk": 27, "model": "contenttypes.contenttype", "fields": {"model": "studentmodule", "name": "student module", "app_label": "courseware"}}, {"pk": 28, "model": "contenttypes.contenttype", "fields": {"model": "studentmodulehistory", "name": "student module history", "app_label": "courseware"}}, {"pk": 148, "model": "contenttypes.contenttype", "fields": {"model": "studenttrainingworkflow", "name": "student training workflow", "app_label": "assessment"}}, {"pk": 149, "model": "contenttypes.contenttype", "fields": {"model": "studenttrainingworkflowitem", "name": "student training workflow item", "app_label": "assessment"}}, {"pk": 169, "model": "contenttypes.contenttype", "fields": {"model": "studioconfig", "name": "studio config", "app_label": "xblock_config"}}, {"pk": 135, "model": "contenttypes.contenttype", "fields": {"model": "submission", "name": "submission", "app_label": "submissions"}}, {"pk": 89, "model": "contenttypes.contenttype", "fields": {"model": "subscription", "name": "subscription", "app_label": "django_notify"}}, {"pk": 161, "model": "contenttypes.contenttype", "fields": {"model": "subtitle", "name": "subtitle", "app_label": "edxval"}}, {"pk": 131, "model": "contenttypes.contenttype", "fields": {"model": "surveyanswer", "name": "survey answer", "app_label": "survey"}}, {"pk": 130, "model": "contenttypes.contenttype", "fields": {"model": "surveyform", "name": "survey form", "app_label": "survey"}}, {"pk": 14, "model": "contenttypes.contenttype", "fields": {"model": "taskstate", "name": "task", "app_label": "djcelery"}}, {"pk": 7, "model": "contenttypes.contenttype", "fields": {"model": "taskmeta", "name": "task state", "app_label": "djcelery"}}, {"pk": 50, "model": "contenttypes.contenttype", "fields": {"model": "trackinglog", "name": "tracking log", "app_label": "track"}}, {"pk": 147, "model": "contenttypes.contenttype", "fields": {"model": "trainingexample", "name": "training example", "app_label": "assessment"}}, {"pk": 76, "model": "contenttypes.contenttype", "fields": {"model": "trustedclient", "name": "trusted client", "app_label": "oauth2_provider"}}, {"pk": 87, "model": "contenttypes.contenttype", "fields": {"model": "notificationtype", "name": "type", "app_label": "django_notify"}}, {"pk": 80, "model": "contenttypes.contenttype", "fields": {"model": "urlpath", "name": "URL path", "app_label": "wiki"}}, {"pk": 3, "model": "contenttypes.contenttype", "fields": {"model": "user", "name": "user", "app_label": "auth"}}, {"pk": 96, "model": "contenttypes.contenttype", "fields": {"model": "usercoursetag", "name": "user course tag", "app_label": "user_api"}}, {"pk": 61, "model": "contenttypes.contenttype", "fields": {"model": "userlicense", "name": "user license", "app_label": "licenses"}}, {"pk": 166, "model": "contenttypes.contenttype", "fields": {"model": "usermilestone", "name": "user milestone", "app_label": "milestones"}}, {"pk": 20, "model": "contenttypes.contenttype", "fields": {"model": "useropenid", "name": "user open id", "app_label": "django_openid_auth"}}, {"pk": 97, "model": "contenttypes.contenttype", "fields": {"model": "userorgtag", "name": "user org tag", "app_label": "user_api"}}, {"pk": 95, "model": "contenttypes.contenttype", "fields": {"model": "userpreference", "name": "user preference", "app_label": "user_api"}}, {"pk": 36, "model": "contenttypes.contenttype", "fields": {"model": "userprofile", "name": "user profile", "app_label": "student"}}, {"pk": 37, "model": "contenttypes.contenttype", "fields": {"model": "usersignupsource", "name": "user signup source", "app_label": "student"}}, {"pk": 23, "model": "contenttypes.contenttype", "fields": {"model": "usersocialauth", "name": "user social auth", "app_label": "default"}}, {"pk": 35, "model": "contenttypes.contenttype", "fields": {"model": "userstanding", "name": "user standing", "app_label": "student"}}, {"pk": 38, "model": "contenttypes.contenttype", "fields": {"model": "usertestgroup", "name": "user test group", "app_label": "student"}}, {"pk": 158, "model": "contenttypes.contenttype", "fields": {"model": "video", "name": "video", "app_label": "edxval"}}, {"pk": 167, "model": "contenttypes.contenttype", "fields": {"model": "videouploadconfig", "name": "video upload config", "app_label": "contentstore"}}, {"pk": 13, "model": "contenttypes.contenttype", "fields": {"model": "workerstate", "name": "worker", "app_label": "djcelery"}}, {"pk": 132, "model": "contenttypes.contenttype", "fields": {"model": "xblockasidesconfig", "name": "x block asides config", "app_label": "lms_xblock"}}, {"pk": 31, "model": "contenttypes.contenttype", "fields": {"model": "xmodulestudentinfofield", "name": "x module student info field", "app_label": "courseware"}}, {"pk": 30, "model": "contenttypes.contenttype", "fields": {"model": "xmodulestudentprefsfield", "name": "x module student prefs field", "app_label": "courseware"}}, {"pk": 29, "model": "contenttypes.contenttype", "fields": {"model": "xmoduleuserstatesummaryfield", "name": "x module user state summary field", "app_label": "courseware"}}, {"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}, {"pk": 1, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:39Z", "app_name": "courseware", "migration": "0001_initial"}}, {"pk": 2, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:39Z", "app_name": "courseware", "migration": "0002_add_indexes"}}, {"pk": 3, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:39Z", "app_name": "courseware", "migration": "0003_done_grade_cache"}}, {"pk": 4, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:39Z", "app_name": "courseware", "migration": "0004_add_field_studentmodule_course_id"}}, {"pk": 5, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:39Z", "app_name": "courseware", "migration": "0005_auto__add_offlinecomputedgrade__add_unique_offlinecomputedgrade_user_c"}}, {"pk": 6, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:39Z", "app_name": "courseware", "migration": "0006_create_student_module_history"}}, {"pk": 7, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:39Z", "app_name": "courseware", "migration": "0007_allow_null_version_in_history"}}, {"pk": 8, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:40Z", "app_name": "courseware", "migration": "0008_add_xmodule_storage"}}, {"pk": 9, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:40Z", "app_name": "courseware", "migration": "0009_add_field_default"}}, {"pk": 10, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:40Z", "app_name": "courseware", "migration": "0010_rename_xblock_field_content_to_user_state_summary"}}, {"pk": 11, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:40Z", "app_name": "student", "migration": "0001_initial"}}, {"pk": 12, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:40Z", "app_name": "student", "migration": "0002_text_to_varchar_and_indexes"}}, {"pk": 13, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:40Z", "app_name": "student", "migration": "0003_auto__add_usertestgroup"}}, {"pk": 14, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0004_add_email_index"}}, {"pk": 15, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0005_name_change"}}, {"pk": 16, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0006_expand_meta_field"}}, {"pk": 17, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0007_convert_to_utf8"}}, {"pk": 18, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0008__auto__add_courseregistration"}}, {"pk": 19, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0009_auto__del_courseregistration__add_courseenrollment"}}, {"pk": 20, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0010_auto__chg_field_courseenrollment_course_id"}}, {"pk": 21, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0011_auto__chg_field_courseenrollment_user__del_unique_courseenrollment_use"}}, {"pk": 22, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0012_auto__add_field_userprofile_gender__add_field_userprofile_date_of_birt"}}, {"pk": 23, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0013_auto__chg_field_userprofile_meta"}}, {"pk": 24, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0014_auto__del_courseenrollment"}}, {"pk": 25, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0015_auto__add_courseenrollment__add_unique_courseenrollment_user_course_id"}}, {"pk": 26, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0016_auto__add_field_courseenrollment_date__chg_field_userprofile_country"}}, {"pk": 27, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0017_rename_date_to_created"}}, {"pk": 28, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0018_auto"}}, {"pk": 29, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:41Z", "app_name": "student", "migration": "0019_create_approved_demographic_fields_fall_2012"}}, {"pk": 30, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0020_add_test_center_user"}}, {"pk": 31, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0021_remove_askbot"}}, {"pk": 32, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0022_auto__add_courseenrollmentallowed__add_unique_courseenrollmentallowed_"}}, {"pk": 33, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0023_add_test_center_registration"}}, {"pk": 34, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0024_add_allow_certificate"}}, {"pk": 35, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0025_auto__add_field_courseenrollmentallowed_auto_enroll"}}, {"pk": 36, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0026_auto__remove_index_student_testcenterregistration_accommodation_request"}}, {"pk": 37, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0027_add_active_flag_and_mode_to_courseware_enrollment"}}, {"pk": 38, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0028_auto__add_userstanding"}}, {"pk": 39, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0029_add_lookup_table_between_user_and_anonymous_student_id"}}, {"pk": 40, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0029_remove_pearson"}}, {"pk": 41, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0030_auto__chg_field_anonymoususerid_anonymous_user_id"}}, {"pk": 42, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0031_drop_student_anonymoususerid_temp_archive"}}, {"pk": 43, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:42Z", "app_name": "student", "migration": "0032_add_field_UserProfile_country_add_field_UserProfile_city"}}, {"pk": 44, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:43Z", "app_name": "student", "migration": "0032_auto__add_loginfailures"}}, {"pk": 45, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:43Z", "app_name": "student", "migration": "0033_auto__add_passwordhistory"}}, {"pk": 46, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:43Z", "app_name": "student", "migration": "0034_auto__add_courseaccessrole"}}, {"pk": 47, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:44Z", "app_name": "student", "migration": "0035_access_roles"}}, {"pk": 48, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:44Z", "app_name": "student", "migration": "0036_access_roles_orgless"}}, {"pk": 49, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:44Z", "app_name": "student", "migration": "0037_auto__add_courseregistrationcode"}}, {"pk": 50, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:44Z", "app_name": "student", "migration": "0038_auto__add_usersignupsource"}}, {"pk": 51, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:44Z", "app_name": "student", "migration": "0039_auto__del_courseregistrationcode"}}, {"pk": 52, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:44Z", "app_name": "student", "migration": "0040_auto__del_field_usersignupsource_user_id__add_field_usersignupsource_u"}}, {"pk": 53, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:44Z", "app_name": "student", "migration": "0041_add_dashboard_config"}}, {"pk": 54, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:44Z", "app_name": "student", "migration": "0042_grant_sales_admin_roles"}}, {"pk": 55, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:44Z", "app_name": "student", "migration": "0043_auto__add_linkedinaddtoprofileconfiguration"}}, {"pk": 56, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:44Z", "app_name": "student", "migration": "0044_linkedin_add_company_identifier"}}, {"pk": 57, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:45Z", "app_name": "student", "migration": "0045_add_trk_partner_to_linkedin_config"}}, {"pk": 58, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:45Z", "app_name": "student", "migration": "0046_auto__add_entranceexamconfiguration__add_unique_entranceexamconfigurat"}}, {"pk": 59, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:45Z", "app_name": "track", "migration": "0001_initial"}}, {"pk": 60, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:45Z", "app_name": "track", "migration": "0002_auto__add_field_trackinglog_host__chg_field_trackinglog_event_type__ch"}}, {"pk": 61, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:45Z", "app_name": "util", "migration": "0001_initial"}}, {"pk": 62, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:45Z", "app_name": "util", "migration": "0002_default_rate_limit_config"}}, {"pk": 63, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0001_added_generatedcertificates"}}, {"pk": 64, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0002_auto__add_field_generatedcertificate_download_url"}}, {"pk": 65, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0003_auto__add_field_generatedcertificate_enabled"}}, {"pk": 66, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0004_auto__add_field_generatedcertificate_graded_certificate_id__add_field_"}}, {"pk": 67, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0005_auto__add_field_generatedcertificate_name"}}, {"pk": 68, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0006_auto__chg_field_generatedcertificate_certificate_id"}}, {"pk": 69, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0007_auto__add_revokedcertificate"}}, {"pk": 70, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0008_auto__del_revokedcertificate__del_field_generatedcertificate_name__add"}}, {"pk": 71, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0009_auto__del_field_generatedcertificate_graded_download_url__del_field_ge"}}, {"pk": 72, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0010_auto__del_field_generatedcertificate_enabled__add_field_generatedcerti"}}, {"pk": 73, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0011_auto__del_field_generatedcertificate_certificate_id__add_field_generat"}}, {"pk": 74, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0012_auto__add_field_generatedcertificate_name__add_field_generatedcertific"}}, {"pk": 75, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0013_auto__add_field_generatedcertificate_error_reason"}}, {"pk": 76, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:46Z", "app_name": "certificates", "migration": "0014_adding_whitelist"}}, {"pk": 77, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:47Z", "app_name": "certificates", "migration": "0015_adding_mode_for_verified_certs"}}, {"pk": 78, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:47Z", "app_name": "certificates", "migration": "0016_change_course_key_fields"}}, {"pk": 79, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:47Z", "app_name": "certificates", "migration": "0017_auto__add_certificategenerationconfiguration"}}, {"pk": 80, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:47Z", "app_name": "certificates", "migration": "0018_add_example_cert_models"}}, {"pk": 81, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:47Z", "app_name": "certificates", "migration": "0019_auto__add_certificatehtmlviewconfiguration"}}, {"pk": 82, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:47Z", "app_name": "certificates", "migration": "0020_certificatehtmlviewconfiguration_data"}}, {"pk": 83, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:47Z", "app_name": "instructor_task", "migration": "0001_initial"}}, {"pk": 84, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:47Z", "app_name": "instructor_task", "migration": "0002_add_subtask_field"}}, {"pk": 85, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:48Z", "app_name": "licenses", "migration": "0001_initial"}}, {"pk": 86, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:48Z", "app_name": "course_groups", "migration": "0001_initial"}}, {"pk": 87, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:48Z", "app_name": "course_groups", "migration": "0002_add_model_CourseUserGroupPartitionGroup"}}, {"pk": 88, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:48Z", "app_name": "course_groups", "migration": "0003_auto__add_coursecohort__add_coursecohortssettings"}}, {"pk": 89, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:48Z", "app_name": "course_groups", "migration": "0004_auto__del_field_coursecohortssettings_cohorted_discussions__add_field_"}}, {"pk": 90, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:49Z", "app_name": "bulk_email", "migration": "0001_initial"}}, {"pk": 91, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:49Z", "app_name": "bulk_email", "migration": "0002_change_field_names"}}, {"pk": 92, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:49Z", "app_name": "bulk_email", "migration": "0003_add_optout_user"}}, {"pk": 93, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:49Z", "app_name": "bulk_email", "migration": "0004_migrate_optout_user"}}, {"pk": 94, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:49Z", "app_name": "bulk_email", "migration": "0005_remove_optout_email"}}, {"pk": 95, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:49Z", "app_name": "bulk_email", "migration": "0006_add_course_email_template"}}, {"pk": 96, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:49Z", "app_name": "bulk_email", "migration": "0007_load_course_email_template"}}, {"pk": 97, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:49Z", "app_name": "bulk_email", "migration": "0008_add_course_authorizations"}}, {"pk": 98, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:49Z", "app_name": "bulk_email", "migration": "0009_force_unique_course_ids"}}, {"pk": 99, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:49Z", "app_name": "bulk_email", "migration": "0010_auto__chg_field_optout_course_id__add_field_courseemail_template_name_"}}, {"pk": 100, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:50Z", "app_name": "branding", "migration": "0001_initial"}}, {"pk": 101, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:50Z", "app_name": "external_auth", "migration": "0001_initial"}}, {"pk": 102, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:50Z", "app_name": "oauth2", "migration": "0001_initial"}}, {"pk": 103, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:50Z", "app_name": "oauth2", "migration": "0002_auto__chg_field_client_user"}}, {"pk": 104, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:51Z", "app_name": "oauth2", "migration": "0003_auto__add_field_client_name"}}, {"pk": 105, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:51Z", "app_name": "oauth2", "migration": "0004_auto__add_index_accesstoken_token"}}, {"pk": 106, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:51Z", "app_name": "oauth2_provider", "migration": "0001_initial"}}, {"pk": 107, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:52Z", "app_name": "wiki", "migration": "0001_initial"}}, {"pk": 108, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:52Z", "app_name": "wiki", "migration": "0002_auto__add_field_articleplugin_created"}}, {"pk": 109, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:52Z", "app_name": "wiki", "migration": "0003_auto__add_field_urlpath_article"}}, {"pk": 110, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:52Z", "app_name": "wiki", "migration": "0004_populate_urlpath__article"}}, {"pk": 111, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:52Z", "app_name": "wiki", "migration": "0005_auto__chg_field_urlpath_article"}}, {"pk": 112, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:52Z", "app_name": "wiki", "migration": "0006_auto__add_attachmentrevision__add_image__add_attachment"}}, {"pk": 113, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:52Z", "app_name": "wiki", "migration": "0007_auto__add_articlesubscription"}}, {"pk": 114, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:52Z", "app_name": "wiki", "migration": "0008_auto__add_simpleplugin__add_revisionpluginrevision__add_imagerevision_"}}, {"pk": 115, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:52Z", "app_name": "wiki", "migration": "0009_auto__add_field_imagerevision_width__add_field_imagerevision_height"}}, {"pk": 116, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:53Z", "app_name": "wiki", "migration": "0010_auto__chg_field_imagerevision_image"}}, {"pk": 117, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:53Z", "app_name": "wiki", "migration": "0011_auto__chg_field_imagerevision_width__chg_field_imagerevision_height"}}, {"pk": 118, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:53Z", "app_name": "django_notify", "migration": "0001_initial"}}, {"pk": 119, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:53Z", "app_name": "notifications", "migration": "0001_initial"}}, {"pk": 120, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:54Z", "app_name": "foldit", "migration": "0001_initial"}}, {"pk": 121, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:54Z", "app_name": "django_comment_client", "migration": "0001_initial"}}, {"pk": 122, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:54Z", "app_name": "django_comment_common", "migration": "0001_initial"}}, {"pk": 123, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:55Z", "app_name": "notes", "migration": "0001_initial"}}, {"pk": 124, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:55Z", "app_name": "splash", "migration": "0001_initial"}}, {"pk": 125, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:55Z", "app_name": "splash", "migration": "0002_auto__add_field_splashconfig_unaffected_url_paths"}}, {"pk": 126, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:56Z", "app_name": "user_api", "migration": "0001_initial"}}, {"pk": 127, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:56Z", "app_name": "user_api", "migration": "0002_auto__add_usercoursetags__add_unique_usercoursetags_user_course_id_key"}}, {"pk": 128, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:56Z", "app_name": "user_api", "migration": "0003_rename_usercoursetags"}}, {"pk": 129, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:56Z", "app_name": "user_api", "migration": "0004_auto__add_userorgtag__add_unique_userorgtag_user_org_key__chg_field_us"}}, {"pk": 130, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:56Z", "app_name": "shoppingcart", "migration": "0001_initial"}}, {"pk": 131, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:56Z", "app_name": "shoppingcart", "migration": "0002_auto__add_field_paidcourseregistration_mode"}}, {"pk": 132, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:56Z", "app_name": "shoppingcart", "migration": "0003_auto__del_field_orderitem_line_cost"}}, {"pk": 133, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:56Z", "app_name": "shoppingcart", "migration": "0004_auto__add_field_orderitem_fulfilled_time"}}, {"pk": 134, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:57Z", "app_name": "shoppingcart", "migration": "0005_auto__add_paidcourseregistrationannotation__add_field_orderitem_report"}}, {"pk": 135, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:57Z", "app_name": "shoppingcart", "migration": "0006_auto__add_field_order_refunded_time__add_field_orderitem_refund_reques"}}, {"pk": 136, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:57Z", "app_name": "shoppingcart", "migration": "0007_auto__add_field_orderitem_service_fee"}}, {"pk": 137, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:57Z", "app_name": "shoppingcart", "migration": "0008_auto__add_coupons__add_couponredemption__chg_field_certificateitem_cou"}}, {"pk": 138, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:57Z", "app_name": "shoppingcart", "migration": "0009_auto__del_coupons__add_courseregistrationcode__add_coupon__chg_field_c"}}, {"pk": 139, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:57Z", "app_name": "shoppingcart", "migration": "0010_auto__add_registrationcoderedemption__del_field_courseregistrationcode"}}, {"pk": 140, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:57Z", "app_name": "shoppingcart", "migration": "0011_auto__add_invoice__add_field_courseregistrationcode_invoice"}}, {"pk": 141, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:58Z", "app_name": "shoppingcart", "migration": "0012_auto__del_field_courseregistrationcode_transaction_group_name__del_fie"}}, {"pk": 142, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:58Z", "app_name": "shoppingcart", "migration": "0013_auto__add_field_invoice_is_valid"}}, {"pk": 143, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:58Z", "app_name": "shoppingcart", "migration": "0014_auto__del_field_invoice_tax_id__add_field_invoice_address_line_1__add_"}}, {"pk": 144, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:58Z", "app_name": "shoppingcart", "migration": "0015_auto__del_field_invoice_purchase_order_number__del_field_invoice_compa"}}, {"pk": 145, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:58Z", "app_name": "shoppingcart", "migration": "0016_auto__del_field_invoice_company_email__del_field_invoice_company_refer"}}, {"pk": 146, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:58Z", "app_name": "shoppingcart", "migration": "0017_auto__add_field_courseregistrationcode_order__chg_field_registrationco"}}, {"pk": 147, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:58Z", "app_name": "shoppingcart", "migration": "0018_auto__add_donation"}}, {"pk": 148, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:58Z", "app_name": "shoppingcart", "migration": "0019_auto__add_donationconfiguration"}}, {"pk": 149, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:58Z", "app_name": "shoppingcart", "migration": "0020_auto__add_courseregcodeitem__add_courseregcodeitemannotation__add_fiel"}}, {"pk": 150, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:58Z", "app_name": "shoppingcart", "migration": "0021_auto__add_field_orderitem_created__add_field_orderitem_modified"}}, {"pk": 151, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:59Z", "app_name": "shoppingcart", "migration": "0022_auto__add_field_registrationcoderedemption_course_enrollment__add_fiel"}}, {"pk": 152, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:59Z", "app_name": "shoppingcart", "migration": "0023_auto__add_field_coupon_expiration_date"}}, {"pk": 153, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:59Z", "app_name": "shoppingcart", "migration": "0024_auto__add_field_courseregistrationcode_mode_slug"}}, {"pk": 154, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:59Z", "app_name": "shoppingcart", "migration": "0025_update_invoice_models"}}, {"pk": 155, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:59Z", "app_name": "shoppingcart", "migration": "0026_migrate_invoices"}}, {"pk": 156, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:25:59Z", "app_name": "shoppingcart", "migration": "0027_add_invoice_history"}}, {"pk": 157, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "course_modes", "migration": "0001_initial"}}, {"pk": 158, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "course_modes", "migration": "0002_auto__add_field_coursemode_currency"}}, {"pk": 159, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "course_modes", "migration": "0003_auto__add_unique_coursemode_course_id_currency_mode_slug"}}, {"pk": 160, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "course_modes", "migration": "0004_auto__add_field_coursemode_expiration_date"}}, {"pk": 161, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "course_modes", "migration": "0005_auto__add_field_coursemode_expiration_datetime"}}, {"pk": 162, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "course_modes", "migration": "0006_expiration_date_to_datetime"}}, {"pk": 163, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "course_modes", "migration": "0007_add_description"}}, {"pk": 164, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "course_modes", "migration": "0007_auto__add_coursemodesarchive__chg_field_coursemode_course_id"}}, {"pk": 165, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "course_modes", "migration": "0008_auto__del_field_coursemodesarchive_description__add_field_coursemode_s"}}, {"pk": 166, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "verify_student", "migration": "0001_initial"}}, {"pk": 167, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "verify_student", "migration": "0002_auto__add_field_softwaresecurephotoverification_window"}}, {"pk": 168, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:00Z", "app_name": "verify_student", "migration": "0003_auto__add_field_softwaresecurephotoverification_display"}}, {"pk": 169, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:01Z", "app_name": "dark_lang", "migration": "0001_initial"}}, {"pk": 170, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:01Z", "app_name": "dark_lang", "migration": "0002_enable_on_install"}}, {"pk": 171, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:01Z", "app_name": "reverification", "migration": "0001_initial"}}, {"pk": 172, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:01Z", "app_name": "embargo", "migration": "0001_initial"}}, {"pk": 173, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:02Z", "app_name": "embargo", "migration": "0002_add_country_access_models"}}, {"pk": 174, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:02Z", "app_name": "embargo", "migration": "0003_add_countries"}}, {"pk": 175, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:02Z", "app_name": "embargo", "migration": "0004_migrate_embargo_config"}}, {"pk": 176, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:02Z", "app_name": "embargo", "migration": "0005_add_courseaccessrulehistory"}}, {"pk": 177, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:03Z", "app_name": "course_action_state", "migration": "0001_initial"}}, {"pk": 178, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:03Z", "app_name": "course_action_state", "migration": "0002_add_rerun_display_name"}}, {"pk": 179, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:03Z", "app_name": "mobile_api", "migration": "0001_initial"}}, {"pk": 180, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:04Z", "app_name": "survey", "migration": "0001_initial"}}, {"pk": 181, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:04Z", "app_name": "lms_xblock", "migration": "0001_initial"}}, {"pk": 182, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:04Z", "app_name": "course_structures", "migration": "0001_initial"}}, {"pk": 183, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:05Z", "app_name": "submissions", "migration": "0001_initial"}}, {"pk": 184, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:05Z", "app_name": "submissions", "migration": "0002_auto__add_scoresummary"}}, {"pk": 185, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:05Z", "app_name": "submissions", "migration": "0003_auto__del_field_submission_answer__add_field_submission_raw_answer"}}, {"pk": 186, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:05Z", "app_name": "submissions", "migration": "0004_auto__add_field_score_reset"}}, {"pk": 187, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:06Z", "app_name": "assessment", "migration": "0001_initial"}}, {"pk": 188, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:06Z", "app_name": "assessment", "migration": "0002_auto__add_assessmentfeedbackoption__del_field_assessmentfeedback_feedb"}}, {"pk": 189, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:06Z", "app_name": "assessment", "migration": "0003_add_index_pw_course_item_student"}}, {"pk": 190, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:06Z", "app_name": "assessment", "migration": "0004_auto__add_field_peerworkflow_graded_count"}}, {"pk": 191, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:06Z", "app_name": "assessment", "migration": "0005_auto__del_field_peerworkflow_graded_count__add_field_peerworkflow_grad"}}, {"pk": 192, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:06Z", "app_name": "assessment", "migration": "0006_auto__add_field_assessmentpart_feedback"}}, {"pk": 193, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:06Z", "app_name": "assessment", "migration": "0007_auto__chg_field_assessmentpart_feedback"}}, {"pk": 194, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:06Z", "app_name": "assessment", "migration": "0008_student_training"}}, {"pk": 195, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:06Z", "app_name": "assessment", "migration": "0009_auto__add_unique_studenttrainingworkflowitem_order_num_workflow"}}, {"pk": 196, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:06Z", "app_name": "assessment", "migration": "0010_auto__add_unique_studenttrainingworkflow_submission_uuid"}}, {"pk": 197, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0011_ai_training"}}, {"pk": 198, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0012_move_algorithm_id_to_classifier_set"}}, {"pk": 199, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0013_auto__add_field_aigradingworkflow_essay_text"}}, {"pk": 200, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0014_auto__add_field_aitrainingworkflow_item_id__add_field_aitrainingworkfl"}}, {"pk": 201, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0015_auto__add_unique_aitrainingworkflow_uuid__add_unique_aigradingworkflow"}}, {"pk": 202, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0016_auto__add_field_aiclassifierset_course_id__add_field_aiclassifierset_i"}}, {"pk": 203, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0016_auto__add_field_rubric_structure_hash"}}, {"pk": 204, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0017_rubric_structure_hash"}}, {"pk": 205, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0018_auto__add_field_assessmentpart_criterion"}}, {"pk": 206, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0019_assessmentpart_criterion_field"}}, {"pk": 207, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0020_assessmentpart_criterion_not_null"}}, {"pk": 208, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:07Z", "app_name": "assessment", "migration": "0021_assessmentpart_option_nullable"}}, {"pk": 209, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:08Z", "app_name": "assessment", "migration": "0022__add_label_fields"}}, {"pk": 210, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:08Z", "app_name": "assessment", "migration": "0023_assign_criteria_and_option_labels"}}, {"pk": 211, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:08Z", "app_name": "assessment", "migration": "0024_auto__chg_field_assessmentpart_criterion"}}, {"pk": 212, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:08Z", "app_name": "assessment", "migration": "0025_auto__add_field_peerworkflow_cancelled_at"}}, {"pk": 213, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:08Z", "app_name": "workflow", "migration": "0001_initial"}}, {"pk": 214, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:08Z", "app_name": "workflow", "migration": "0002_auto__add_field_assessmentworkflow_course_id__add_field_assessmentwork"}}, {"pk": 215, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:08Z", "app_name": "workflow", "migration": "0003_auto__add_assessmentworkflowstep"}}, {"pk": 216, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:08Z", "app_name": "workflow", "migration": "0004_auto__add_assessmentworkflowcancellation"}}, {"pk": 217, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:09Z", "app_name": "edxval", "migration": "0001_initial"}}, {"pk": 218, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:09Z", "app_name": "edxval", "migration": "0002_default_profiles"}}, {"pk": 219, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:09Z", "app_name": "edxval", "migration": "0003_status_and_created_fields"}}, {"pk": 220, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:09Z", "app_name": "edxval", "migration": "0004_remove_profile_fields"}}, {"pk": 221, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:10Z", "app_name": "milestones", "migration": "0001_initial"}}, {"pk": 222, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:10Z", "app_name": "milestones", "migration": "0002_seed_relationship_types"}}, {"pk": 223, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:10Z", "app_name": "django_extensions", "migration": "0001_empty"}}, {"pk": 224, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:15Z", "app_name": "contentstore", "migration": "0001_initial"}}, {"pk": 225, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:15Z", "app_name": "contentstore", "migration": "0002_auto__del_field_videouploadconfig_status_whitelist"}}, {"pk": 226, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:15Z", "app_name": "course_creators", "migration": "0001_initial"}}, {"pk": 227, "model": "south.migrationhistory", "fields": {"applied": "2015-03-31T06:26:16Z", "app_name": "xblock_config", "migration": "0001_initial"}}, {"pk": 5, "model": "embargo.country", "fields": {"country": "AD"}}, {"pk": 233, "model": "embargo.country", "fields": {"country": "AE"}}, {"pk": 1, "model": "embargo.country", "fields": {"country": "AF"}}, {"pk": 9, "model": "embargo.country", "fields": {"country": "AG"}}, {"pk": 7, "model": "embargo.country", "fields": {"country": "AI"}}, {"pk": 2, "model": "embargo.country", "fields": {"country": "AL"}}, {"pk": 11, "model": "embargo.country", "fields": {"country": "AM"}}, {"pk": 6, "model": "embargo.country", "fields": {"country": "AO"}}, {"pk": 8, "model": "embargo.country", "fields": {"country": "AQ"}}, {"pk": 10, "model": "embargo.country", "fields": {"country": "AR"}}, {"pk": 4, "model": "embargo.country", "fields": {"country": "AS"}}, {"pk": 14, "model": "embargo.country", "fields": {"country": "AT"}}, {"pk": 13, "model": "embargo.country", "fields": {"country": "AU"}}, {"pk": 12, "model": "embargo.country", "fields": {"country": "AW"}}, {"pk": 249, "model": "embargo.country", "fields": {"country": "AX"}}, {"pk": 15, "model": "embargo.country", "fields": {"country": "AZ"}}, {"pk": 28, "model": "embargo.country", "fields": {"country": "BA"}}, {"pk": 19, "model": "embargo.country", "fields": {"country": "BB"}}, {"pk": 18, "model": "embargo.country", "fields": {"country": "BD"}}, {"pk": 21, "model": "embargo.country", "fields": {"country": "BE"}}, {"pk": 35, "model": "embargo.country", "fields": {"country": "BF"}}, {"pk": 34, "model": "embargo.country", "fields": {"country": "BG"}}, {"pk": 17, "model": "embargo.country", "fields": {"country": "BH"}}, {"pk": 36, "model": "embargo.country", "fields": {"country": "BI"}}, {"pk": 23, "model": "embargo.country", "fields": {"country": "BJ"}}, {"pk": 184, "model": "embargo.country", "fields": {"country": "BL"}}, {"pk": 24, "model": "embargo.country", "fields": {"country": "BM"}}, {"pk": 33, "model": "embargo.country", "fields": {"country": "BN"}}, {"pk": 26, "model": "embargo.country", "fields": {"country": "BO"}}, {"pk": 27, "model": "embargo.country", "fields": {"country": "BQ"}}, {"pk": 31, "model": "embargo.country", "fields": {"country": "BR"}}, {"pk": 16, "model": "embargo.country", "fields": {"country": "BS"}}, {"pk": 25, "model": "embargo.country", "fields": {"country": "BT"}}, {"pk": 30, "model": "embargo.country", "fields": {"country": "BV"}}, {"pk": 29, "model": "embargo.country", "fields": {"country": "BW"}}, {"pk": 20, "model": "embargo.country", "fields": {"country": "BY"}}, {"pk": 22, "model": "embargo.country", "fields": {"country": "BZ"}}, {"pk": 39, "model": "embargo.country", "fields": {"country": "CA"}}, {"pk": 47, "model": "embargo.country", "fields": {"country": "CC"}}, {"pk": 51, "model": "embargo.country", "fields": {"country": "CD"}}, {"pk": 42, "model": "embargo.country", "fields": {"country": "CF"}}, {"pk": 50, "model": "embargo.country", "fields": {"country": "CG"}}, {"pk": 215, "model": "embargo.country", "fields": {"country": "CH"}}, {"pk": 59, "model": "embargo.country", "fields": {"country": "CI"}}, {"pk": 52, "model": "embargo.country", "fields": {"country": "CK"}}, {"pk": 44, "model": "embargo.country", "fields": {"country": "CL"}}, {"pk": 38, "model": "embargo.country", "fields": {"country": "CM"}}, {"pk": 45, "model": "embargo.country", "fields": {"country": "CN"}}, {"pk": 48, "model": "embargo.country", "fields": {"country": "CO"}}, {"pk": 53, "model": "embargo.country", "fields": {"country": "CR"}}, {"pk": 55, "model": "embargo.country", "fields": {"country": "CU"}}, {"pk": 40, "model": "embargo.country", "fields": {"country": "CV"}}, {"pk": 56, "model": "embargo.country", "fields": {"country": "CW"}}, {"pk": 46, "model": "embargo.country", "fields": {"country": "CX"}}, {"pk": 57, "model": "embargo.country", "fields": {"country": "CY"}}, {"pk": 58, "model": "embargo.country", "fields": {"country": "CZ"}}, {"pk": 82, "model": "embargo.country", "fields": {"country": "DE"}}, {"pk": 61, "model": "embargo.country", "fields": {"country": "DJ"}}, {"pk": 60, "model": "embargo.country", "fields": {"country": "DK"}}, {"pk": 62, "model": "embargo.country", "fields": {"country": "DM"}}, {"pk": 63, "model": "embargo.country", "fields": {"country": "DO"}}, {"pk": 3, "model": "embargo.country", "fields": {"country": "DZ"}}, {"pk": 64, "model": "embargo.country", "fields": {"country": "EC"}}, {"pk": 69, "model": "embargo.country", "fields": {"country": "EE"}}, {"pk": 65, "model": "embargo.country", "fields": {"country": "EG"}}, {"pk": 245, "model": "embargo.country", "fields": {"country": "EH"}}, {"pk": 68, "model": "embargo.country", "fields": {"country": "ER"}}, {"pk": 208, "model": "embargo.country", "fields": {"country": "ES"}}, {"pk": 70, "model": "embargo.country", "fields": {"country": "ET"}}, {"pk": 74, "model": "embargo.country", "fields": {"country": "FI"}}, {"pk": 73, "model": "embargo.country", "fields": {"country": "FJ"}}, {"pk": 71, "model": "embargo.country", "fields": {"country": "FK"}}, {"pk": 144, "model": "embargo.country", "fields": {"country": "FM"}}, {"pk": 72, "model": "embargo.country", "fields": {"country": "FO"}}, {"pk": 75, "model": "embargo.country", "fields": {"country": "FR"}}, {"pk": 79, "model": "embargo.country", "fields": {"country": "GA"}}, {"pk": 234, "model": "embargo.country", "fields": {"country": "GB"}}, {"pk": 87, "model": "embargo.country", "fields": {"country": "GD"}}, {"pk": 81, "model": "embargo.country", "fields": {"country": "GE"}}, {"pk": 76, "model": "embargo.country", "fields": {"country": "GF"}}, {"pk": 91, "model": "embargo.country", "fields": {"country": "GG"}}, {"pk": 83, "model": "embargo.country", "fields": {"country": "GH"}}, {"pk": 84, "model": "embargo.country", "fields": {"country": "GI"}}, {"pk": 86, "model": "embargo.country", "fields": {"country": "GL"}}, {"pk": 80, "model": "embargo.country", "fields": {"country": "GM"}}, {"pk": 92, "model": "embargo.country", "fields": {"country": "GN"}}, {"pk": 88, "model": "embargo.country", "fields": {"country": "GP"}}, {"pk": 67, "model": "embargo.country", "fields": {"country": "GQ"}}, {"pk": 85, "model": "embargo.country", "fields": {"country": "GR"}}, {"pk": 206, "model": "embargo.country", "fields": {"country": "GS"}}, {"pk": 90, "model": "embargo.country", "fields": {"country": "GT"}}, {"pk": 89, "model": "embargo.country", "fields": {"country": "GU"}}, {"pk": 93, "model": "embargo.country", "fields": {"country": "GW"}}, {"pk": 94, "model": "embargo.country", "fields": {"country": "GY"}}, {"pk": 99, "model": "embargo.country", "fields": {"country": "HK"}}, {"pk": 96, "model": "embargo.country", "fields": {"country": "HM"}}, {"pk": 98, "model": "embargo.country", "fields": {"country": "HN"}}, {"pk": 54, "model": "embargo.country", "fields": {"country": "HR"}}, {"pk": 95, "model": "embargo.country", "fields": {"country": "HT"}}, {"pk": 100, "model": "embargo.country", "fields": {"country": "HU"}}, {"pk": 103, "model": "embargo.country", "fields": {"country": "ID"}}, {"pk": 106, "model": "embargo.country", "fields": {"country": "IE"}}, {"pk": 108, "model": "embargo.country", "fields": {"country": "IL"}}, {"pk": 107, "model": "embargo.country", "fields": {"country": "IM"}}, {"pk": 102, "model": "embargo.country", "fields": {"country": "IN"}}, {"pk": 32, "model": "embargo.country", "fields": {"country": "IO"}}, {"pk": 105, "model": "embargo.country", "fields": {"country": "IQ"}}, {"pk": 104, "model": "embargo.country", "fields": {"country": "IR"}}, {"pk": 101, "model": "embargo.country", "fields": {"country": "IS"}}, {"pk": 109, "model": "embargo.country", "fields": {"country": "IT"}}, {"pk": 112, "model": "embargo.country", "fields": {"country": "JE"}}, {"pk": 110, "model": "embargo.country", "fields": {"country": "JM"}}, {"pk": 113, "model": "embargo.country", "fields": {"country": "JO"}}, {"pk": 111, "model": "embargo.country", "fields": {"country": "JP"}}, {"pk": 115, "model": "embargo.country", "fields": {"country": "KE"}}, {"pk": 120, "model": "embargo.country", "fields": {"country": "KG"}}, {"pk": 37, "model": "embargo.country", "fields": {"country": "KH"}}, {"pk": 116, "model": "embargo.country", "fields": {"country": "KI"}}, {"pk": 49, "model": "embargo.country", "fields": {"country": "KM"}}, {"pk": 186, "model": "embargo.country", "fields": {"country": "KN"}}, {"pk": 117, "model": "embargo.country", "fields": {"country": "KP"}}, {"pk": 118, "model": "embargo.country", "fields": {"country": "KR"}}, {"pk": 119, "model": "embargo.country", "fields": {"country": "KW"}}, {"pk": 41, "model": "embargo.country", "fields": {"country": "KY"}}, {"pk": 114, "model": "embargo.country", "fields": {"country": "KZ"}}, {"pk": 121, "model": "embargo.country", "fields": {"country": "LA"}}, {"pk": 123, "model": "embargo.country", "fields": {"country": "LB"}}, {"pk": 187, "model": "embargo.country", "fields": {"country": "LC"}}, {"pk": 127, "model": "embargo.country", "fields": {"country": "LI"}}, {"pk": 209, "model": "embargo.country", "fields": {"country": "LK"}}, {"pk": 125, "model": "embargo.country", "fields": {"country": "LR"}}, {"pk": 124, "model": "embargo.country", "fields": {"country": "LS"}}, {"pk": 128, "model": "embargo.country", "fields": {"country": "LT"}}, {"pk": 129, "model": "embargo.country", "fields": {"country": "LU"}}, {"pk": 122, "model": "embargo.country", "fields": {"country": "LV"}}, {"pk": 126, "model": "embargo.country", "fields": {"country": "LY"}}, {"pk": 150, "model": "embargo.country", "fields": {"country": "MA"}}, {"pk": 146, "model": "embargo.country", "fields": {"country": "MC"}}, {"pk": 145, "model": "embargo.country", "fields": {"country": "MD"}}, {"pk": 148, "model": "embargo.country", "fields": {"country": "ME"}}, {"pk": 188, "model": "embargo.country", "fields": {"country": "MF"}}, {"pk": 132, "model": "embargo.country", "fields": {"country": "MG"}}, {"pk": 138, "model": "embargo.country", "fields": {"country": "MH"}}, {"pk": 131, "model": "embargo.country", "fields": {"country": "MK"}}, {"pk": 136, "model": "embargo.country", "fields": {"country": "ML"}}, {"pk": 152, "model": "embargo.country", "fields": {"country": "MM"}}, {"pk": 147, "model": "embargo.country", "fields": {"country": "MN"}}, {"pk": 130, "model": "embargo.country", "fields": {"country": "MO"}}, {"pk": 164, "model": "embargo.country", "fields": {"country": "MP"}}, {"pk": 139, "model": "embargo.country", "fields": {"country": "MQ"}}, {"pk": 140, "model": "embargo.country", "fields": {"country": "MR"}}, {"pk": 149, "model": "embargo.country", "fields": {"country": "MS"}}, {"pk": 137, "model": "embargo.country", "fields": {"country": "MT"}}, {"pk": 141, "model": "embargo.country", "fields": {"country": "MU"}}, {"pk": 135, "model": "embargo.country", "fields": {"country": "MV"}}, {"pk": 133, "model": "embargo.country", "fields": {"country": "MW"}}, {"pk": 143, "model": "embargo.country", "fields": {"country": "MX"}}, {"pk": 134, "model": "embargo.country", "fields": {"country": "MY"}}, {"pk": 151, "model": "embargo.country", "fields": {"country": "MZ"}}, {"pk": 153, "model": "embargo.country", "fields": {"country": "NA"}}, {"pk": 157, "model": "embargo.country", "fields": {"country": "NC"}}, {"pk": 160, "model": "embargo.country", "fields": {"country": "NE"}}, {"pk": 163, "model": "embargo.country", "fields": {"country": "NF"}}, {"pk": 161, "model": "embargo.country", "fields": {"country": "NG"}}, {"pk": 159, "model": "embargo.country", "fields": {"country": "NI"}}, {"pk": 156, "model": "embargo.country", "fields": {"country": "NL"}}, {"pk": 165, "model": "embargo.country", "fields": {"country": "NO"}}, {"pk": 155, "model": "embargo.country", "fields": {"country": "NP"}}, {"pk": 154, "model": "embargo.country", "fields": {"country": "NR"}}, {"pk": 162, "model": "embargo.country", "fields": {"country": "NU"}}, {"pk": 158, "model": "embargo.country", "fields": {"country": "NZ"}}, {"pk": 166, "model": "embargo.country", "fields": {"country": "OM"}}, {"pk": 170, "model": "embargo.country", "fields": {"country": "PA"}}, {"pk": 173, "model": "embargo.country", "fields": {"country": "PE"}}, {"pk": 77, "model": "embargo.country", "fields": {"country": "PF"}}, {"pk": 171, "model": "embargo.country", "fields": {"country": "PG"}}, {"pk": 174, "model": "embargo.country", "fields": {"country": "PH"}}, {"pk": 167, "model": "embargo.country", "fields": {"country": "PK"}}, {"pk": 176, "model": "embargo.country", "fields": {"country": "PL"}}, {"pk": 189, "model": "embargo.country", "fields": {"country": "PM"}}, {"pk": 175, "model": "embargo.country", "fields": {"country": "PN"}}, {"pk": 178, "model": "embargo.country", "fields": {"country": "PR"}}, {"pk": 169, "model": "embargo.country", "fields": {"country": "PS"}}, {"pk": 177, "model": "embargo.country", "fields": {"country": "PT"}}, {"pk": 168, "model": "embargo.country", "fields": {"country": "PW"}}, {"pk": 172, "model": "embargo.country", "fields": {"country": "PY"}}, {"pk": 179, "model": "embargo.country", "fields": {"country": "QA"}}, {"pk": 183, "model": "embargo.country", "fields": {"country": "RE"}}, {"pk": 180, "model": "embargo.country", "fields": {"country": "RO"}}, {"pk": 196, "model": "embargo.country", "fields": {"country": "RS"}}, {"pk": 181, "model": "embargo.country", "fields": {"country": "RU"}}, {"pk": 182, "model": "embargo.country", "fields": {"country": "RW"}}, {"pk": 194, "model": "embargo.country", "fields": {"country": "SA"}}, {"pk": 203, "model": "embargo.country", "fields": {"country": "SB"}}, {"pk": 197, "model": "embargo.country", "fields": {"country": "SC"}}, {"pk": 210, "model": "embargo.country", "fields": {"country": "SD"}}, {"pk": 214, "model": "embargo.country", "fields": {"country": "SE"}}, {"pk": 199, "model": "embargo.country", "fields": {"country": "SG"}}, {"pk": 185, "model": "embargo.country", "fields": {"country": "SH"}}, {"pk": 202, "model": "embargo.country", "fields": {"country": "SI"}}, {"pk": 212, "model": "embargo.country", "fields": {"country": "SJ"}}, {"pk": 201, "model": "embargo.country", "fields": {"country": "SK"}}, {"pk": 198, "model": "embargo.country", "fields": {"country": "SL"}}, {"pk": 192, "model": "embargo.country", "fields": {"country": "SM"}}, {"pk": 195, "model": "embargo.country", "fields": {"country": "SN"}}, {"pk": 204, "model": "embargo.country", "fields": {"country": "SO"}}, {"pk": 211, "model": "embargo.country", "fields": {"country": "SR"}}, {"pk": 207, "model": "embargo.country", "fields": {"country": "SS"}}, {"pk": 193, "model": "embargo.country", "fields": {"country": "ST"}}, {"pk": 66, "model": "embargo.country", "fields": {"country": "SV"}}, {"pk": 200, "model": "embargo.country", "fields": {"country": "SX"}}, {"pk": 216, "model": "embargo.country", "fields": {"country": "SY"}}, {"pk": 213, "model": "embargo.country", "fields": {"country": "SZ"}}, {"pk": 229, "model": "embargo.country", "fields": {"country": "TC"}}, {"pk": 43, "model": "embargo.country", "fields": {"country": "TD"}}, {"pk": 78, "model": "embargo.country", "fields": {"country": "TF"}}, {"pk": 222, "model": "embargo.country", "fields": {"country": "TG"}}, {"pk": 220, "model": "embargo.country", "fields": {"country": "TH"}}, {"pk": 218, "model": "embargo.country", "fields": {"country": "TJ"}}, {"pk": 223, "model": "embargo.country", "fields": {"country": "TK"}}, {"pk": 221, "model": "embargo.country", "fields": {"country": "TL"}}, {"pk": 228, "model": "embargo.country", "fields": {"country": "TM"}}, {"pk": 226, "model": "embargo.country", "fields": {"country": "TN"}}, {"pk": 224, "model": "embargo.country", "fields": {"country": "TO"}}, {"pk": 227, "model": "embargo.country", "fields": {"country": "TR"}}, {"pk": 225, "model": "embargo.country", "fields": {"country": "TT"}}, {"pk": 230, "model": "embargo.country", "fields": {"country": "TV"}}, {"pk": 217, "model": "embargo.country", "fields": {"country": "TW"}}, {"pk": 219, "model": "embargo.country", "fields": {"country": "TZ"}}, {"pk": 232, "model": "embargo.country", "fields": {"country": "UA"}}, {"pk": 231, "model": "embargo.country", "fields": {"country": "UG"}}, {"pk": 236, "model": "embargo.country", "fields": {"country": "UM"}}, {"pk": 235, "model": "embargo.country", "fields": {"country": "US"}}, {"pk": 237, "model": "embargo.country", "fields": {"country": "UY"}}, {"pk": 238, "model": "embargo.country", "fields": {"country": "UZ"}}, {"pk": 97, "model": "embargo.country", "fields": {"country": "VA"}}, {"pk": 190, "model": "embargo.country", "fields": {"country": "VC"}}, {"pk": 240, "model": "embargo.country", "fields": {"country": "VE"}}, {"pk": 242, "model": "embargo.country", "fields": {"country": "VG"}}, {"pk": 243, "model": "embargo.country", "fields": {"country": "VI"}}, {"pk": 241, "model": "embargo.country", "fields": {"country": "VN"}}, {"pk": 239, "model": "embargo.country", "fields": {"country": "VU"}}, {"pk": 244, "model": "embargo.country", "fields": {"country": "WF"}}, {"pk": 191, "model": "embargo.country", "fields": {"country": "WS"}}, {"pk": 246, "model": "embargo.country", "fields": {"country": "YE"}}, {"pk": 142, "model": "embargo.country", "fields": {"country": "YT"}}, {"pk": 205, "model": "embargo.country", "fields": {"country": "ZA"}}, {"pk": 247, "model": "embargo.country", "fields": {"country": "ZM"}}, {"pk": 248, "model": "embargo.country", "fields": {"country": "ZW"}}, {"pk": 1, "model": "edxval.profile", "fields": {"profile_name": "desktop_mp4"}}, {"pk": 2, "model": "edxval.profile", "fields": {"profile_name": "desktop_webm"}}, {"pk": 3, "model": "edxval.profile", "fields": {"profile_name": "mobile_high"}}, {"pk": 4, "model": "edxval.profile", "fields": {"profile_name": "mobile_low"}}, {"pk": 5, "model": "edxval.profile", "fields": {"profile_name": "youtube"}}, {"pk": 1, "model": "milestones.milestonerelationshiptype", "fields": {"active": true, "description": "Autogenerated milestone relationship type \"fulfills\"", "modified": "2015-03-31T06:26:10Z", "name": "fulfills", "created": "2015-03-31T06:26:10Z"}}, {"pk": 2, "model": "milestones.milestonerelationshiptype", "fields": {"active": true, "description": "Autogenerated milestone relationship type \"requires\"", "modified": "2015-03-31T06:26:10Z", "name": "requires", "created": "2015-03-31T06:26:10Z"}}, {"pk": 61, "model": "auth.permission", "fields": {"codename": "add_logentry", "name": "Can add log entry", "content_type": 21}}, {"pk": 62, "model": "auth.permission", "fields": {"codename": "change_logentry", "name": "Can change log entry", "content_type": 21}}, {"pk": 63, "model": "auth.permission", "fields": {"codename": "delete_logentry", "name": "Can delete log entry", "content_type": 21}}, {"pk": 454, "model": "auth.permission", "fields": {"codename": "add_aiclassifier", "name": "Can add ai classifier", "content_type": 151}}, {"pk": 455, "model": "auth.permission", "fields": {"codename": "change_aiclassifier", "name": "Can change ai classifier", "content_type": 151}}, {"pk": 456, "model": "auth.permission", "fields": {"codename": "delete_aiclassifier", "name": "Can delete ai classifier", "content_type": 151}}, {"pk": 451, "model": "auth.permission", "fields": {"codename": "add_aiclassifierset", "name": "Can add ai classifier set", "content_type": 150}}, {"pk": 452, "model": "auth.permission", "fields": {"codename": "change_aiclassifierset", "name": "Can change ai classifier set", "content_type": 150}}, {"pk": 453, "model": "auth.permission", "fields": {"codename": "delete_aiclassifierset", "name": "Can delete ai classifier set", "content_type": 150}}, {"pk": 460, "model": "auth.permission", "fields": {"codename": "add_aigradingworkflow", "name": "Can add ai grading workflow", "content_type": 153}}, {"pk": 461, "model": "auth.permission", "fields": {"codename": "change_aigradingworkflow", "name": "Can change ai grading workflow", "content_type": 153}}, {"pk": 462, "model": "auth.permission", "fields": {"codename": "delete_aigradingworkflow", "name": "Can delete ai grading workflow", "content_type": 153}}, {"pk": 457, "model": "auth.permission", "fields": {"codename": "add_aitrainingworkflow", "name": "Can add ai training workflow", "content_type": 152}}, {"pk": 458, "model": "auth.permission", "fields": {"codename": "change_aitrainingworkflow", "name": "Can change ai training workflow", "content_type": 152}}, {"pk": 459, "model": "auth.permission", "fields": {"codename": "delete_aitrainingworkflow", "name": "Can delete ai training workflow", "content_type": 152}}, {"pk": 424, "model": "auth.permission", "fields": {"codename": "add_assessment", "name": "Can add assessment", "content_type": 141}}, {"pk": 425, "model": "auth.permission", "fields": {"codename": "change_assessment", "name": "Can change assessment", "content_type": 141}}, {"pk": 426, "model": "auth.permission", "fields": {"codename": "delete_assessment", "name": "Can delete assessment", "content_type": 141}}, {"pk": 433, "model": "auth.permission", "fields": {"codename": "add_assessmentfeedback", "name": "Can add assessment feedback", "content_type": 144}}, {"pk": 434, "model": "auth.permission", "fields": {"codename": "change_assessmentfeedback", "name": "Can change assessment feedback", "content_type": 144}}, {"pk": 435, "model": "auth.permission", "fields": {"codename": "delete_assessmentfeedback", "name": "Can delete assessment feedback", "content_type": 144}}, {"pk": 430, "model": "auth.permission", "fields": {"codename": "add_assessmentfeedbackoption", "name": "Can add assessment feedback option", "content_type": 143}}, {"pk": 431, "model": "auth.permission", "fields": {"codename": "change_assessmentfeedbackoption", "name": "Can change assessment feedback option", "content_type": 143}}, {"pk": 432, "model": "auth.permission", "fields": {"codename": "delete_assessmentfeedbackoption", "name": "Can delete assessment feedback option", "content_type": 143}}, {"pk": 427, "model": "auth.permission", "fields": {"codename": "add_assessmentpart", "name": "Can add assessment part", "content_type": 142}}, {"pk": 428, "model": "auth.permission", "fields": {"codename": "change_assessmentpart", "name": "Can change assessment part", "content_type": 142}}, {"pk": 429, "model": "auth.permission", "fields": {"codename": "delete_assessmentpart", "name": "Can delete assessment part", "content_type": 142}}, {"pk": 418, "model": "auth.permission", "fields": {"codename": "add_criterion", "name": "Can add criterion", "content_type": 139}}, {"pk": 419, "model": "auth.permission", "fields": {"codename": "change_criterion", "name": "Can change criterion", "content_type": 139}}, {"pk": 420, "model": "auth.permission", "fields": {"codename": "delete_criterion", "name": "Can delete criterion", "content_type": 139}}, {"pk": 421, "model": "auth.permission", "fields": {"codename": "add_criterionoption", "name": "Can add criterion option", "content_type": 140}}, {"pk": 422, "model": "auth.permission", "fields": {"codename": "change_criterionoption", "name": "Can change criterion option", "content_type": 140}}, {"pk": 423, "model": "auth.permission", "fields": {"codename": "delete_criterionoption", "name": "Can delete criterion option", "content_type": 140}}, {"pk": 436, "model": "auth.permission", "fields": {"codename": "add_peerworkflow", "name": "Can add peer workflow", "content_type": 145}}, {"pk": 437, "model": "auth.permission", "fields": {"codename": "change_peerworkflow", "name": "Can change peer workflow", "content_type": 145}}, {"pk": 438, "model": "auth.permission", "fields": {"codename": "delete_peerworkflow", "name": "Can delete peer workflow", "content_type": 145}}, {"pk": 439, "model": "auth.permission", "fields": {"codename": "add_peerworkflowitem", "name": "Can add peer workflow item", "content_type": 146}}, {"pk": 440, "model": "auth.permission", "fields": {"codename": "change_peerworkflowitem", "name": "Can change peer workflow item", "content_type": 146}}, {"pk": 441, "model": "auth.permission", "fields": {"codename": "delete_peerworkflowitem", "name": "Can delete peer workflow item", "content_type": 146}}, {"pk": 415, "model": "auth.permission", "fields": {"codename": "add_rubric", "name": "Can add rubric", "content_type": 138}}, {"pk": 416, "model": "auth.permission", "fields": {"codename": "change_rubric", "name": "Can change rubric", "content_type": 138}}, {"pk": 417, "model": "auth.permission", "fields": {"codename": "delete_rubric", "name": "Can delete rubric", "content_type": 138}}, {"pk": 445, "model": "auth.permission", "fields": {"codename": "add_studenttrainingworkflow", "name": "Can add student training workflow", "content_type": 148}}, {"pk": 446, "model": "auth.permission", "fields": {"codename": "change_studenttrainingworkflow", "name": "Can change student training workflow", "content_type": 148}}, {"pk": 447, "model": "auth.permission", "fields": {"codename": "delete_studenttrainingworkflow", "name": "Can delete student training workflow", "content_type": 148}}, {"pk": 448, "model": "auth.permission", "fields": {"codename": "add_studenttrainingworkflowitem", "name": "Can add student training workflow item", "content_type": 149}}, {"pk": 449, "model": "auth.permission", "fields": {"codename": "change_studenttrainingworkflowitem", "name": "Can change student training workflow item", "content_type": 149}}, {"pk": 450, "model": "auth.permission", "fields": {"codename": "delete_studenttrainingworkflowitem", "name": "Can delete student training workflow item", "content_type": 149}}, {"pk": 442, "model": "auth.permission", "fields": {"codename": "add_trainingexample", "name": "Can add training example", "content_type": 147}}, {"pk": 443, "model": "auth.permission", "fields": {"codename": "change_trainingexample", "name": "Can change training example", "content_type": 147}}, {"pk": 444, "model": "auth.permission", "fields": {"codename": "delete_trainingexample", "name": "Can delete training example", "content_type": 147}}, {"pk": 4, "model": "auth.permission", "fields": {"codename": "add_group", "name": "Can add group", "content_type": 2}}, {"pk": 5, "model": "auth.permission", "fields": {"codename": "change_group", "name": "Can change group", "content_type": 2}}, {"pk": 6, "model": "auth.permission", "fields": {"codename": "delete_group", "name": "Can delete group", "content_type": 2}}, {"pk": 1, "model": "auth.permission", "fields": {"codename": "add_permission", "name": "Can add permission", "content_type": 1}}, {"pk": 2, "model": "auth.permission", "fields": {"codename": "change_permission", "name": "Can change permission", "content_type": 1}}, {"pk": 3, "model": "auth.permission", "fields": {"codename": "delete_permission", "name": "Can delete permission", "content_type": 1}}, {"pk": 7, "model": "auth.permission", "fields": {"codename": "add_user", "name": "Can add user", "content_type": 3}}, {"pk": 8, "model": "auth.permission", "fields": {"codename": "change_user", "name": "Can change user", "content_type": 3}}, {"pk": 9, "model": "auth.permission", "fields": {"codename": "delete_user", "name": "Can delete user", "content_type": 3}}, {"pk": 208, "model": "auth.permission", "fields": {"codename": "add_brandinginfoconfig", "name": "Can add branding info config", "content_type": 70}}, {"pk": 209, "model": "auth.permission", "fields": {"codename": "change_brandinginfoconfig", "name": "Can change branding info config", "content_type": 70}}, {"pk": 210, "model": "auth.permission", "fields": {"codename": "delete_brandinginfoconfig", "name": "Can delete branding info config", "content_type": 70}}, {"pk": 205, "model": "auth.permission", "fields": {"codename": "add_courseauthorization", "name": "Can add course authorization", "content_type": 69}}, {"pk": 206, "model": "auth.permission", "fields": {"codename": "change_courseauthorization", "name": "Can change course authorization", "content_type": 69}}, {"pk": 207, "model": "auth.permission", "fields": {"codename": "delete_courseauthorization", "name": "Can delete course authorization", "content_type": 69}}, {"pk": 196, "model": "auth.permission", "fields": {"codename": "add_courseemail", "name": "Can add course email", "content_type": 66}}, {"pk": 197, "model": "auth.permission", "fields": {"codename": "change_courseemail", "name": "Can change course email", "content_type": 66}}, {"pk": 198, "model": "auth.permission", "fields": {"codename": "delete_courseemail", "name": "Can delete course email", "content_type": 66}}, {"pk": 202, "model": "auth.permission", "fields": {"codename": "add_courseemailtemplate", "name": "Can add course email template", "content_type": 68}}, {"pk": 203, "model": "auth.permission", "fields": {"codename": "change_courseemailtemplate", "name": "Can change course email template", "content_type": 68}}, {"pk": 204, "model": "auth.permission", "fields": {"codename": "delete_courseemailtemplate", "name": "Can delete course email template", "content_type": 68}}, {"pk": 199, "model": "auth.permission", "fields": {"codename": "add_optout", "name": "Can add optout", "content_type": 67}}, {"pk": 200, "model": "auth.permission", "fields": {"codename": "change_optout", "name": "Can change optout", "content_type": 67}}, {"pk": 201, "model": "auth.permission", "fields": {"codename": "delete_optout", "name": "Can delete optout", "content_type": 67}}, {"pk": 169, "model": "auth.permission", "fields": {"codename": "add_certificategenerationconfiguration", "name": "Can add certificate generation configuration", "content_type": 57}}, {"pk": 170, "model": "auth.permission", "fields": {"codename": "change_certificategenerationconfiguration", "name": "Can change certificate generation configuration", "content_type": 57}}, {"pk": 171, "model": "auth.permission", "fields": {"codename": "delete_certificategenerationconfiguration", "name": "Can delete certificate generation configuration", "content_type": 57}}, {"pk": 166, "model": "auth.permission", "fields": {"codename": "add_certificategenerationcoursesetting", "name": "Can add certificate generation course setting", "content_type": 56}}, {"pk": 167, "model": "auth.permission", "fields": {"codename": "change_certificategenerationcoursesetting", "name": "Can change certificate generation course setting", "content_type": 56}}, {"pk": 168, "model": "auth.permission", "fields": {"codename": "delete_certificategenerationcoursesetting", "name": "Can delete certificate generation course setting", "content_type": 56}}, {"pk": 172, "model": "auth.permission", "fields": {"codename": "add_certificatehtmlviewconfiguration", "name": "Can add certificate html view configuration", "content_type": 58}}, {"pk": 173, "model": "auth.permission", "fields": {"codename": "change_certificatehtmlviewconfiguration", "name": "Can change certificate html view configuration", "content_type": 58}}, {"pk": 174, "model": "auth.permission", "fields": {"codename": "delete_certificatehtmlviewconfiguration", "name": "Can delete certificate html view configuration", "content_type": 58}}, {"pk": 154, "model": "auth.permission", "fields": {"codename": "add_certificatewhitelist", "name": "Can add certificate whitelist", "content_type": 52}}, {"pk": 155, "model": "auth.permission", "fields": {"codename": "change_certificatewhitelist", "name": "Can change certificate whitelist", "content_type": 52}}, {"pk": 156, "model": "auth.permission", "fields": {"codename": "delete_certificatewhitelist", "name": "Can delete certificate whitelist", "content_type": 52}}, {"pk": 163, "model": "auth.permission", "fields": {"codename": "add_examplecertificate", "name": "Can add example certificate", "content_type": 55}}, {"pk": 164, "model": "auth.permission", "fields": {"codename": "change_examplecertificate", "name": "Can change example certificate", "content_type": 55}}, {"pk": 165, "model": "auth.permission", "fields": {"codename": "delete_examplecertificate", "name": "Can delete example certificate", "content_type": 55}}, {"pk": 160, "model": "auth.permission", "fields": {"codename": "add_examplecertificateset", "name": "Can add example certificate set", "content_type": 54}}, {"pk": 161, "model": "auth.permission", "fields": {"codename": "change_examplecertificateset", "name": "Can change example certificate set", "content_type": 54}}, {"pk": 162, "model": "auth.permission", "fields": {"codename": "delete_examplecertificateset", "name": "Can delete example certificate set", "content_type": 54}}, {"pk": 157, "model": "auth.permission", "fields": {"codename": "add_generatedcertificate", "name": "Can add generated certificate", "content_type": 53}}, {"pk": 158, "model": "auth.permission", "fields": {"codename": "change_generatedcertificate", "name": "Can change generated certificate", "content_type": 53}}, {"pk": 159, "model": "auth.permission", "fields": {"codename": "delete_generatedcertificate", "name": "Can delete generated certificate", "content_type": 53}}, {"pk": 46, "model": "auth.permission", "fields": {"codename": "add_servercircuit", "name": "Can add server circuit", "content_type": 16}}, {"pk": 47, "model": "auth.permission", "fields": {"codename": "change_servercircuit", "name": "Can change server circuit", "content_type": 16}}, {"pk": 48, "model": "auth.permission", "fields": {"codename": "delete_servercircuit", "name": "Can delete server circuit", "content_type": 16}}, {"pk": 502, "model": "auth.permission", "fields": {"codename": "add_videouploadconfig", "name": "Can add video upload config", "content_type": 167}}, {"pk": 503, "model": "auth.permission", "fields": {"codename": "change_videouploadconfig", "name": "Can change video upload config", "content_type": 167}}, {"pk": 504, "model": "auth.permission", "fields": {"codename": "delete_videouploadconfig", "name": "Can delete video upload config", "content_type": 167}}, {"pk": 10, "model": "auth.permission", "fields": {"codename": "add_contenttype", "name": "Can add content type", "content_type": 4}}, {"pk": 11, "model": "auth.permission", "fields": {"codename": "change_contenttype", "name": "Can change content type", "content_type": 4}}, {"pk": 12, "model": "auth.permission", "fields": {"codename": "delete_contenttype", "name": "Can delete content type", "content_type": 4}}, {"pk": 64, "model": "auth.permission", "fields": {"codename": "add_corsmodel", "name": "Can add cors model", "content_type": 22}}, {"pk": 65, "model": "auth.permission", "fields": {"codename": "change_corsmodel", "name": "Can change cors model", "content_type": 22}}, {"pk": 66, "model": "auth.permission", "fields": {"codename": "delete_corsmodel", "name": "Can delete cors model", "content_type": 22}}, {"pk": 94, "model": "auth.permission", "fields": {"codename": "add_offlinecomputedgrade", "name": "Can add offline computed grade", "content_type": 32}}, {"pk": 95, "model": "auth.permission", "fields": {"codename": "change_offlinecomputedgrade", "name": "Can change offline computed grade", "content_type": 32}}, {"pk": 96, "model": "auth.permission", "fields": {"codename": "delete_offlinecomputedgrade", "name": "Can delete offline computed grade", "content_type": 32}}, {"pk": 97, "model": "auth.permission", "fields": {"codename": "add_offlinecomputedgradelog", "name": "Can add offline computed grade log", "content_type": 33}}, {"pk": 98, "model": "auth.permission", "fields": {"codename": "change_offlinecomputedgradelog", "name": "Can change offline computed grade log", "content_type": 33}}, {"pk": 99, "model": "auth.permission", "fields": {"codename": "delete_offlinecomputedgradelog", "name": "Can delete offline computed grade log", "content_type": 33}}, {"pk": 79, "model": "auth.permission", "fields": {"codename": "add_studentmodule", "name": "Can add student module", "content_type": 27}}, {"pk": 80, "model": "auth.permission", "fields": {"codename": "change_studentmodule", "name": "Can change student module", "content_type": 27}}, {"pk": 81, "model": "auth.permission", "fields": {"codename": "delete_studentmodule", "name": "Can delete student module", "content_type": 27}}, {"pk": 82, "model": "auth.permission", "fields": {"codename": "add_studentmodulehistory", "name": "Can add student module history", "content_type": 28}}, {"pk": 83, "model": "auth.permission", "fields": {"codename": "change_studentmodulehistory", "name": "Can change student module history", "content_type": 28}}, {"pk": 84, "model": "auth.permission", "fields": {"codename": "delete_studentmodulehistory", "name": "Can delete student module history", "content_type": 28}}, {"pk": 91, "model": "auth.permission", "fields": {"codename": "add_xmodulestudentinfofield", "name": "Can add x module student info field", "content_type": 31}}, {"pk": 92, "model": "auth.permission", "fields": {"codename": "change_xmodulestudentinfofield", "name": "Can change x module student info field", "content_type": 31}}, {"pk": 93, "model": "auth.permission", "fields": {"codename": "delete_xmodulestudentinfofield", "name": "Can delete x module student info field", "content_type": 31}}, {"pk": 88, "model": "auth.permission", "fields": {"codename": "add_xmodulestudentprefsfield", "name": "Can add x module student prefs field", "content_type": 30}}, {"pk": 89, "model": "auth.permission", "fields": {"codename": "change_xmodulestudentprefsfield", "name": "Can change x module student prefs field", "content_type": 30}}, {"pk": 90, "model": "auth.permission", "fields": {"codename": "delete_xmodulestudentprefsfield", "name": "Can delete x module student prefs field", "content_type": 30}}, {"pk": 85, "model": "auth.permission", "fields": {"codename": "add_xmoduleuserstatesummaryfield", "name": "Can add x module user state summary field", "content_type": 29}}, {"pk": 86, "model": "auth.permission", "fields": {"codename": "change_xmoduleuserstatesummaryfield", "name": "Can change x module user state summary field", "content_type": 29}}, {"pk": 87, "model": "auth.permission", "fields": {"codename": "delete_xmoduleuserstatesummaryfield", "name": "Can delete x module user state summary field", "content_type": 29}}, {"pk": 385, "model": "auth.permission", "fields": {"codename": "add_coursererunstate", "name": "Can add course rerun state", "content_type": 128}}, {"pk": 386, "model": "auth.permission", "fields": {"codename": "change_coursererunstate", "name": "Can change course rerun state", "content_type": 128}}, {"pk": 387, "model": "auth.permission", "fields": {"codename": "delete_coursererunstate", "name": "Can delete course rerun state", "content_type": 128}}, {"pk": 505, "model": "auth.permission", "fields": {"codename": "add_coursecreator", "name": "Can add course creator", "content_type": 168}}, {"pk": 506, "model": "auth.permission", "fields": {"codename": "change_coursecreator", "name": "Can change course creator", "content_type": 168}}, {"pk": 507, "model": "auth.permission", "fields": {"codename": "delete_coursecreator", "name": "Can delete course creator", "content_type": 168}}, {"pk": 193, "model": "auth.permission", "fields": {"codename": "add_coursecohort", "name": "Can add course cohort", "content_type": 65}}, {"pk": 194, "model": "auth.permission", "fields": {"codename": "change_coursecohort", "name": "Can change course cohort", "content_type": 65}}, {"pk": 195, "model": "auth.permission", "fields": {"codename": "delete_coursecohort", "name": "Can delete course cohort", "content_type": 65}}, {"pk": 190, "model": "auth.permission", "fields": {"codename": "add_coursecohortssettings", "name": "Can add course cohorts settings", "content_type": 64}}, {"pk": 191, "model": "auth.permission", "fields": {"codename": "change_coursecohortssettings", "name": "Can change course cohorts settings", "content_type": 64}}, {"pk": 192, "model": "auth.permission", "fields": {"codename": "delete_coursecohortssettings", "name": "Can delete course cohorts settings", "content_type": 64}}, {"pk": 184, "model": "auth.permission", "fields": {"codename": "add_courseusergroup", "name": "Can add course user group", "content_type": 62}}, {"pk": 185, "model": "auth.permission", "fields": {"codename": "change_courseusergroup", "name": "Can change course user group", "content_type": 62}}, {"pk": 186, "model": "auth.permission", "fields": {"codename": "delete_courseusergroup", "name": "Can delete course user group", "content_type": 62}}, {"pk": 187, "model": "auth.permission", "fields": {"codename": "add_courseusergrouppartitiongroup", "name": "Can add course user group partition group", "content_type": 63}}, {"pk": 188, "model": "auth.permission", "fields": {"codename": "change_courseusergrouppartitiongroup", "name": "Can change course user group partition group", "content_type": 63}}, {"pk": 189, "model": "auth.permission", "fields": {"codename": "delete_courseusergrouppartitiongroup", "name": "Can delete course user group partition group", "content_type": 63}}, {"pk": 349, "model": "auth.permission", "fields": {"codename": "add_coursemode", "name": "Can add course mode", "content_type": 116}}, {"pk": 350, "model": "auth.permission", "fields": {"codename": "change_coursemode", "name": "Can change course mode", "content_type": 116}}, {"pk": 351, "model": "auth.permission", "fields": {"codename": "delete_coursemode", "name": "Can delete course mode", "content_type": 116}}, {"pk": 352, "model": "auth.permission", "fields": {"codename": "add_coursemodesarchive", "name": "Can add course modes archive", "content_type": 117}}, {"pk": 353, "model": "auth.permission", "fields": {"codename": "change_coursemodesarchive", "name": "Can change course modes archive", "content_type": 117}}, {"pk": 354, "model": "auth.permission", "fields": {"codename": "delete_coursemodesarchive", "name": "Can delete course modes archive", "content_type": 117}}, {"pk": 400, "model": "auth.permission", "fields": {"codename": "add_coursestructure", "name": "Can add course structure", "content_type": 133}}, {"pk": 401, "model": "auth.permission", "fields": {"codename": "change_coursestructure", "name": "Can change course structure", "content_type": 133}}, {"pk": 402, "model": "auth.permission", "fields": {"codename": "delete_coursestructure", "name": "Can delete course structure", "content_type": 133}}, {"pk": 358, "model": "auth.permission", "fields": {"codename": "add_darklangconfig", "name": "Can add dark lang config", "content_type": 119}}, {"pk": 359, "model": "auth.permission", "fields": {"codename": "change_darklangconfig", "name": "Can change dark lang config", "content_type": 119}}, {"pk": 360, "model": "auth.permission", "fields": {"codename": "delete_darklangconfig", "name": "Can delete dark lang config", "content_type": 119}}, {"pk": 73, "model": "auth.permission", "fields": {"codename": "add_association", "name": "Can add association", "content_type": 25}}, {"pk": 74, "model": "auth.permission", "fields": {"codename": "change_association", "name": "Can change association", "content_type": 25}}, {"pk": 75, "model": "auth.permission", "fields": {"codename": "delete_association", "name": "Can delete association", "content_type": 25}}, {"pk": 76, "model": "auth.permission", "fields": {"codename": "add_code", "name": "Can add code", "content_type": 26}}, {"pk": 77, "model": "auth.permission", "fields": {"codename": "change_code", "name": "Can change code", "content_type": 26}}, {"pk": 78, "model": "auth.permission", "fields": {"codename": "delete_code", "name": "Can delete code", "content_type": 26}}, {"pk": 70, "model": "auth.permission", "fields": {"codename": "add_nonce", "name": "Can add nonce", "content_type": 24}}, {"pk": 71, "model": "auth.permission", "fields": {"codename": "change_nonce", "name": "Can change nonce", "content_type": 24}}, {"pk": 72, "model": "auth.permission", "fields": {"codename": "delete_nonce", "name": "Can delete nonce", "content_type": 24}}, {"pk": 67, "model": "auth.permission", "fields": {"codename": "add_usersocialauth", "name": "Can add user social auth", "content_type": 23}}, {"pk": 68, "model": "auth.permission", "fields": {"codename": "change_usersocialauth", "name": "Can change user social auth", "content_type": 23}}, {"pk": 69, "model": "auth.permission", "fields": {"codename": "delete_usersocialauth", "name": "Can delete user social auth", "content_type": 23}}, {"pk": 271, "model": "auth.permission", "fields": {"codename": "add_notification", "name": "Can add notification", "content_type": 90}}, {"pk": 272, "model": "auth.permission", "fields": {"codename": "change_notification", "name": "Can change notification", "content_type": 90}}, {"pk": 273, "model": "auth.permission", "fields": {"codename": "delete_notification", "name": "Can delete notification", "content_type": 90}}, {"pk": 262, "model": "auth.permission", "fields": {"codename": "add_notificationtype", "name": "Can add type", "content_type": 87}}, {"pk": 263, "model": "auth.permission", "fields": {"codename": "change_notificationtype", "name": "Can change type", "content_type": 87}}, {"pk": 264, "model": "auth.permission", "fields": {"codename": "delete_notificationtype", "name": "Can delete type", "content_type": 87}}, {"pk": 265, "model": "auth.permission", "fields": {"codename": "add_settings", "name": "Can add settings", "content_type": 88}}, {"pk": 266, "model": "auth.permission", "fields": {"codename": "change_settings", "name": "Can change settings", "content_type": 88}}, {"pk": 267, "model": "auth.permission", "fields": {"codename": "delete_settings", "name": "Can delete settings", "content_type": 88}}, {"pk": 268, "model": "auth.permission", "fields": {"codename": "add_subscription", "name": "Can add subscription", "content_type": 89}}, {"pk": 269, "model": "auth.permission", "fields": {"codename": "change_subscription", "name": "Can change subscription", "content_type": 89}}, {"pk": 270, "model": "auth.permission", "fields": {"codename": "delete_subscription", "name": "Can delete subscription", "content_type": 89}}, {"pk": 55, "model": "auth.permission", "fields": {"codename": "add_association", "name": "Can add association", "content_type": 19}}, {"pk": 56, "model": "auth.permission", "fields": {"codename": "change_association", "name": "Can change association", "content_type": 19}}, {"pk": 57, "model": "auth.permission", "fields": {"codename": "delete_association", "name": "Can delete association", "content_type": 19}}, {"pk": 52, "model": "auth.permission", "fields": {"codename": "add_nonce", "name": "Can add nonce", "content_type": 18}}, {"pk": 53, "model": "auth.permission", "fields": {"codename": "change_nonce", "name": "Can change nonce", "content_type": 18}}, {"pk": 54, "model": "auth.permission", "fields": {"codename": "delete_nonce", "name": "Can delete nonce", "content_type": 18}}, {"pk": 58, "model": "auth.permission", "fields": {"codename": "add_useropenid", "name": "Can add user open id", "content_type": 20}}, {"pk": 59, "model": "auth.permission", "fields": {"codename": "change_useropenid", "name": "Can change user open id", "content_type": 20}}, {"pk": 60, "model": "auth.permission", "fields": {"codename": "delete_useropenid", "name": "Can delete user open id", "content_type": 20}}, {"pk": 28, "model": "auth.permission", "fields": {"codename": "add_crontabschedule", "name": "Can add crontab", "content_type": 10}}, {"pk": 29, "model": "auth.permission", "fields": {"codename": "change_crontabschedule", "name": "Can change crontab", "content_type": 10}}, {"pk": 30, "model": "auth.permission", "fields": {"codename": "delete_crontabschedule", "name": "Can delete crontab", "content_type": 10}}, {"pk": 25, "model": "auth.permission", "fields": {"codename": "add_intervalschedule", "name": "Can add interval", "content_type": 9}}, {"pk": 26, "model": "auth.permission", "fields": {"codename": "change_intervalschedule", "name": "Can change interval", "content_type": 9}}, {"pk": 27, "model": "auth.permission", "fields": {"codename": "delete_intervalschedule", "name": "Can delete interval", "content_type": 9}}, {"pk": 34, "model": "auth.permission", "fields": {"codename": "add_periodictask", "name": "Can add periodic task", "content_type": 12}}, {"pk": 35, "model": "auth.permission", "fields": {"codename": "change_periodictask", "name": "Can change periodic task", "content_type": 12}}, {"pk": 36, "model": "auth.permission", "fields": {"codename": "delete_periodictask", "name": "Can delete periodic task", "content_type": 12}}, {"pk": 31, "model": "auth.permission", "fields": {"codename": "add_periodictasks", "name": "Can add periodic tasks", "content_type": 11}}, {"pk": 32, "model": "auth.permission", "fields": {"codename": "change_periodictasks", "name": "Can change periodic tasks", "content_type": 11}}, {"pk": 33, "model": "auth.permission", "fields": {"codename": "delete_periodictasks", "name": "Can delete periodic tasks", "content_type": 11}}, {"pk": 19, "model": "auth.permission", "fields": {"codename": "add_taskmeta", "name": "Can add task state", "content_type": 7}}, {"pk": 20, "model": "auth.permission", "fields": {"codename": "change_taskmeta", "name": "Can change task state", "content_type": 7}}, {"pk": 21, "model": "auth.permission", "fields": {"codename": "delete_taskmeta", "name": "Can delete task state", "content_type": 7}}, {"pk": 22, "model": "auth.permission", "fields": {"codename": "add_tasksetmeta", "name": "Can add saved group result", "content_type": 8}}, {"pk": 23, "model": "auth.permission", "fields": {"codename": "change_tasksetmeta", "name": "Can change saved group result", "content_type": 8}}, {"pk": 24, "model": "auth.permission", "fields": {"codename": "delete_tasksetmeta", "name": "Can delete saved group result", "content_type": 8}}, {"pk": 40, "model": "auth.permission", "fields": {"codename": "add_taskstate", "name": "Can add task", "content_type": 14}}, {"pk": 41, "model": "auth.permission", "fields": {"codename": "change_taskstate", "name": "Can change task", "content_type": 14}}, {"pk": 42, "model": "auth.permission", "fields": {"codename": "delete_taskstate", "name": "Can delete task", "content_type": 14}}, {"pk": 37, "model": "auth.permission", "fields": {"codename": "add_workerstate", "name": "Can add worker", "content_type": 13}}, {"pk": 38, "model": "auth.permission", "fields": {"codename": "change_workerstate", "name": "Can change worker", "content_type": 13}}, {"pk": 39, "model": "auth.permission", "fields": {"codename": "delete_workerstate", "name": "Can delete worker", "content_type": 13}}, {"pk": 478, "model": "auth.permission", "fields": {"codename": "add_coursevideo", "name": "Can add course video", "content_type": 159}}, {"pk": 479, "model": "auth.permission", "fields": {"codename": "change_coursevideo", "name": "Can change course video", "content_type": 159}}, {"pk": 480, "model": "auth.permission", "fields": {"codename": "delete_coursevideo", "name": "Can delete course video", "content_type": 159}}, {"pk": 481, "model": "auth.permission", "fields": {"codename": "add_encodedvideo", "name": "Can add encoded video", "content_type": 160}}, {"pk": 482, "model": "auth.permission", "fields": {"codename": "change_encodedvideo", "name": "Can change encoded video", "content_type": 160}}, {"pk": 483, "model": "auth.permission", "fields": {"codename": "delete_encodedvideo", "name": "Can delete encoded video", "content_type": 160}}, {"pk": 472, "model": "auth.permission", "fields": {"codename": "add_profile", "name": "Can add profile", "content_type": 157}}, {"pk": 473, "model": "auth.permission", "fields": {"codename": "change_profile", "name": "Can change profile", "content_type": 157}}, {"pk": 474, "model": "auth.permission", "fields": {"codename": "delete_profile", "name": "Can delete profile", "content_type": 157}}, {"pk": 484, "model": "auth.permission", "fields": {"codename": "add_subtitle", "name": "Can add subtitle", "content_type": 161}}, {"pk": 485, "model": "auth.permission", "fields": {"codename": "change_subtitle", "name": "Can change subtitle", "content_type": 161}}, {"pk": 486, "model": "auth.permission", "fields": {"codename": "delete_subtitle", "name": "Can delete subtitle", "content_type": 161}}, {"pk": 475, "model": "auth.permission", "fields": {"codename": "add_video", "name": "Can add video", "content_type": 158}}, {"pk": 476, "model": "auth.permission", "fields": {"codename": "change_video", "name": "Can change video", "content_type": 158}}, {"pk": 477, "model": "auth.permission", "fields": {"codename": "delete_video", "name": "Can delete video", "content_type": 158}}, {"pk": 373, "model": "auth.permission", "fields": {"codename": "add_country", "name": "Can add country", "content_type": 124}}, {"pk": 374, "model": "auth.permission", "fields": {"codename": "change_country", "name": "Can change country", "content_type": 124}}, {"pk": 375, "model": "auth.permission", "fields": {"codename": "delete_country", "name": "Can delete country", "content_type": 124}}, {"pk": 376, "model": "auth.permission", "fields": {"codename": "add_countryaccessrule", "name": "Can add country access rule", "content_type": 125}}, {"pk": 377, "model": "auth.permission", "fields": {"codename": "change_countryaccessrule", "name": "Can change country access rule", "content_type": 125}}, {"pk": 378, "model": "auth.permission", "fields": {"codename": "delete_countryaccessrule", "name": "Can delete country access rule", "content_type": 125}}, {"pk": 379, "model": "auth.permission", "fields": {"codename": "add_courseaccessrulehistory", "name": "Can add course access rule history", "content_type": 126}}, {"pk": 380, "model": "auth.permission", "fields": {"codename": "change_courseaccessrulehistory", "name": "Can change course access rule history", "content_type": 126}}, {"pk": 381, "model": "auth.permission", "fields": {"codename": "delete_courseaccessrulehistory", "name": "Can delete course access rule history", "content_type": 126}}, {"pk": 364, "model": "auth.permission", "fields": {"codename": "add_embargoedcourse", "name": "Can add embargoed course", "content_type": 121}}, {"pk": 365, "model": "auth.permission", "fields": {"codename": "change_embargoedcourse", "name": "Can change embargoed course", "content_type": 121}}, {"pk": 366, "model": "auth.permission", "fields": {"codename": "delete_embargoedcourse", "name": "Can delete embargoed course", "content_type": 121}}, {"pk": 367, "model": "auth.permission", "fields": {"codename": "add_embargoedstate", "name": "Can add embargoed state", "content_type": 122}}, {"pk": 368, "model": "auth.permission", "fields": {"codename": "change_embargoedstate", "name": "Can change embargoed state", "content_type": 122}}, {"pk": 369, "model": "auth.permission", "fields": {"codename": "delete_embargoedstate", "name": "Can delete embargoed state", "content_type": 122}}, {"pk": 382, "model": "auth.permission", "fields": {"codename": "add_ipfilter", "name": "Can add ip filter", "content_type": 127}}, {"pk": 383, "model": "auth.permission", "fields": {"codename": "change_ipfilter", "name": "Can change ip filter", "content_type": 127}}, {"pk": 384, "model": "auth.permission", "fields": {"codename": "delete_ipfilter", "name": "Can delete ip filter", "content_type": 127}}, {"pk": 370, "model": "auth.permission", "fields": {"codename": "add_restrictedcourse", "name": "Can add restricted course", "content_type": 123}}, {"pk": 371, "model": "auth.permission", "fields": {"codename": "change_restrictedcourse", "name": "Can change restricted course", "content_type": 123}}, {"pk": 372, "model": "auth.permission", "fields": {"codename": "delete_restrictedcourse", "name": "Can delete restricted course", "content_type": 123}}, {"pk": 211, "model": "auth.permission", "fields": {"codename": "add_externalauthmap", "name": "Can add external auth map", "content_type": 71}}, {"pk": 212, "model": "auth.permission", "fields": {"codename": "change_externalauthmap", "name": "Can change external auth map", "content_type": 71}}, {"pk": 213, "model": "auth.permission", "fields": {"codename": "delete_externalauthmap", "name": "Can delete external auth map", "content_type": 71}}, {"pk": 277, "model": "auth.permission", "fields": {"codename": "add_puzzlecomplete", "name": "Can add puzzle complete", "content_type": 92}}, {"pk": 278, "model": "auth.permission", "fields": {"codename": "change_puzzlecomplete", "name": "Can change puzzle complete", "content_type": 92}}, {"pk": 279, "model": "auth.permission", "fields": {"codename": "delete_puzzlecomplete", "name": "Can delete puzzle complete", "content_type": 92}}, {"pk": 274, "model": "auth.permission", "fields": {"codename": "add_score", "name": "Can add score", "content_type": 91}}, {"pk": 275, "model": "auth.permission", "fields": {"codename": "change_score", "name": "Can change score", "content_type": 91}}, {"pk": 276, "model": "auth.permission", "fields": {"codename": "delete_score", "name": "Can delete score", "content_type": 91}}, {"pk": 175, "model": "auth.permission", "fields": {"codename": "add_instructortask", "name": "Can add instructor task", "content_type": 59}}, {"pk": 176, "model": "auth.permission", "fields": {"codename": "change_instructortask", "name": "Can change instructor task", "content_type": 59}}, {"pk": 177, "model": "auth.permission", "fields": {"codename": "delete_instructortask", "name": "Can delete instructor task", "content_type": 59}}, {"pk": 178, "model": "auth.permission", "fields": {"codename": "add_coursesoftware", "name": "Can add course software", "content_type": 60}}, {"pk": 179, "model": "auth.permission", "fields": {"codename": "change_coursesoftware", "name": "Can change course software", "content_type": 60}}, {"pk": 180, "model": "auth.permission", "fields": {"codename": "delete_coursesoftware", "name": "Can delete course software", "content_type": 60}}, {"pk": 181, "model": "auth.permission", "fields": {"codename": "add_userlicense", "name": "Can add user license", "content_type": 61}}, {"pk": 182, "model": "auth.permission", "fields": {"codename": "change_userlicense", "name": "Can change user license", "content_type": 61}}, {"pk": 183, "model": "auth.permission", "fields": {"codename": "delete_userlicense", "name": "Can delete user license", "content_type": 61}}, {"pk": 397, "model": "auth.permission", "fields": {"codename": "add_xblockasidesconfig", "name": "Can add x block asides config", "content_type": 132}}, {"pk": 398, "model": "auth.permission", "fields": {"codename": "change_xblockasidesconfig", "name": "Can change x block asides config", "content_type": 132}}, {"pk": 399, "model": "auth.permission", "fields": {"codename": "delete_xblockasidesconfig", "name": "Can delete x block asides config", "content_type": 132}}, {"pk": 496, "model": "auth.permission", "fields": {"codename": "add_coursecontentmilestone", "name": "Can add course content milestone", "content_type": 165}}, {"pk": 497, "model": "auth.permission", "fields": {"codename": "change_coursecontentmilestone", "name": "Can change course content milestone", "content_type": 165}}, {"pk": 498, "model": "auth.permission", "fields": {"codename": "delete_coursecontentmilestone", "name": "Can delete course content milestone", "content_type": 165}}, {"pk": 493, "model": "auth.permission", "fields": {"codename": "add_coursemilestone", "name": "Can add course milestone", "content_type": 164}}, {"pk": 494, "model": "auth.permission", "fields": {"codename": "change_coursemilestone", "name": "Can change course milestone", "content_type": 164}}, {"pk": 495, "model": "auth.permission", "fields": {"codename": "delete_coursemilestone", "name": "Can delete course milestone", "content_type": 164}}, {"pk": 487, "model": "auth.permission", "fields": {"codename": "add_milestone", "name": "Can add milestone", "content_type": 162}}, {"pk": 488, "model": "auth.permission", "fields": {"codename": "change_milestone", "name": "Can change milestone", "content_type": 162}}, {"pk": 489, "model": "auth.permission", "fields": {"codename": "delete_milestone", "name": "Can delete milestone", "content_type": 162}}, {"pk": 490, "model": "auth.permission", "fields": {"codename": "add_milestonerelationshiptype", "name": "Can add milestone relationship type", "content_type": 163}}, {"pk": 491, "model": "auth.permission", "fields": {"codename": "change_milestonerelationshiptype", "name": "Can change milestone relationship type", "content_type": 163}}, {"pk": 492, "model": "auth.permission", "fields": {"codename": "delete_milestonerelationshiptype", "name": "Can delete milestone relationship type", "content_type": 163}}, {"pk": 499, "model": "auth.permission", "fields": {"codename": "add_usermilestone", "name": "Can add user milestone", "content_type": 166}}, {"pk": 500, "model": "auth.permission", "fields": {"codename": "change_usermilestone", "name": "Can change user milestone", "content_type": 166}}, {"pk": 501, "model": "auth.permission", "fields": {"codename": "delete_usermilestone", "name": "Can delete user milestone", "content_type": 166}}, {"pk": 388, "model": "auth.permission", "fields": {"codename": "add_mobileapiconfig", "name": "Can add mobile api config", "content_type": 129}}, {"pk": 389, "model": "auth.permission", "fields": {"codename": "change_mobileapiconfig", "name": "Can change mobile api config", "content_type": 129}}, {"pk": 390, "model": "auth.permission", "fields": {"codename": "delete_mobileapiconfig", "name": "Can delete mobile api config", "content_type": 129}}, {"pk": 280, "model": "auth.permission", "fields": {"codename": "add_note", "name": "Can add note", "content_type": 93}}, {"pk": 281, "model": "auth.permission", "fields": {"codename": "change_note", "name": "Can change note", "content_type": 93}}, {"pk": 282, "model": "auth.permission", "fields": {"codename": "delete_note", "name": "Can delete note", "content_type": 93}}, {"pk": 220, "model": "auth.permission", "fields": {"codename": "add_accesstoken", "name": "Can add access token", "content_type": 74}}, {"pk": 221, "model": "auth.permission", "fields": {"codename": "change_accesstoken", "name": "Can change access token", "content_type": 74}}, {"pk": 222, "model": "auth.permission", "fields": {"codename": "delete_accesstoken", "name": "Can delete access token", "content_type": 74}}, {"pk": 214, "model": "auth.permission", "fields": {"codename": "add_client", "name": "Can add client", "content_type": 72}}, {"pk": 215, "model": "auth.permission", "fields": {"codename": "change_client", "name": "Can change client", "content_type": 72}}, {"pk": 216, "model": "auth.permission", "fields": {"codename": "delete_client", "name": "Can delete client", "content_type": 72}}, {"pk": 217, "model": "auth.permission", "fields": {"codename": "add_grant", "name": "Can add grant", "content_type": 73}}, {"pk": 218, "model": "auth.permission", "fields": {"codename": "change_grant", "name": "Can change grant", "content_type": 73}}, {"pk": 219, "model": "auth.permission", "fields": {"codename": "delete_grant", "name": "Can delete grant", "content_type": 73}}, {"pk": 223, "model": "auth.permission", "fields": {"codename": "add_refreshtoken", "name": "Can add refresh token", "content_type": 75}}, {"pk": 224, "model": "auth.permission", "fields": {"codename": "change_refreshtoken", "name": "Can change refresh token", "content_type": 75}}, {"pk": 225, "model": "auth.permission", "fields": {"codename": "delete_refreshtoken", "name": "Can delete refresh token", "content_type": 75}}, {"pk": 226, "model": "auth.permission", "fields": {"codename": "add_trustedclient", "name": "Can add trusted client", "content_type": 76}}, {"pk": 227, "model": "auth.permission", "fields": {"codename": "change_trustedclient", "name": "Can change trusted client", "content_type": 76}}, {"pk": 228, "model": "auth.permission", "fields": {"codename": "delete_trustedclient", "name": "Can delete trusted client", "content_type": 76}}, {"pk": 49, "model": "auth.permission", "fields": {"codename": "add_psychometricdata", "name": "Can add psychometric data", "content_type": 17}}, {"pk": 50, "model": "auth.permission", "fields": {"codename": "change_psychometricdata", "name": "Can change psychometric data", "content_type": 17}}, {"pk": 51, "model": "auth.permission", "fields": {"codename": "delete_psychometricdata", "name": "Can delete psychometric data", "content_type": 17}}, {"pk": 361, "model": "auth.permission", "fields": {"codename": "add_midcoursereverificationwindow", "name": "Can add midcourse reverification window", "content_type": 120}}, {"pk": 362, "model": "auth.permission", "fields": {"codename": "change_midcoursereverificationwindow", "name": "Can change midcourse reverification window", "content_type": 120}}, {"pk": 363, "model": "auth.permission", "fields": {"codename": "delete_midcoursereverificationwindow", "name": "Can delete midcourse reverification window", "content_type": 120}}, {"pk": 13, "model": "auth.permission", "fields": {"codename": "add_session", "name": "Can add session", "content_type": 5}}, {"pk": 14, "model": "auth.permission", "fields": {"codename": "change_session", "name": "Can change session", "content_type": 5}}, {"pk": 15, "model": "auth.permission", "fields": {"codename": "delete_session", "name": "Can delete session", "content_type": 5}}, {"pk": 340, "model": "auth.permission", "fields": {"codename": "add_certificateitem", "name": "Can add certificate item", "content_type": 113}}, {"pk": 341, "model": "auth.permission", "fields": {"codename": "change_certificateitem", "name": "Can change certificate item", "content_type": 113}}, {"pk": 342, "model": "auth.permission", "fields": {"codename": "delete_certificateitem", "name": "Can delete certificate item", "content_type": 113}}, {"pk": 322, "model": "auth.permission", "fields": {"codename": "add_coupon", "name": "Can add coupon", "content_type": 107}}, {"pk": 323, "model": "auth.permission", "fields": {"codename": "change_coupon", "name": "Can change coupon", "content_type": 107}}, {"pk": 324, "model": "auth.permission", "fields": {"codename": "delete_coupon", "name": "Can delete coupon", "content_type": 107}}, {"pk": 325, "model": "auth.permission", "fields": {"codename": "add_couponredemption", "name": "Can add coupon redemption", "content_type": 108}}, {"pk": 326, "model": "auth.permission", "fields": {"codename": "change_couponredemption", "name": "Can change coupon redemption", "content_type": 108}}, {"pk": 327, "model": "auth.permission", "fields": {"codename": "delete_couponredemption", "name": "Can delete coupon redemption", "content_type": 108}}, {"pk": 331, "model": "auth.permission", "fields": {"codename": "add_courseregcodeitem", "name": "Can add course reg code item", "content_type": 110}}, {"pk": 332, "model": "auth.permission", "fields": {"codename": "change_courseregcodeitem", "name": "Can change course reg code item", "content_type": 110}}, {"pk": 333, "model": "auth.permission", "fields": {"codename": "delete_courseregcodeitem", "name": "Can delete course reg code item", "content_type": 110}}, {"pk": 334, "model": "auth.permission", "fields": {"codename": "add_courseregcodeitemannotation", "name": "Can add course reg code item annotation", "content_type": 111}}, {"pk": 335, "model": "auth.permission", "fields": {"codename": "change_courseregcodeitemannotation", "name": "Can change course reg code item annotation", "content_type": 111}}, {"pk": 336, "model": "auth.permission", "fields": {"codename": "delete_courseregcodeitemannotation", "name": "Can delete course reg code item annotation", "content_type": 111}}, {"pk": 316, "model": "auth.permission", "fields": {"codename": "add_courseregistrationcode", "name": "Can add course registration code", "content_type": 105}}, {"pk": 317, "model": "auth.permission", "fields": {"codename": "change_courseregistrationcode", "name": "Can change course registration code", "content_type": 105}}, {"pk": 318, "model": "auth.permission", "fields": {"codename": "delete_courseregistrationcode", "name": "Can delete course registration code", "content_type": 105}}, {"pk": 310, "model": "auth.permission", "fields": {"codename": "add_courseregistrationcodeinvoiceitem", "name": "Can add course registration code invoice item", "content_type": 103}}, {"pk": 311, "model": "auth.permission", "fields": {"codename": "change_courseregistrationcodeinvoiceitem", "name": "Can change course registration code invoice item", "content_type": 103}}, {"pk": 312, "model": "auth.permission", "fields": {"codename": "delete_courseregistrationcodeinvoiceitem", "name": "Can delete course registration code invoice item", "content_type": 103}}, {"pk": 346, "model": "auth.permission", "fields": {"codename": "add_donation", "name": "Can add donation", "content_type": 115}}, {"pk": 347, "model": "auth.permission", "fields": {"codename": "change_donation", "name": "Can change donation", "content_type": 115}}, {"pk": 348, "model": "auth.permission", "fields": {"codename": "delete_donation", "name": "Can delete donation", "content_type": 115}}, {"pk": 343, "model": "auth.permission", "fields": {"codename": "add_donationconfiguration", "name": "Can add donation configuration", "content_type": 114}}, {"pk": 344, "model": "auth.permission", "fields": {"codename": "change_donationconfiguration", "name": "Can change donation configuration", "content_type": 114}}, {"pk": 345, "model": "auth.permission", "fields": {"codename": "delete_donationconfiguration", "name": "Can delete donation configuration", "content_type": 114}}, {"pk": 301, "model": "auth.permission", "fields": {"codename": "add_invoice", "name": "Can add invoice", "content_type": 100}}, {"pk": 302, "model": "auth.permission", "fields": {"codename": "change_invoice", "name": "Can change invoice", "content_type": 100}}, {"pk": 303, "model": "auth.permission", "fields": {"codename": "delete_invoice", "name": "Can delete invoice", "content_type": 100}}, {"pk": 313, "model": "auth.permission", "fields": {"codename": "add_invoicehistory", "name": "Can add invoice history", "content_type": 104}}, {"pk": 314, "model": "auth.permission", "fields": {"codename": "change_invoicehistory", "name": "Can change invoice history", "content_type": 104}}, {"pk": 315, "model": "auth.permission", "fields": {"codename": "delete_invoicehistory", "name": "Can delete invoice history", "content_type": 104}}, {"pk": 307, "model": "auth.permission", "fields": {"codename": "add_invoiceitem", "name": "Can add invoice item", "content_type": 102}}, {"pk": 308, "model": "auth.permission", "fields": {"codename": "change_invoiceitem", "name": "Can change invoice item", "content_type": 102}}, {"pk": 309, "model": "auth.permission", "fields": {"codename": "delete_invoiceitem", "name": "Can delete invoice item", "content_type": 102}}, {"pk": 304, "model": "auth.permission", "fields": {"codename": "add_invoicetransaction", "name": "Can add invoice transaction", "content_type": 101}}, {"pk": 305, "model": "auth.permission", "fields": {"codename": "change_invoicetransaction", "name": "Can change invoice transaction", "content_type": 101}}, {"pk": 306, "model": "auth.permission", "fields": {"codename": "delete_invoicetransaction", "name": "Can delete invoice transaction", "content_type": 101}}, {"pk": 295, "model": "auth.permission", "fields": {"codename": "add_order", "name": "Can add order", "content_type": 98}}, {"pk": 296, "model": "auth.permission", "fields": {"codename": "change_order", "name": "Can change order", "content_type": 98}}, {"pk": 297, "model": "auth.permission", "fields": {"codename": "delete_order", "name": "Can delete order", "content_type": 98}}, {"pk": 298, "model": "auth.permission", "fields": {"codename": "add_orderitem", "name": "Can add order item", "content_type": 99}}, {"pk": 299, "model": "auth.permission", "fields": {"codename": "change_orderitem", "name": "Can change order item", "content_type": 99}}, {"pk": 300, "model": "auth.permission", "fields": {"codename": "delete_orderitem", "name": "Can delete order item", "content_type": 99}}, {"pk": 328, "model": "auth.permission", "fields": {"codename": "add_paidcourseregistration", "name": "Can add paid course registration", "content_type": 109}}, {"pk": 329, "model": "auth.permission", "fields": {"codename": "change_paidcourseregistration", "name": "Can change paid course registration", "content_type": 109}}, {"pk": 330, "model": "auth.permission", "fields": {"codename": "delete_paidcourseregistration", "name": "Can delete paid course registration", "content_type": 109}}, {"pk": 337, "model": "auth.permission", "fields": {"codename": "add_paidcourseregistrationannotation", "name": "Can add paid course registration annotation", "content_type": 112}}, {"pk": 338, "model": "auth.permission", "fields": {"codename": "change_paidcourseregistrationannotation", "name": "Can change paid course registration annotation", "content_type": 112}}, {"pk": 339, "model": "auth.permission", "fields": {"codename": "delete_paidcourseregistrationannotation", "name": "Can delete paid course registration annotation", "content_type": 112}}, {"pk": 319, "model": "auth.permission", "fields": {"codename": "add_registrationcoderedemption", "name": "Can add registration code redemption", "content_type": 106}}, {"pk": 320, "model": "auth.permission", "fields": {"codename": "change_registrationcoderedemption", "name": "Can change registration code redemption", "content_type": 106}}, {"pk": 321, "model": "auth.permission", "fields": {"codename": "delete_registrationcoderedemption", "name": "Can delete registration code redemption", "content_type": 106}}, {"pk": 16, "model": "auth.permission", "fields": {"codename": "add_site", "name": "Can add site", "content_type": 6}}, {"pk": 17, "model": "auth.permission", "fields": {"codename": "change_site", "name": "Can change site", "content_type": 6}}, {"pk": 18, "model": "auth.permission", "fields": {"codename": "delete_site", "name": "Can delete site", "content_type": 6}}, {"pk": 43, "model": "auth.permission", "fields": {"codename": "add_migrationhistory", "name": "Can add migration history", "content_type": 15}}, {"pk": 44, "model": "auth.permission", "fields": {"codename": "change_migrationhistory", "name": "Can change migration history", "content_type": 15}}, {"pk": 45, "model": "auth.permission", "fields": {"codename": "delete_migrationhistory", "name": "Can delete migration history", "content_type": 15}}, {"pk": 283, "model": "auth.permission", "fields": {"codename": "add_splashconfig", "name": "Can add splash config", "content_type": 94}}, {"pk": 284, "model": "auth.permission", "fields": {"codename": "change_splashconfig", "name": "Can change splash config", "content_type": 94}}, {"pk": 285, "model": "auth.permission", "fields": {"codename": "delete_splashconfig", "name": "Can delete splash config", "content_type": 94}}, {"pk": 100, "model": "auth.permission", "fields": {"codename": "add_anonymoususerid", "name": "Can add anonymous user id", "content_type": 34}}, {"pk": 101, "model": "auth.permission", "fields": {"codename": "change_anonymoususerid", "name": "Can change anonymous user id", "content_type": 34}}, {"pk": 102, "model": "auth.permission", "fields": {"codename": "delete_anonymoususerid", "name": "Can delete anonymous user id", "content_type": 34}}, {"pk": 136, "model": "auth.permission", "fields": {"codename": "add_courseaccessrole", "name": "Can add course access role", "content_type": 46}}, {"pk": 137, "model": "auth.permission", "fields": {"codename": "change_courseaccessrole", "name": "Can change course access role", "content_type": 46}}, {"pk": 138, "model": "auth.permission", "fields": {"codename": "delete_courseaccessrole", "name": "Can delete course access role", "content_type": 46}}, {"pk": 130, "model": "auth.permission", "fields": {"codename": "add_courseenrollment", "name": "Can add course enrollment", "content_type": 44}}, {"pk": 131, "model": "auth.permission", "fields": {"codename": "change_courseenrollment", "name": "Can change course enrollment", "content_type": 44}}, {"pk": 132, "model": "auth.permission", "fields": {"codename": "delete_courseenrollment", "name": "Can delete course enrollment", "content_type": 44}}, {"pk": 133, "model": "auth.permission", "fields": {"codename": "add_courseenrollmentallowed", "name": "Can add course enrollment allowed", "content_type": 45}}, {"pk": 134, "model": "auth.permission", "fields": {"codename": "change_courseenrollmentallowed", "name": "Can change course enrollment allowed", "content_type": 45}}, {"pk": 135, "model": "auth.permission", "fields": {"codename": "delete_courseenrollmentallowed", "name": "Can delete course enrollment allowed", "content_type": 45}}, {"pk": 139, "model": "auth.permission", "fields": {"codename": "add_dashboardconfiguration", "name": "Can add dashboard configuration", "content_type": 47}}, {"pk": 140, "model": "auth.permission", "fields": {"codename": "change_dashboardconfiguration", "name": "Can change dashboard configuration", "content_type": 47}}, {"pk": 141, "model": "auth.permission", "fields": {"codename": "delete_dashboardconfiguration", "name": "Can delete dashboard configuration", "content_type": 47}}, {"pk": 145, "model": "auth.permission", "fields": {"codename": "add_entranceexamconfiguration", "name": "Can add entrance exam configuration", "content_type": 49}}, {"pk": 146, "model": "auth.permission", "fields": {"codename": "change_entranceexamconfiguration", "name": "Can change entrance exam configuration", "content_type": 49}}, {"pk": 147, "model": "auth.permission", "fields": {"codename": "delete_entranceexamconfiguration", "name": "Can delete entrance exam configuration", "content_type": 49}}, {"pk": 142, "model": "auth.permission", "fields": {"codename": "add_linkedinaddtoprofileconfiguration", "name": "Can add linked in add to profile configuration", "content_type": 48}}, {"pk": 143, "model": "auth.permission", "fields": {"codename": "change_linkedinaddtoprofileconfiguration", "name": "Can change linked in add to profile configuration", "content_type": 48}}, {"pk": 144, "model": "auth.permission", "fields": {"codename": "delete_linkedinaddtoprofileconfiguration", "name": "Can delete linked in add to profile configuration", "content_type": 48}}, {"pk": 127, "model": "auth.permission", "fields": {"codename": "add_loginfailures", "name": "Can add login failures", "content_type": 43}}, {"pk": 128, "model": "auth.permission", "fields": {"codename": "change_loginfailures", "name": "Can change login failures", "content_type": 43}}, {"pk": 129, "model": "auth.permission", "fields": {"codename": "delete_loginfailures", "name": "Can delete login failures", "content_type": 43}}, {"pk": 124, "model": "auth.permission", "fields": {"codename": "add_passwordhistory", "name": "Can add password history", "content_type": 42}}, {"pk": 125, "model": "auth.permission", "fields": {"codename": "change_passwordhistory", "name": "Can change password history", "content_type": 42}}, {"pk": 126, "model": "auth.permission", "fields": {"codename": "delete_passwordhistory", "name": "Can delete password history", "content_type": 42}}, {"pk": 121, "model": "auth.permission", "fields": {"codename": "add_pendingemailchange", "name": "Can add pending email change", "content_type": 41}}, {"pk": 122, "model": "auth.permission", "fields": {"codename": "change_pendingemailchange", "name": "Can change pending email change", "content_type": 41}}, {"pk": 123, "model": "auth.permission", "fields": {"codename": "delete_pendingemailchange", "name": "Can delete pending email change", "content_type": 41}}, {"pk": 118, "model": "auth.permission", "fields": {"codename": "add_pendingnamechange", "name": "Can add pending name change", "content_type": 40}}, {"pk": 119, "model": "auth.permission", "fields": {"codename": "change_pendingnamechange", "name": "Can change pending name change", "content_type": 40}}, {"pk": 120, "model": "auth.permission", "fields": {"codename": "delete_pendingnamechange", "name": "Can delete pending name change", "content_type": 40}}, {"pk": 115, "model": "auth.permission", "fields": {"codename": "add_registration", "name": "Can add registration", "content_type": 39}}, {"pk": 116, "model": "auth.permission", "fields": {"codename": "change_registration", "name": "Can change registration", "content_type": 39}}, {"pk": 117, "model": "auth.permission", "fields": {"codename": "delete_registration", "name": "Can delete registration", "content_type": 39}}, {"pk": 106, "model": "auth.permission", "fields": {"codename": "add_userprofile", "name": "Can add user profile", "content_type": 36}}, {"pk": 107, "model": "auth.permission", "fields": {"codename": "change_userprofile", "name": "Can change user profile", "content_type": 36}}, {"pk": 108, "model": "auth.permission", "fields": {"codename": "delete_userprofile", "name": "Can delete user profile", "content_type": 36}}, {"pk": 109, "model": "auth.permission", "fields": {"codename": "add_usersignupsource", "name": "Can add user signup source", "content_type": 37}}, {"pk": 110, "model": "auth.permission", "fields": {"codename": "change_usersignupsource", "name": "Can change user signup source", "content_type": 37}}, {"pk": 111, "model": "auth.permission", "fields": {"codename": "delete_usersignupsource", "name": "Can delete user signup source", "content_type": 37}}, {"pk": 103, "model": "auth.permission", "fields": {"codename": "add_userstanding", "name": "Can add user standing", "content_type": 35}}, {"pk": 104, "model": "auth.permission", "fields": {"codename": "change_userstanding", "name": "Can change user standing", "content_type": 35}}, {"pk": 105, "model": "auth.permission", "fields": {"codename": "delete_userstanding", "name": "Can delete user standing", "content_type": 35}}, {"pk": 112, "model": "auth.permission", "fields": {"codename": "add_usertestgroup", "name": "Can add user test group", "content_type": 38}}, {"pk": 113, "model": "auth.permission", "fields": {"codename": "change_usertestgroup", "name": "Can change user test group", "content_type": 38}}, {"pk": 114, "model": "auth.permission", "fields": {"codename": "delete_usertestgroup", "name": "Can delete user test group", "content_type": 38}}, {"pk": 409, "model": "auth.permission", "fields": {"codename": "add_score", "name": "Can add score", "content_type": 136}}, {"pk": 410, "model": "auth.permission", "fields": {"codename": "change_score", "name": "Can change score", "content_type": 136}}, {"pk": 411, "model": "auth.permission", "fields": {"codename": "delete_score", "name": "Can delete score", "content_type": 136}}, {"pk": 412, "model": "auth.permission", "fields": {"codename": "add_scoresummary", "name": "Can add score summary", "content_type": 137}}, {"pk": 413, "model": "auth.permission", "fields": {"codename": "change_scoresummary", "name": "Can change score summary", "content_type": 137}}, {"pk": 414, "model": "auth.permission", "fields": {"codename": "delete_scoresummary", "name": "Can delete score summary", "content_type": 137}}, {"pk": 403, "model": "auth.permission", "fields": {"codename": "add_studentitem", "name": "Can add student item", "content_type": 134}}, {"pk": 404, "model": "auth.permission", "fields": {"codename": "change_studentitem", "name": "Can change student item", "content_type": 134}}, {"pk": 405, "model": "auth.permission", "fields": {"codename": "delete_studentitem", "name": "Can delete student item", "content_type": 134}}, {"pk": 406, "model": "auth.permission", "fields": {"codename": "add_submission", "name": "Can add submission", "content_type": 135}}, {"pk": 407, "model": "auth.permission", "fields": {"codename": "change_submission", "name": "Can change submission", "content_type": 135}}, {"pk": 408, "model": "auth.permission", "fields": {"codename": "delete_submission", "name": "Can delete submission", "content_type": 135}}, {"pk": 394, "model": "auth.permission", "fields": {"codename": "add_surveyanswer", "name": "Can add survey answer", "content_type": 131}}, {"pk": 395, "model": "auth.permission", "fields": {"codename": "change_surveyanswer", "name": "Can change survey answer", "content_type": 131}}, {"pk": 396, "model": "auth.permission", "fields": {"codename": "delete_surveyanswer", "name": "Can delete survey answer", "content_type": 131}}, {"pk": 391, "model": "auth.permission", "fields": {"codename": "add_surveyform", "name": "Can add survey form", "content_type": 130}}, {"pk": 392, "model": "auth.permission", "fields": {"codename": "change_surveyform", "name": "Can change survey form", "content_type": 130}}, {"pk": 393, "model": "auth.permission", "fields": {"codename": "delete_surveyform", "name": "Can delete survey form", "content_type": 130}}, {"pk": 148, "model": "auth.permission", "fields": {"codename": "add_trackinglog", "name": "Can add tracking log", "content_type": 50}}, {"pk": 149, "model": "auth.permission", "fields": {"codename": "change_trackinglog", "name": "Can change tracking log", "content_type": 50}}, {"pk": 150, "model": "auth.permission", "fields": {"codename": "delete_trackinglog", "name": "Can delete tracking log", "content_type": 50}}, {"pk": 289, "model": "auth.permission", "fields": {"codename": "add_usercoursetag", "name": "Can add user course tag", "content_type": 96}}, {"pk": 290, "model": "auth.permission", "fields": {"codename": "change_usercoursetag", "name": "Can change user course tag", "content_type": 96}}, {"pk": 291, "model": "auth.permission", "fields": {"codename": "delete_usercoursetag", "name": "Can delete user course tag", "content_type": 96}}, {"pk": 292, "model": "auth.permission", "fields": {"codename": "add_userorgtag", "name": "Can add user org tag", "content_type": 97}}, {"pk": 293, "model": "auth.permission", "fields": {"codename": "change_userorgtag", "name": "Can change user org tag", "content_type": 97}}, {"pk": 294, "model": "auth.permission", "fields": {"codename": "delete_userorgtag", "name": "Can delete user org tag", "content_type": 97}}, {"pk": 286, "model": "auth.permission", "fields": {"codename": "add_userpreference", "name": "Can add user preference", "content_type": 95}}, {"pk": 287, "model": "auth.permission", "fields": {"codename": "change_userpreference", "name": "Can change user preference", "content_type": 95}}, {"pk": 288, "model": "auth.permission", "fields": {"codename": "delete_userpreference", "name": "Can delete user preference", "content_type": 95}}, {"pk": 151, "model": "auth.permission", "fields": {"codename": "add_ratelimitconfiguration", "name": "Can add rate limit configuration", "content_type": 51}}, {"pk": 152, "model": "auth.permission", "fields": {"codename": "change_ratelimitconfiguration", "name": "Can change rate limit configuration", "content_type": 51}}, {"pk": 153, "model": "auth.permission", "fields": {"codename": "delete_ratelimitconfiguration", "name": "Can delete rate limit configuration", "content_type": 51}}, {"pk": 355, "model": "auth.permission", "fields": {"codename": "add_softwaresecurephotoverification", "name": "Can add software secure photo verification", "content_type": 118}}, {"pk": 356, "model": "auth.permission", "fields": {"codename": "change_softwaresecurephotoverification", "name": "Can change software secure photo verification", "content_type": 118}}, {"pk": 357, "model": "auth.permission", "fields": {"codename": "delete_softwaresecurephotoverification", "name": "Can delete software secure photo verification", "content_type": 118}}, {"pk": 229, "model": "auth.permission", "fields": {"codename": "add_article", "name": "Can add article", "content_type": 77}}, {"pk": 233, "model": "auth.permission", "fields": {"codename": "assign", "name": "Can change ownership of any article", "content_type": 77}}, {"pk": 230, "model": "auth.permission", "fields": {"codename": "change_article", "name": "Can change article", "content_type": 77}}, {"pk": 231, "model": "auth.permission", "fields": {"codename": "delete_article", "name": "Can delete article", "content_type": 77}}, {"pk": 234, "model": "auth.permission", "fields": {"codename": "grant", "name": "Can assign permissions to other users", "content_type": 77}}, {"pk": 232, "model": "auth.permission", "fields": {"codename": "moderate", "name": "Can edit all articles and lock/unlock/restore", "content_type": 77}}, {"pk": 235, "model": "auth.permission", "fields": {"codename": "add_articleforobject", "name": "Can add Article for object", "content_type": 78}}, {"pk": 236, "model": "auth.permission", "fields": {"codename": "change_articleforobject", "name": "Can change Article for object", "content_type": 78}}, {"pk": 237, "model": "auth.permission", "fields": {"codename": "delete_articleforobject", "name": "Can delete Article for object", "content_type": 78}}, {"pk": 244, "model": "auth.permission", "fields": {"codename": "add_articleplugin", "name": "Can add article plugin", "content_type": 81}}, {"pk": 245, "model": "auth.permission", "fields": {"codename": "change_articleplugin", "name": "Can change article plugin", "content_type": 81}}, {"pk": 246, "model": "auth.permission", "fields": {"codename": "delete_articleplugin", "name": "Can delete article plugin", "content_type": 81}}, {"pk": 238, "model": "auth.permission", "fields": {"codename": "add_articlerevision", "name": "Can add article revision", "content_type": 79}}, {"pk": 239, "model": "auth.permission", "fields": {"codename": "change_articlerevision", "name": "Can change article revision", "content_type": 79}}, {"pk": 240, "model": "auth.permission", "fields": {"codename": "delete_articlerevision", "name": "Can delete article revision", "content_type": 79}}, {"pk": 259, "model": "auth.permission", "fields": {"codename": "add_articlesubscription", "name": "Can add article subscription", "content_type": 86}}, {"pk": 260, "model": "auth.permission", "fields": {"codename": "change_articlesubscription", "name": "Can change article subscription", "content_type": 86}}, {"pk": 261, "model": "auth.permission", "fields": {"codename": "delete_articlesubscription", "name": "Can delete article subscription", "content_type": 86}}, {"pk": 247, "model": "auth.permission", "fields": {"codename": "add_reusableplugin", "name": "Can add reusable plugin", "content_type": 82}}, {"pk": 248, "model": "auth.permission", "fields": {"codename": "change_reusableplugin", "name": "Can change reusable plugin", "content_type": 82}}, {"pk": 249, "model": "auth.permission", "fields": {"codename": "delete_reusableplugin", "name": "Can delete reusable plugin", "content_type": 82}}, {"pk": 253, "model": "auth.permission", "fields": {"codename": "add_revisionplugin", "name": "Can add revision plugin", "content_type": 84}}, {"pk": 254, "model": "auth.permission", "fields": {"codename": "change_revisionplugin", "name": "Can change revision plugin", "content_type": 84}}, {"pk": 255, "model": "auth.permission", "fields": {"codename": "delete_revisionplugin", "name": "Can delete revision plugin", "content_type": 84}}, {"pk": 256, "model": "auth.permission", "fields": {"codename": "add_revisionpluginrevision", "name": "Can add revision plugin revision", "content_type": 85}}, {"pk": 257, "model": "auth.permission", "fields": {"codename": "change_revisionpluginrevision", "name": "Can change revision plugin revision", "content_type": 85}}, {"pk": 258, "model": "auth.permission", "fields": {"codename": "delete_revisionpluginrevision", "name": "Can delete revision plugin revision", "content_type": 85}}, {"pk": 250, "model": "auth.permission", "fields": {"codename": "add_simpleplugin", "name": "Can add simple plugin", "content_type": 83}}, {"pk": 251, "model": "auth.permission", "fields": {"codename": "change_simpleplugin", "name": "Can change simple plugin", "content_type": 83}}, {"pk": 252, "model": "auth.permission", "fields": {"codename": "delete_simpleplugin", "name": "Can delete simple plugin", "content_type": 83}}, {"pk": 241, "model": "auth.permission", "fields": {"codename": "add_urlpath", "name": "Can add URL path", "content_type": 80}}, {"pk": 242, "model": "auth.permission", "fields": {"codename": "change_urlpath", "name": "Can change URL path", "content_type": 80}}, {"pk": 243, "model": "auth.permission", "fields": {"codename": "delete_urlpath", "name": "Can delete URL path", "content_type": 80}}, {"pk": 463, "model": "auth.permission", "fields": {"codename": "add_assessmentworkflow", "name": "Can add assessment workflow", "content_type": 154}}, {"pk": 464, "model": "auth.permission", "fields": {"codename": "change_assessmentworkflow", "name": "Can change assessment workflow", "content_type": 154}}, {"pk": 465, "model": "auth.permission", "fields": {"codename": "delete_assessmentworkflow", "name": "Can delete assessment workflow", "content_type": 154}}, {"pk": 469, "model": "auth.permission", "fields": {"codename": "add_assessmentworkflowcancellation", "name": "Can add assessment workflow cancellation", "content_type": 156}}, {"pk": 470, "model": "auth.permission", "fields": {"codename": "change_assessmentworkflowcancellation", "name": "Can change assessment workflow cancellation", "content_type": 156}}, {"pk": 471, "model": "auth.permission", "fields": {"codename": "delete_assessmentworkflowcancellation", "name": "Can delete assessment workflow cancellation", "content_type": 156}}, {"pk": 466, "model": "auth.permission", "fields": {"codename": "add_assessmentworkflowstep", "name": "Can add assessment workflow step", "content_type": 155}}, {"pk": 467, "model": "auth.permission", "fields": {"codename": "change_assessmentworkflowstep", "name": "Can change assessment workflow step", "content_type": 155}}, {"pk": 468, "model": "auth.permission", "fields": {"codename": "delete_assessmentworkflowstep", "name": "Can delete assessment workflow step", "content_type": 155}}, {"pk": 508, "model": "auth.permission", "fields": {"codename": "add_studioconfig", "name": "Can add studio config", "content_type": 169}}, {"pk": 509, "model": "auth.permission", "fields": {"codename": "change_studioconfig", "name": "Can change studio config", "content_type": 169}}, {"pk": 510, "model": "auth.permission", "fields": {"codename": "delete_studioconfig", "name": "Can delete studio config", "content_type": 169}}, {"pk": 1, "model": "util.ratelimitconfiguration", "fields": {"change_date": "2015-03-31T06:25:45Z", "changed_by": null, "enabled": true}}, {"pk": 1, "model": "certificates.certificatehtmlviewconfiguration", "fields": {"change_date": "2015-03-31T06:25:47Z", "changed_by": null, "configuration": "{\n            {\n    \"default\": {\n        \"accomplishment_class_append\": \"accomplishment-certificate\",\n        \"platform_name\": \"edX\",\n        \"company_privacy_url\": \"http://www.edx.org/edx-privacy-policy\",\n        \"company_tos_url\": \"http://www.edx.org/edx-terms-service\",\n        \"company_verified_certificate_url\": \"http://www.edx.org/verified-certificate\",\n        \"document_stylesheet_url_application\": \"/static/certificates/sass/main-ltr.css\",\n        \"logo_src\": \"/static/certificates/images/logo-edx.svg\",\n        \"logo_url\": \"http://www.edx.org\"\n    },\n    \"honor\": {\n        \"certificate_type\": \"Honor Code\",\n        \"document_body_class_append\": \"is-honorcode\"\n    },\n    \"verified\": {\n        \"certificate_type\": \"Verified\",\n        \"document_body_class_append\": \"is-idverified\"\n    },\n    \"xseries\": {\n        \"certificate_type\": \"XSeries\",\n        \"document_body_class_append\": \"is-xseries\"\n    }\n}\n        }", "enabled": false}}, {"pk": 1, "model": "dark_lang.darklangconfig", "fields": {"change_date": "2015-03-31T06:26:01Z", "changed_by": null, "enabled": true, "released_languages": ""}}, {"pk": 1, "model": "mobile_api.mobileapiconfig", "fields": {"change_date": "2015-03-31T06:26:03Z", "video_profiles": "mobile_low,mobile_high,youtube", "changed_by": null, "enabled": false}},{"pk": 1, "model": "edxval.profile", "fields": {"profile_name": "desktop_mp4"}}, {"pk": 1, "model": "edxval.video", "fields": {"duration": 10.0, "status": "status", "edx_video_id": "edx_video_id", "client_video_id": "", "created": "2015-04-25T18:04:41Z"}}, {"pk": 1, "model": "edxval.encodedvideo", "fields": {"profile": 1, "created": "2015-04-25T18:04:41Z", "url": "http://www.w3schools.com/html/mov_bbb.webm", "modified": "2015-04-25T18:04:41Z", "video": 1, "file_size": 1000, "bitrate": 1000}}]
diff --git a/lms/djangoapps/courseware/tests/test_video_handlers.py b/lms/djangoapps/courseware/tests/test_video_handlers.py
index d785136597b..86be625395d 100644
--- a/lms/djangoapps/courseware/tests/test_video_handlers.py
+++ b/lms/djangoapps/courseware/tests/test_video_handlers.py
@@ -1,15 +1,17 @@
 # -*- coding: utf-8 -*-
 """Video xmodule tests in mongo."""
 
-from mock import patch
-from nose.plugins.attrib import attr
 import os
+import freezegun
 import tempfile
 import textwrap
 import json
-from datetime import timedelta
+import ddt
+
+from nose.plugins.attrib import attr
+from datetime import timedelta, datetime
 from webob import Request
-from mock import MagicMock, Mock
+from mock import MagicMock, Mock, patch
 
 from xmodule.contentstore.content import StaticContent
 from xmodule.contentstore.django import contentstore
@@ -26,6 +28,9 @@ from xmodule.video_module.transcripts_utils import (
     TranscriptsGenerationException,
 )
 
+
+TRANSCRIPT = {"start": [10], "end": [100], "text": ["Hi, welcome to Edx."]}
+BUMPER_TRANSCRIPT = {"start": [1], "end": [10], "text": ["A bumper"]}
 SRT_content = textwrap.dedent("""
         0
         00:00:00,12 --> 00:00:00,100
@@ -104,6 +109,20 @@ def _upload_file(subs_file, location, filename):
     del_cached_content(content.location)
 
 
+def attach_sub(item, filename):
+    """
+    Attach `en` transcript.
+    """
+    item.sub = filename
+
+
+def attach_bumper_transcript(item, filename, lang="en"):
+    """
+    Attach bumper transcript.
+    """
+    item.video_bumper["transcripts"][lang] = filename
+
+
 @attr('shard_1')
 class TestVideo(BaseTestXmodule):
     """Integration tests: web client + mongo."""
@@ -129,6 +148,8 @@ class TestVideo(BaseTestXmodule):
             {'speed': 2.0},
             {'saved_video_position': "00:00:10"},
             {'transcript_language': 'uk'},
+            {'bumper_do_not_show_again': True},
+            {'bumper_last_view_date': True},
             {'demoo�': 'sample'}
         ]
         for sample in data:
@@ -151,6 +172,15 @@ class TestVideo(BaseTestXmodule):
         self.item_descriptor.handle_ajax('save_user_state', {'transcript_language': "uk"})
         self.assertEqual(self.item_descriptor.transcript_language, 'uk')
 
+        self.assertEqual(self.item_descriptor.bumper_do_not_show_again, False)
+        self.item_descriptor.handle_ajax('save_user_state', {'bumper_do_not_show_again': True})
+        self.assertEqual(self.item_descriptor.bumper_do_not_show_again, True)
+
+        with freezegun.freeze_time(datetime.now()):
+            self.assertEqual(self.item_descriptor.bumper_last_view_date, None)
+            self.item_descriptor.handle_ajax('save_user_state', {'bumper_last_view_date': True})
+            self.assertEqual(self.item_descriptor.bumper_last_view_date, datetime.utcnow())
+
         response = self.item_descriptor.handle_ajax('save_user_state', {u'demoo�': "sample"})
         self.assertEqual(json.loads(response)['success'], True)
 
@@ -166,7 +196,7 @@ class TestTranscriptAvailableTranslationsDispatch(TestVideo):
 
     Tests for `available_translations` dispatch.
     """
-    non_en_file = _create_srt_file()
+    srt_file = _create_srt_file()
     DATA = """
         <video show_captions="true"
         display_name="A Name"
@@ -175,7 +205,7 @@ class TestTranscriptAvailableTranslationsDispatch(TestVideo):
             <source src="example.webm"/>
             <transcript language="uk" src="{}"/>
         </video>
-    """.format(os.path.split(non_en_file.name)[1])
+    """.format(os.path.split(srt_file.name)[1])
 
     MODEL_DATA = {
         'data': DATA
@@ -197,7 +227,7 @@ class TestTranscriptAvailableTranslationsDispatch(TestVideo):
         self.assertEqual(json.loads(response.body), ['en'])
 
     def test_available_translation_non_en(self):
-        _upload_file(self.non_en_file, self.item_descriptor.location, os.path.split(self.non_en_file.name)[1])
+        _upload_file(self.srt_file, self.item_descriptor.location, os.path.split(self.srt_file.name)[1])
 
         request = Request.blank('/available_translations')
         response = self.item.transcript(request=request, dispatch='available_translations')
@@ -210,7 +240,7 @@ class TestTranscriptAvailableTranslationsDispatch(TestVideo):
         _upload_sjson_file(good_sjson, self.item_descriptor.location)
 
         # Upload non-english transcript.
-        _upload_file(self.non_en_file, self.item_descriptor.location, os.path.split(self.non_en_file.name)[1])
+        _upload_file(self.srt_file, self.item_descriptor.location, os.path.split(self.srt_file.name)[1])
 
         self.item.sub = _get_subs_id(good_sjson.name)
 
@@ -220,6 +250,63 @@ class TestTranscriptAvailableTranslationsDispatch(TestVideo):
 
 
 @attr('shard_1')
+@ddt.ddt
+class TestTranscriptAvailableTranslationsBumperDispatch(TestVideo):
+    """
+    Test video handler that provide available translations info.
+
+    Tests for `available_translations_bumper` dispatch.
+    """
+    srt_file = _create_srt_file()
+    DATA = """
+        <video show_captions="true"
+        display_name="A Name"
+        >
+            <source src="example.mp4"/>
+            <source src="example.webm"/>
+            <transcript language="uk" src="{}"/>
+        </video>
+    """.format(os.path.split(srt_file.name)[1])
+
+    MODEL_DATA = {
+        'data': DATA
+    }
+
+    def setUp(self):
+        super(TestTranscriptAvailableTranslationsBumperDispatch, self).setUp()
+        self.item_descriptor.render(STUDENT_VIEW)
+        self.item = self.item_descriptor.xmodule_runtime.xmodule_instance
+        self.dispatch = "available_translations/?is_bumper=1"
+        self.item.video_bumper = {"transcripts": {"en": ""}}
+
+    @ddt.data("en", "uk")
+    def test_available_translation_en_and_non_en(self, lang):
+        filename = os.path.split(self.srt_file.name)[1]
+        _upload_file(self.srt_file, self.item_descriptor.location, filename)
+        self.item.video_bumper["transcripts"][lang] = filename
+
+        request = Request.blank('/' + self.dispatch)
+        response = self.item.transcript(request=request, dispatch=self.dispatch)
+        self.assertEqual(json.loads(response.body), [lang])
+
+    def test_multiple_available_translations(self):
+        en_translation = _create_srt_file()
+        en_translation_filename = os.path.split(en_translation.name)[1]
+        uk_translation_filename = os.path.split(self.srt_file.name)[1]
+        # Upload english transcript.
+        _upload_file(en_translation, self.item_descriptor.location, en_translation_filename)
+
+        # Upload non-english transcript.
+        _upload_file(self.srt_file, self.item_descriptor.location, uk_translation_filename)
+
+        self.item.video_bumper["transcripts"]["en"] = en_translation_filename
+        self.item.video_bumper["transcripts"]["uk"] = uk_translation_filename
+
+        request = Request.blank('/' + self.dispatch)
+        response = self.item.transcript(request=request, dispatch=self.dispatch)
+        self.assertEqual(json.loads(response.body), ['en', 'uk'])
+
+
 class TestTranscriptDownloadDispatch(TestVideo):
     """
     Test video handler that provide translation transcripts.
@@ -272,8 +359,9 @@ class TestTranscriptDownloadDispatch(TestVideo):
         request = Request.blank('/download')
         response = self.item.transcript(request=request, dispatch='download')
         self.assertEqual(response.status, '404 Not Found')
+        transcripts = self.item.get_transcripts_info()
         with self.assertRaises(NotFoundError):
-            self.item.get_transcript()
+            self.item.get_transcript(transcripts)
 
     @patch('xmodule.video_module.VideoModule.get_transcript', return_value=('Subs!', u"å¡ž.srt", 'application/x-subrip; charset=utf-8'))
     def test_download_non_en_non_ascii_filename(self, __):
@@ -285,14 +373,15 @@ class TestTranscriptDownloadDispatch(TestVideo):
 
 
 @attr('shard_1')
+@ddt.ddt
 class TestTranscriptTranslationGetDispatch(TestVideo):
     """
     Test video handler that provide translation transcripts.
 
-    Tests for `translation` dispatch.
+    Tests for `translation` and `translation_bumper` dispatches.
     """
 
-    non_en_file = _create_srt_file()
+    srt_file = _create_srt_file()
     DATA = """
         <video show_captions="true"
         display_name="A Name"
@@ -301,7 +390,7 @@ class TestTranscriptTranslationGetDispatch(TestVideo):
             <source src="example.webm"/>
             <transcript language="uk" src="{}"/>
         </video>
-    """.format(os.path.split(non_en_file.name)[1])
+    """.format(os.path.split(srt_file.name)[1])
 
     MODEL_DATA = {
         'data': DATA
@@ -311,37 +400,41 @@ class TestTranscriptTranslationGetDispatch(TestVideo):
         super(TestTranscriptTranslationGetDispatch, self).setUp()
         self.item_descriptor.render(STUDENT_VIEW)
         self.item = self.item_descriptor.xmodule_runtime.xmodule_instance
+        self.item.video_bumper = {"transcripts": {"en": ""}}
 
-    def test_translation_fails(self):
+    @ddt.data(
         # No language
-        request = Request.blank('/translation')
-        response = self.item.transcript(request=request, dispatch='translation')
-        self.assertEqual(response.status, '400 Bad Request')
-
+        ('/translation', 'translation', '400 Bad Request'),
         # No videoId - HTML5 video with language that is not in available languages
-        request = Request.blank('/translation/ru')
-        response = self.item.transcript(request=request, dispatch='translation/ru')
-        self.assertEqual(response.status, '404 Not Found')
-
+        ('/translation/ru', 'translation/ru', '404 Not Found'),
         # Language is not in available languages
-        request = Request.blank('/translation/ru?videoId=12345')
-        response = self.item.transcript(request=request, dispatch='translation/ru')
-        self.assertEqual(response.status, '404 Not Found')
-
+        ('/translation/ru?videoId=12345', 'translation/ru', '404 Not Found'),
         # Youtube_id is invalid or does not exist
-        request = Request.blank('/translation/uk?videoId=9855256955511225')
-        response = self.item.transcript(request=request, dispatch='translation/uk')
-        self.assertEqual(response.status, '404 Not Found')
-
-    def test_translaton_en_youtube_success(self):
+        ('/translation/uk?videoId=9855256955511225', 'translation/uk', '404 Not Found'),
+        ('/translation?is_bumper=1', 'translation', '400 Bad Request'),
+        ('/translation/ru?is_bumper=1', 'translation/ru', '404 Not Found'),
+        ('/translation/ru?videoId=12345&is_bumper=1', 'translation/ru', '404 Not Found'),
+        ('/translation/uk?videoId=9855256955511225&is_bumper=1', 'translation/uk', '404 Not Found'),
+    )
+    @ddt.unpack
+    def test_translation_fails(self, url, dispatch, status_code):
+        request = Request.blank(url)
+        response = self.item.transcript(request=request, dispatch=dispatch)
+        self.assertEqual(response.status, status_code)
+
+    @ddt.data(
+        ('translation/en?videoId={}', 'translation/en', attach_sub),
+        ('translation/en?videoId={}&is_bumper=1', 'translation/en', attach_bumper_transcript))
+    @ddt.unpack
+    def test_translaton_en_youtube_success(self, url, dispatch, attach):
         subs = {"start": [10], "end": [100], "text": ["Hi, welcome to Edx."]}
         good_sjson = _create_file(json.dumps(subs))
         _upload_sjson_file(good_sjson, self.item_descriptor.location)
         subs_id = _get_subs_id(good_sjson.name)
 
-        self.item.sub = subs_id
-        request = Request.blank('/translation/en?videoId={}'.format(subs_id))
-        response = self.item.transcript(request=request, dispatch='translation/en')
+        attach(self.item, subs_id)
+        request = Request.blank(url.format(subs_id))
+        response = self.item.transcript(request=request, dispatch=dispatch)
         self.assertDictEqual(json.loads(response.body), subs)
 
     def test_translation_non_en_youtube_success(self):
@@ -352,9 +445,9 @@ class TestTranscriptTranslationGetDispatch(TestVideo):
                 u'\u041f\u0440\u0438\u0432\u0456\u0442, edX \u0432\u0456\u0442\u0430\u0454 \u0432\u0430\u0441.'
             ]
         }
-        self.non_en_file.seek(0)
-        _upload_file(self.non_en_file, self.item_descriptor.location, os.path.split(self.non_en_file.name)[1])
-        subs_id = _get_subs_id(self.non_en_file.name)
+        self.srt_file.seek(0)
+        _upload_file(self.srt_file, self.item_descriptor.location, os.path.split(self.srt_file.name)[1])
+        subs_id = _get_subs_id(self.srt_file.name)
 
         # youtube 1_0 request, will generate for all speeds for existing ids
         self.item.youtube_id_1_0 = subs_id
@@ -387,16 +480,19 @@ class TestTranscriptTranslationGetDispatch(TestVideo):
         }
         self.assertDictEqual(json.loads(response.body), calculated_1_5)
 
-    def test_translaton_en_html5_success(self):
-        subs = {"start": [10], "end": [100], "text": ["Hi, welcome to Edx."]}
-        good_sjson = _create_file(json.dumps(subs))
+    @ddt.data(
+        ('translation/en', 'translation/en', attach_sub),
+        ('translation/en?is_bumper=1', 'translation/en', attach_bumper_transcript))
+    @ddt.unpack
+    def test_translaton_en_html5_success(self, url, dispatch, attach):
+        good_sjson = _create_file(json.dumps(TRANSCRIPT))
         _upload_sjson_file(good_sjson, self.item_descriptor.location)
         subs_id = _get_subs_id(good_sjson.name)
 
-        self.item.sub = subs_id
-        request = Request.blank('/translation/en')
-        response = self.item.transcript(request=request, dispatch='translation/en')
-        self.assertDictEqual(json.loads(response.body), subs)
+        attach(self.item, subs_id)
+        request = Request.blank(url)
+        response = self.item.transcript(request=request, dispatch=dispatch)
+        self.assertDictEqual(json.loads(response.body), TRANSCRIPT)
 
     def test_translaton_non_en_html5_success(self):
         subs = {
@@ -406,8 +502,8 @@ class TestTranscriptTranslationGetDispatch(TestVideo):
                 u'\u041f\u0440\u0438\u0432\u0456\u0442, edX \u0432\u0456\u0442\u0430\u0454 \u0432\u0430\u0441.'
             ]
         }
-        self.non_en_file.seek(0)
-        _upload_file(self.non_en_file, self.item_descriptor.location, os.path.split(self.non_en_file.name)[1])
+        self.srt_file.seek(0)
+        _upload_file(self.srt_file, self.item_descriptor.location, os.path.split(self.srt_file.name)[1])
 
         # manually clean youtube_id_1_0, as it has default value
         self.item.youtube_id_1_0 = ""
@@ -453,7 +549,22 @@ class TestTranscriptTranslationGetDispatch(TestVideo):
         response = self.item.transcript(request=request, dispatch='translation/uk')
         self.assertEqual(response.status, '404 Not Found')
 
-    def test_translation_static_transcript(self):
+    @ddt.data(
+        # Test youtube style en
+        ('/translation/en?videoId=12345', 'translation/en', '307 Temporary Redirect', '12345'),
+        # Test html5 style en
+        ('/translation/en', 'translation/en', '307 Temporary Redirect', 'OEoXaMPEzfM', attach_sub),
+        # Test different language to ensure we are just ignoring it since we can't
+        # translate with static fallback
+        ('/translation/uk', 'translation/uk', '404 Not Found'),
+        (
+            '/translation/en?is_bumper=1', 'translation/en', '307 Temporary Redirect', 'OEoXaMPEzfM',
+            attach_bumper_transcript
+        ),
+        ('/translation/uk?is_bumper=1', 'translation/uk', '404 Not Found'),
+    )
+    @ddt.unpack
+    def test_translation_static_transcript(self, url, dispatch, status_code, sub=None, attach=None):
         """
         Set course static_asset_path and ensure we get redirected to that path
         if it isn't found in the contentstore
@@ -464,30 +575,16 @@ class TestTranscriptTranslationGetDispatch(TestVideo):
         with store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.course.id):
             store.update_item(self.course, self.user.id)
 
-        # Test youtube style en
-        request = Request.blank('/translation/en?videoId=12345')
-        response = self.item.transcript(request=request, dispatch='translation/en')
-        self.assertEqual(response.status, '307 Temporary Redirect')
-        self.assertIn(
-            ('Location', '/static/dummy/static/subs_12345.srt.sjson'),
-            response.headerlist
-        )
-
-        # Test HTML5 video style
-        self.item.sub = 'OEoXaMPEzfM'
-        request = Request.blank('/translation/en')
-        response = self.item.transcript(request=request, dispatch='translation/en')
-        self.assertEqual(response.status, '307 Temporary Redirect')
-        self.assertIn(
-            ('Location', '/static/dummy/static/subs_OEoXaMPEzfM.srt.sjson'),
-            response.headerlist
-        )
-
-        # Test different language to ensure we are just ignoring it since we can't
-        # translate with static fallback
-        request = Request.blank('/translation/uk')
-        response = self.item.transcript(request=request, dispatch='translation/uk')
-        self.assertEqual(response.status, '404 Not Found')
+        if attach:
+            attach(self.item, sub)
+        request = Request.blank(url)
+        response = self.item.transcript(request=request, dispatch=dispatch)
+        self.assertEqual(response.status, status_code)
+        if sub:
+            self.assertIn(
+                ('Location', '/static/dummy/static/subs_{}.srt.sjson'.format(sub)),
+                response.headerlist
+            )
 
 
 @attr('shard_1')
@@ -497,7 +594,7 @@ class TestStudioTranscriptTranslationGetDispatch(TestVideo):
 
     Tests for `translation` dispatch GET HTTP method.
     """
-    non_en_file = _create_srt_file()
+    srt_file = _create_srt_file()
     DATA = """
         <video show_captions="true"
         display_name="A Name"
@@ -507,7 +604,7 @@ class TestStudioTranscriptTranslationGetDispatch(TestVideo):
             <transcript language="uk" src="{}"/>
             <transcript language="zh" src="{}"/>
         </video>
-    """.format(os.path.split(non_en_file.name)[1], u"å¡ž.srt".encode('utf8'))
+    """.format(os.path.split(srt_file.name)[1], u"å¡ž.srt".encode('utf8'))
 
     MODEL_DATA = {'data': DATA}
 
@@ -523,12 +620,12 @@ class TestStudioTranscriptTranslationGetDispatch(TestVideo):
         self.assertEqual(response.status, '400 Bad Request')
 
         # Correct case:
-        filename = os.path.split(self.non_en_file.name)[1]
-        _upload_file(self.non_en_file, self.item_descriptor.location, filename)
-        self.non_en_file.seek(0)
+        filename = os.path.split(self.srt_file.name)[1]
+        _upload_file(self.srt_file, self.item_descriptor.location, filename)
+        self.srt_file.seek(0)
         request = Request.blank(u'translation/uk?filename={}'.format(filename))
         response = self.item_descriptor.studio_transcript(request=request, dispatch='translation/uk')
-        self.assertEqual(response.body, self.non_en_file.read())
+        self.assertEqual(response.body, self.srt_file.read())
         self.assertEqual(response.headers['Content-Type'], 'application/x-subrip; charset=utf-8')
         self.assertEqual(
             response.headers['Content-Disposition'],
@@ -537,12 +634,12 @@ class TestStudioTranscriptTranslationGetDispatch(TestVideo):
         self.assertEqual(response.headers['Content-Language'], 'uk')
 
         # Non ascii file name download:
-        self.non_en_file.seek(0)
-        _upload_file(self.non_en_file, self.item_descriptor.location, u'å¡ž.srt')
-        self.non_en_file.seek(0)
+        self.srt_file.seek(0)
+        _upload_file(self.srt_file, self.item_descriptor.location, u'å¡ž.srt')
+        self.srt_file.seek(0)
         request = Request.blank('translation/zh?filename={}'.format(u'å¡ž.srt'.encode('utf8')))
         response = self.item_descriptor.studio_transcript(request=request, dispatch='translation/zh')
-        self.assertEqual(response.body, self.non_en_file.read())
+        self.assertEqual(response.body, self.srt_file.read())
         self.assertEqual(response.headers['Content-Type'], 'application/x-subrip; charset=utf-8')
         self.assertEqual(response.headers['Content-Disposition'], 'attachment; filename="å¡ž.srt"')
         self.assertEqual(response.headers['Content-Language'], 'zh')
@@ -614,7 +711,7 @@ class TestGetTranscript(TestVideo):
     """
     Make sure that `get_transcript` method works correctly
     """
-    non_en_file = _create_srt_file()
+    srt_file = _create_srt_file()
     DATA = """
         <video show_captions="true"
         display_name="A Name"
@@ -624,7 +721,7 @@ class TestGetTranscript(TestVideo):
             <transcript language="uk" src="{}"/>
             <transcript language="zh" src="{}"/>
         </video>
-    """.format(os.path.split(non_en_file.name)[1], u"å¡ž.srt".encode('utf8'))
+    """.format(os.path.split(srt_file.name)[1], u"å¡ž.srt".encode('utf8'))
 
     MODEL_DATA = {
         'data': DATA
@@ -660,7 +757,8 @@ class TestGetTranscript(TestVideo):
         _upload_sjson_file(good_sjson, self.item.location)
         self.item.sub = _get_subs_id(good_sjson.name)
 
-        text, filename, mime_type = self.item.get_transcript()
+        transcripts = self.item.get_transcripts_info()
+        text, filename, mime_type = self.item.get_transcript(transcripts)
 
         expected_text = textwrap.dedent("""\
             0
@@ -697,7 +795,8 @@ class TestGetTranscript(TestVideo):
 
         _upload_sjson_file(good_sjson, self.item.location)
         self.item.sub = _get_subs_id(good_sjson.name)
-        text, filename, mime_type = self.item.get_transcript("txt")
+        transcripts = self.item.get_transcripts_info()
+        text, filename, mime_type = self.item.get_transcript(transcripts, transcript_format="txt")
         expected_text = textwrap.dedent("""\
             Hi, welcome to Edx.
             Let's start with what is on your screen right now.""")
@@ -708,14 +807,15 @@ class TestGetTranscript(TestVideo):
 
     def test_en_with_empty_sub(self):
 
+        transcripts = {"transcripts": {}, "sub": ""}
         # no self.sub, self.youttube_1_0 exist, but no file in assets
         with self.assertRaises(NotFoundError):
-            self.item.get_transcript()
+            self.item.get_transcript(transcripts)
 
-        # no self.sub and no self.youtube_1_0
+        # no self.sub and no self.youtube_1_0, no non-en transcritps
         self.item.youtube_id_1_0 = None
         with self.assertRaises(ValueError):
-            self.item.get_transcript()
+            self.item.get_transcript(transcripts)
 
         # no self.sub but youtube_1_0 exists with file in assets
         good_sjson = _create_file(content=textwrap.dedent("""\
@@ -737,7 +837,7 @@ class TestGetTranscript(TestVideo):
         _upload_sjson_file(good_sjson, self.item.location)
         self.item.youtube_id_1_0 = _get_subs_id(good_sjson.name)
 
-        text, filename, mime_type = self.item.get_transcript()
+        text, filename, mime_type = self.item.get_transcript(transcripts)
         expected_text = textwrap.dedent("""\
             0
             00:00:00,270 --> 00:00:02,720
@@ -755,10 +855,11 @@ class TestGetTranscript(TestVideo):
 
     def test_non_en_with_non_ascii_filename(self):
         self.item.transcript_language = 'zh'
-        self.non_en_file.seek(0)
-        _upload_file(self.non_en_file, self.item_descriptor.location, u"å¡ž.srt")
+        self.srt_file.seek(0)
+        _upload_file(self.srt_file, self.item_descriptor.location, u"å¡ž.srt")
 
-        text, filename, mime_type = self.item.get_transcript()
+        transcripts = self.item.get_transcripts_info()
+        text, filename, mime_type = self.item.get_transcript(transcripts)
         expected_text = textwrap.dedent("""
         0
         00:00:00,12 --> 00:00:00,100
@@ -774,8 +875,9 @@ class TestGetTranscript(TestVideo):
         _upload_sjson_file(good_sjson, self.item.location)
         self.item.sub = _get_subs_id(good_sjson.name)
 
+        transcripts = self.item.get_transcripts_info()
         with self.assertRaises(ValueError):
-            self.item.get_transcript()
+            self.item.get_transcript(transcripts)
 
     def test_key_error(self):
         good_sjson = _create_file(content="""
@@ -794,5 +896,6 @@ class TestGetTranscript(TestVideo):
         _upload_sjson_file(good_sjson, self.item.location)
         self.item.sub = _get_subs_id(good_sjson.name)
 
+        transcripts = self.item.get_transcripts_info()
         with self.assertRaises(KeyError):
-            self.item.get_transcript()
+            self.item.get_transcript(transcripts)
diff --git a/lms/djangoapps/courseware/tests/test_video_mongo.py b/lms/djangoapps/courseware/tests/test_video_mongo.py
index c9b02696f38..36c1eacb2ee 100644
--- a/lms/djangoapps/courseware/tests/test_video_mongo.py
+++ b/lms/djangoapps/courseware/tests/test_video_mongo.py
@@ -9,8 +9,9 @@ from nose.plugins.attrib import attr
 
 from django.conf import settings
 from django.test import TestCase
+from django.test.utils import override_settings
 
-from xmodule.video_module import create_youtube_string, VideoDescriptor
+from xmodule.video_module import VideoDescriptor, bumper_utils, video_utils
 from xmodule.x_module import STUDENT_VIEW
 from xmodule.tests.test_video import VideoDescriptorTestBase
 from xmodule.tests.test_import import DummySystem
@@ -31,43 +32,51 @@ class TestVideoYouTube(TestVideo):
     def test_video_constructor(self):
         """Make sure that all parameters extracted correctly from xml"""
         context = self.item_descriptor.render(STUDENT_VIEW).content
-        sources = json.dumps([u'example.mp4', u'example.webm'])
+        sources = [u'example.mp4', u'example.webm']
 
         expected_context = {
-            'ajax_url': self.item_descriptor.xmodule_runtime.ajax_url + '/save_user_state',
-            'autoplay': settings.FEATURES.get('AUTOPLAY_VIDEOS', False),
             'branding_info': None,
             'license': None,
+            'bumper_metadata': 'null',
             'cdn_eval': False,
             'cdn_exp_group': None,
-            'data_dir': getattr(self, 'data_dir', None),
             'display_name': u'A Name',
-            'end': 3610.0,
-            'id': self.item_descriptor.location.html_id(),
-            'show_captions': 'true',
-            'handout': None,
             'download_video_link': u'example.mp4',
-            'sources': sources,
-            'speed': 'null',
-            'general_speed': 1.0,
-            'start': 3603.0,
-            'saved_video_position': 0.0,
-            'sub': u'a_sub_file.srt.sjson',
+            'handout': None,
+            'id': self.item_descriptor.location.html_id(),
+            'metadata': json.dumps(OrderedDict({
+                "saveStateUrl": self.item_descriptor.xmodule_runtime.ajax_url + "/save_user_state",
+                "autoplay": False,
+                "streams": "0.75:jNCf2gIqpeE,1.00:ZwkTiUPN0mg,1.25:rsq9auxASqI,1.50:kMyNdzVHHgg",
+                "sub": "a_sub_file.srt.sjson",
+                "sources": sources,
+                "captionDataDir": None,
+                "showCaptions": "true",
+                "generalSpeed": 1.0,
+                "speed": None,
+                "savedVideoPosition": 0.0,
+                "start": 3603.0,
+                "end": 3610.0,
+                "transcriptLanguage": "en",
+                "transcriptLanguages": OrderedDict({"en": "English", "uk": u"Українська"}),
+                "ytTestTimeout": 1500,
+                "ytApiUrl": "www.youtube.com/iframe_api",
+                "ytTestUrl": "gdata.youtube.com/feeds/api/videos/",
+                "transcriptTranslationUrl": self.item_descriptor.xmodule_runtime.handler_url(
+                    self.item_descriptor, 'transcript', 'translation/__lang__'
+                ).rstrip('/?'),
+                "transcriptAvailableTranslationsUrl": self.item_descriptor.xmodule_runtime.handler_url(
+                    self.item_descriptor, 'transcript', 'available_translations'
+                ).rstrip('/?'),
+                "autohideHtml5": False,
+            })),
             'track': None,
-            'youtube_streams': create_youtube_string(self.item_descriptor),
-            'yt_test_timeout': 1500,
-            'yt_api_url': 'www.youtube.com/iframe_api',
-            'yt_test_url': 'gdata.youtube.com/feeds/api/videos/',
             'transcript_download_format': 'srt',
-            'transcript_download_formats_list': [{'display_name': 'SubRip (.srt) file', 'value': 'srt'}, {'display_name': 'Text (.txt) file', 'value': 'txt'}],
-            'transcript_language': u'en',
-            'transcript_languages': json.dumps(OrderedDict({"en": "English", "uk": u"Українська"})),
-            'transcript_translation_url': self.item_descriptor.xmodule_runtime.handler_url(
-                self.item_descriptor, 'transcript', 'translation'
-            ).rstrip('/?'),
-            'transcript_available_translations_url': self.item_descriptor.xmodule_runtime.handler_url(
-                self.item_descriptor, 'transcript', 'available_translations'
-            ).rstrip('/?'),
+            'transcript_download_formats_list': [
+                {'display_name': 'SubRip (.srt) file', 'value': 'srt'},
+                {'display_name': 'Text (.txt) file', 'value': 'txt'}
+            ],
+            'poster': 'null',
         }
 
         self.assertEqual(
@@ -100,43 +109,51 @@ class TestVideoNonYouTube(TestVideo):
             the template generates an empty string for the YouTube streams.
         """
         context = self.item_descriptor.render(STUDENT_VIEW).content
-        sources = json.dumps([u'example.mp4', u'example.webm'])
+        sources = [u'example.mp4', u'example.webm']
 
         expected_context = {
-            'ajax_url': self.item_descriptor.xmodule_runtime.ajax_url + '/save_user_state',
             'branding_info': None,
             'license': None,
+            'bumper_metadata': 'null',
             'cdn_eval': False,
             'cdn_exp_group': None,
-            'data_dir': getattr(self, 'data_dir', None),
-            'show_captions': 'true',
-            'handout': None,
             'display_name': u'A Name',
             'download_video_link': u'example.mp4',
-            'end': 3610.0,
+            'handout': None,
             'id': self.item_descriptor.location.html_id(),
-            'sources': sources,
-            'speed': 'null',
-            'general_speed': 1.0,
-            'start': 3603.0,
-            'saved_video_position': 0.0,
-            'sub': u'a_sub_file.srt.sjson',
+            'metadata': json.dumps(OrderedDict({
+                "saveStateUrl": self.item_descriptor.xmodule_runtime.ajax_url + "/save_user_state",
+                "autoplay": False,
+                "streams": "1.00:3_yD_cEKoCk",
+                "sub": "a_sub_file.srt.sjson",
+                "sources": sources,
+                "captionDataDir": None,
+                "showCaptions": "true",
+                "generalSpeed": 1.0,
+                "speed": None,
+                "savedVideoPosition": 0.0,
+                "start": 3603.0,
+                "end": 3610.0,
+                "transcriptLanguage": "en",
+                "transcriptLanguages": OrderedDict({"en": "English"}),
+                "ytTestTimeout": 1500,
+                "ytApiUrl": "www.youtube.com/iframe_api",
+                "ytTestUrl": "gdata.youtube.com/feeds/api/videos/",
+                "transcriptTranslationUrl": self.item_descriptor.xmodule_runtime.handler_url(
+                    self.item_descriptor, 'transcript', 'translation/__lang__'
+                ).rstrip('/?'),
+                "transcriptAvailableTranslationsUrl": self.item_descriptor.xmodule_runtime.handler_url(
+                    self.item_descriptor, 'transcript', 'available_translations'
+                ).rstrip('/?'),
+                "autohideHtml5": False,
+            })),
             'track': None,
-            'youtube_streams': '1.00:3_yD_cEKoCk',
-            'autoplay': settings.FEATURES.get('AUTOPLAY_VIDEOS', True),
-            'yt_test_timeout': 1500,
-            'yt_api_url': 'www.youtube.com/iframe_api',
-            'yt_test_url': 'gdata.youtube.com/feeds/api/videos/',
             'transcript_download_format': 'srt',
-            'transcript_download_formats_list': [{'display_name': 'SubRip (.srt) file', 'value': 'srt'}, {'display_name': 'Text (.txt) file', 'value': 'txt'}],
-            'transcript_language': u'en',
-            'transcript_languages': '{"en": "English"}',
-            'transcript_translation_url': self.item_descriptor.xmodule_runtime.handler_url(
-                self.item_descriptor, 'transcript', 'translation'
-            ).rstrip('/?'),
-            'transcript_available_translations_url': self.item_descriptor.xmodule_runtime.handler_url(
-                self.item_descriptor, 'transcript', 'available_translations'
-            ).rstrip('/?')
+            'transcript_download_formats_list': [
+                {'display_name': 'SubRip (.srt) file', 'value': 'srt'},
+                {'display_name': 'Text (.txt) file', 'value': 'txt'}
+            ],
+            'poster': 'null',
         }
 
         self.assertEqual(
@@ -157,6 +174,32 @@ class TestGetHtmlMethod(BaseTestXmodule):
     def setUp(self):
         super(TestGetHtmlMethod, self).setUp()
         self.setup_course()
+        self.default_metadata_dict = OrderedDict({
+            "saveStateUrl": "",
+            "autoplay": settings.FEATURES.get('AUTOPLAY_VIDEOS', True),
+            "streams": "1.00:3_yD_cEKoCk",
+            "sub": "a_sub_file.srt.sjson",
+            "sources": '[]',
+            "captionDataDir": None,
+            "showCaptions": "true",
+            "generalSpeed": 1.0,
+            "speed": None,
+            "savedVideoPosition": 0.0,
+            "start": 3603.0,
+            "end": 3610.0,
+            "transcriptLanguage": "en",
+            "transcriptLanguages": OrderedDict({"en": "English"}),
+            "ytTestTimeout": 1500,
+            "ytApiUrl": "www.youtube.com/iframe_api",
+            "ytTestUrl": "gdata.youtube.com/feeds/api/videos/",
+            "transcriptTranslationUrl": self.item_descriptor.xmodule_runtime.handler_url(
+                self.item_descriptor, 'transcript', 'translation/__lang__'
+            ).rstrip('/?'),
+            "transcriptAvailableTranslationsUrl": self.item_descriptor.xmodule_runtime.handler_url(
+                self.item_descriptor, 'transcript', 'available_translations'
+            ).rstrip('/?'),
+            "autohideHtml5": False,
+        })
 
     def test_get_html_track(self):
         SOURCE_XML = """
@@ -209,36 +252,31 @@ class TestGetHtmlMethod(BaseTestXmodule):
                 'transcripts': '<transcript language="uk" src="ukrainian.srt" />',
             },
         ]
-        sources = json.dumps([u'example.mp4', u'example.webm'])
+        sources = [u'example.mp4', u'example.webm']
 
         expected_context = {
             'branding_info': None,
             'license': None,
+            'bumper_metadata': 'null',
             'cdn_eval': False,
             'cdn_exp_group': None,
-            'data_dir': getattr(self, 'data_dir', None),
-            'show_captions': 'true',
-            'handout': None,
             'display_name': u'A Name',
             'download_video_link': u'example.mp4',
-            'end': 3610.0,
-            'id': None,
-            'sources': sources,
-            'start': 3603.0,
-            'saved_video_position': 0.0,
-            'sub': u'a_sub_file.srt.sjson',
-            'speed': 'null',
-            'general_speed': 1.0,
-            'track': u'http://www.example.com/track',
-            'youtube_streams': '1.00:3_yD_cEKoCk',
-            'autoplay': settings.FEATURES.get('AUTOPLAY_VIDEOS', True),
-            'yt_test_timeout': 1500,
-            'yt_api_url': 'www.youtube.com/iframe_api',
-            'yt_test_url': 'gdata.youtube.com/feeds/api/videos/',
-            'transcript_download_formats_list': [{'display_name': 'SubRip (.srt) file', 'value': 'srt'}, {'display_name': 'Text (.txt) file', 'value': 'txt'}],
+            'handout': None,
+            'id': self.item_descriptor.location.html_id(),
+            'metadata': '',
+            'track': None,
+            'transcript_download_format': 'srt',
+            'transcript_download_formats_list': [
+                {'display_name': 'SubRip (.srt) file', 'value': 'srt'},
+                {'display_name': 'Text (.txt) file', 'value': 'txt'}
+            ],
+            'poster': 'null',
         }
 
         for data in cases:
+            metadata = self.default_metadata_dict
+            metadata['sources'] = sources
             DATA = SOURCE_XML.format(
                 download_track=data['download_track'],
                 track=data['track'],
@@ -252,22 +290,29 @@ class TestGetHtmlMethod(BaseTestXmodule):
             ).rstrip('/?')
 
             context = self.item_descriptor.render(STUDENT_VIEW).content
-
-            expected_context.update({
-                'transcript_download_format': None if self.item_descriptor.track and self.item_descriptor.download_track else 'srt',
-                'transcript_languages': '{"en": "English"}' if not data['transcripts'] else json.dumps({"uk": u'Українська'}),
-                'transcript_language': u'en' if not data['transcripts'] or data.get('sub') else u'uk',
-                'transcript_translation_url': self.item_descriptor.xmodule_runtime.handler_url(
-                    self.item_descriptor, 'transcript', 'translation'
+            metadata.update({
+                'transcriptLanguages': {"en": "English"} if not data['transcripts'] else {"uk": u'Українська'},
+                'transcriptLanguage': u'en' if not data['transcripts'] or data.get('sub') else u'uk',
+                'transcriptTranslationUrl': self.item_descriptor.xmodule_runtime.handler_url(
+                    self.item_descriptor, 'transcript', 'translation/__lang__'
                 ).rstrip('/?'),
-                'transcript_available_translations_url': self.item_descriptor.xmodule_runtime.handler_url(
+                'transcriptAvailableTranslationsUrl': self.item_descriptor.xmodule_runtime.handler_url(
                     self.item_descriptor, 'transcript', 'available_translations'
                 ).rstrip('/?'),
-                'ajax_url': self.item_descriptor.xmodule_runtime.ajax_url + '/save_user_state',
-                'track': track_url if data['expected_track_url'] == u'a_sub_file.srt.sjson' else data['expected_track_url'],
+                'saveStateUrl': self.item_descriptor.xmodule_runtime.ajax_url + '/save_user_state',
                 'sub': data['sub'],
+            })
+            expected_context.update({
+                'transcript_download_format': (
+                    None if self.item_descriptor.track and self.item_descriptor.download_track else 'srt'
+                ),
+                'track': (
+                    track_url if data['expected_track_url'] == u'a_sub_file.srt.sjson' else data['expected_track_url']
+                ),
                 'id': self.item_descriptor.location.html_id(),
+                'metadata': json.dumps(metadata)
             })
+
             self.assertEqual(
                 context,
                 self.item_descriptor.xmodule_runtime.render_template('video.html', expected_context),
@@ -295,7 +340,7 @@ class TestGetHtmlMethod(BaseTestXmodule):
                 """,
                 'result': {
                     'download_video_link': u'example_source.mp4',
-                    'sources': json.dumps([u'example.mp4', u'example.webm']),
+                    'sources': [u'example.mp4', u'example.webm'],
                 },
             },
             {
@@ -307,7 +352,7 @@ class TestGetHtmlMethod(BaseTestXmodule):
                 """,
                 'result': {
                     'download_video_link': u'example.mp4',
-                    'sources': json.dumps([u'example.mp4', u'example.webm']),
+                    'sources': [u'example.mp4', u'example.webm'],
                 },
             },
             {
@@ -326,7 +371,7 @@ class TestGetHtmlMethod(BaseTestXmodule):
                     <source src="example.webm"/>
                 """,
                 'result': {
-                    'sources': json.dumps([u'example.mp4', u'example.webm']),
+                    'sources': [u'example.mp4', u'example.webm'],
                 },
             },
         ]
@@ -334,31 +379,21 @@ class TestGetHtmlMethod(BaseTestXmodule):
         initial_context = {
             'branding_info': None,
             'license': None,
+            'bumper_metadata': 'null',
             'cdn_eval': False,
             'cdn_exp_group': None,
-            'data_dir': getattr(self, 'data_dir', None),
-            'show_captions': 'true',
-            'handout': None,
             'display_name': u'A Name',
-            'download_video_link': None,
-            'end': 3610.0,
-            'id': None,
-            'sources': '[]',
-            'speed': 'null',
-            'general_speed': 1.0,
-            'start': 3603.0,
-            'saved_video_position': 0.0,
-            'sub': u'a_sub_file.srt.sjson',
+            'download_video_link': u'example.mp4',
+            'handout': None,
+            'id': self.item_descriptor.location.html_id(),
+            'metadata': self.default_metadata_dict,
             'track': None,
-            'youtube_streams': '1.00:3_yD_cEKoCk',
-            'autoplay': settings.FEATURES.get('AUTOPLAY_VIDEOS', True),
-            'yt_test_timeout': 1500,
-            'yt_api_url': 'www.youtube.com/iframe_api',
-            'yt_test_url': 'gdata.youtube.com/feeds/api/videos/',
             'transcript_download_format': 'srt',
-            'transcript_download_formats_list': [{'display_name': 'SubRip (.srt) file', 'value': 'srt'}, {'display_name': 'Text (.txt) file', 'value': 'txt'}],
-            'transcript_language': u'en',
-            'transcript_languages': '{"en": "English"}',
+            'transcript_download_formats_list': [
+                {'display_name': 'SubRip (.srt) file', 'value': 'srt'},
+                {'display_name': 'Text (.txt) file', 'value': 'txt'}
+            ],
+            'poster': 'null',
         }
 
         for data in cases:
@@ -371,17 +406,21 @@ class TestGetHtmlMethod(BaseTestXmodule):
             context = self.item_descriptor.render(STUDENT_VIEW).content
 
             expected_context = dict(initial_context)
-            expected_context.update({
-                'transcript_translation_url': self.item_descriptor.xmodule_runtime.handler_url(
-                    self.item_descriptor, 'transcript', 'translation'
+            expected_context['metadata'].update({
+                'transcriptTranslationUrl': self.item_descriptor.xmodule_runtime.handler_url(
+                    self.item_descriptor, 'transcript', 'translation/__lang__'
                 ).rstrip('/?'),
-                'transcript_available_translations_url': self.item_descriptor.xmodule_runtime.handler_url(
+                'transcriptAvailableTranslationsUrl': self.item_descriptor.xmodule_runtime.handler_url(
                     self.item_descriptor, 'transcript', 'available_translations'
                 ).rstrip('/?'),
-                'ajax_url': self.item_descriptor.xmodule_runtime.ajax_url + '/save_user_state',
+                'saveStateUrl': self.item_descriptor.xmodule_runtime.ajax_url + '/save_user_state',
+                'sources': data['result'].get('sources', []),
+            })
+            expected_context.update({
                 'id': self.item_descriptor.location.html_id(),
+                'download_video_link': data['result'].get('download_video_link'),
+                'metadata': json.dumps(expected_context['metadata'])
             })
-            expected_context.update(data['result'])
 
             self.assertEqual(
                 context,
@@ -413,7 +452,7 @@ class TestGetHtmlMethod(BaseTestXmodule):
             'edx_video_id': "meow",
             'result': {
                 'download_video_link': u'example_source.mp4',
-                'sources': json.dumps([u'example.mp4', u'example.webm']),
+                'sources': [u'example.mp4', u'example.webm'],
             }
         }
         DATA = SOURCE_XML.format(
@@ -469,39 +508,32 @@ class TestGetHtmlMethod(BaseTestXmodule):
             'result': {
                 'download_video_link': None,
                 # make sure the desktop_mp4 url is included as part of the alternative sources.
-                'sources': json.dumps([u'example.mp4', u'example.webm', u'http://www.meowmix.com']),
+                'sources': [u'example.mp4', u'example.webm', u'http://www.meowmix.com'],
             }
         }
 
         # Video found for edx_video_id
+        metadata = self.default_metadata_dict
+        metadata['autoplay'] = False
+        metadata['sources'] = ""
         initial_context = {
             'branding_info': None,
             'license': None,
+            'bumper_metadata': 'null',
             'cdn_eval': False,
             'cdn_exp_group': None,
-            'data_dir': getattr(self, 'data_dir', None),
-            'show_captions': 'true',
-            'handout': None,
             'display_name': u'A Name',
-            'download_video_link': None,
-            'end': 3610.0,
-            'id': None,
-            'sources': '[]',
-            'speed': 'null',
-            'general_speed': 1.0,
-            'start': 3603.0,
-            'saved_video_position': 0.0,
-            'sub': u'a_sub_file.srt.sjson',
+            'download_video_link': u'example.mp4',
+            'handout': None,
+            'id': self.item_descriptor.location.html_id(),
             'track': None,
-            'youtube_streams': '1.00:3_yD_cEKoCk',
-            'autoplay': settings.FEATURES.get('AUTOPLAY_VIDEOS', True),
-            'yt_test_timeout': 1500,
-            'yt_api_url': 'www.youtube.com/iframe_api',
-            'yt_test_url': 'gdata.youtube.com/feeds/api/videos/',
             'transcript_download_format': 'srt',
-            'transcript_download_formats_list': [{'display_name': 'SubRip (.srt) file', 'value': 'srt'}, {'display_name': 'Text (.txt) file', 'value': 'txt'}],
-            'transcript_language': u'en',
-            'transcript_languages': '{"en": "English"}',
+            'transcript_download_formats_list': [
+                {'display_name': 'SubRip (.srt) file', 'value': 'srt'},
+                {'display_name': 'Text (.txt) file', 'value': 'txt'}
+            ],
+            'poster': 'null',
+            'metadata': metadata
         }
 
         DATA = SOURCE_XML.format(
@@ -514,17 +546,21 @@ class TestGetHtmlMethod(BaseTestXmodule):
         context = self.item_descriptor.render(STUDENT_VIEW).content
 
         expected_context = dict(initial_context)
-        expected_context.update({
-            'transcript_translation_url': self.item_descriptor.xmodule_runtime.handler_url(
-                self.item_descriptor, 'transcript', 'translation'
+        expected_context['metadata'].update({
+            'transcriptTranslationUrl': self.item_descriptor.xmodule_runtime.handler_url(
+                self.item_descriptor, 'transcript', 'translation/__lang__'
             ).rstrip('/?'),
-            'transcript_available_translations_url': self.item_descriptor.xmodule_runtime.handler_url(
+            'transcriptAvailableTranslationsUrl': self.item_descriptor.xmodule_runtime.handler_url(
                 self.item_descriptor, 'transcript', 'available_translations'
             ).rstrip('/?'),
-            'ajax_url': self.item_descriptor.xmodule_runtime.ajax_url + '/save_user_state',
+            'saveStateUrl': self.item_descriptor.xmodule_runtime.ajax_url + '/save_user_state',
+            'sources': data['result']['sources'],
+        })
+        expected_context.update({
             'id': self.item_descriptor.location.html_id(),
+            'download_video_link': data['result']['download_video_link'],
+            'metadata': json.dumps(expected_context['metadata'])
         })
-        expected_context.update(data['result'])
 
         self.assertEqual(
             context,
@@ -579,42 +615,32 @@ class TestGetHtmlMethod(BaseTestXmodule):
             'result': {
                 'download_video_link': u'http://fake-video.edx.org/thundercats.mp4',
                 # make sure the urls for the various encodings are included as part of the alternative sources.
-                'sources': json.dumps(
-                    [u'example.mp4', u'example.webm'] +
-                    [video['url'] for video in encoded_videos]
-                ),
+                'sources': [u'example.mp4', u'example.webm'] +
+                           [video['url'] for video in encoded_videos],
             }
         }
 
         # Video found for edx_video_id
+        metadata = self.default_metadata_dict
+        metadata['sources'] = ""
         initial_context = {
             'branding_info': None,
             'license': None,
+            'bumper_metadata': 'null',
             'cdn_eval': False,
             'cdn_exp_group': None,
-            'data_dir': getattr(self, 'data_dir', None),
-            'show_captions': 'true',
-            'handout': None,
             'display_name': u'A Name',
-            'download_video_link': None,
-            'end': 3610.0,
-            'id': None,
-            'sources': '[]',
-            'speed': 'null',
-            'general_speed': 1.0,
-            'start': 3603.0,
-            'saved_video_position': 0.0,
-            'sub': u'a_sub_file.srt.sjson',
+            'download_video_link': u'example.mp4',
+            'handout': None,
+            'id': self.item_descriptor.location.html_id(),
             'track': None,
-            'youtube_streams': '1.00:3_yD_cEKoCk',
-            'autoplay': settings.FEATURES.get('AUTOPLAY_VIDEOS', True),
-            'yt_test_timeout': 1500,
-            'yt_api_url': 'www.youtube.com/iframe_api',
-            'yt_test_url': 'gdata.youtube.com/feeds/api/videos/',
             'transcript_download_format': 'srt',
-            'transcript_download_formats_list': [{'display_name': 'SubRip (.srt) file', 'value': 'srt'}, {'display_name': 'Text (.txt) file', 'value': 'txt'}],
-            'transcript_language': u'en',
-            'transcript_languages': '{"en": "English"}',
+            'transcript_download_formats_list': [
+                {'display_name': 'SubRip (.srt) file', 'value': 'srt'},
+                {'display_name': 'Text (.txt) file', 'value': 'txt'}
+            ],
+            'poster': 'null',
+            'metadata': metadata,
         }
 
         DATA = SOURCE_XML.format(
@@ -627,17 +653,21 @@ class TestGetHtmlMethod(BaseTestXmodule):
         context = self.item_descriptor.render(STUDENT_VIEW).content
 
         expected_context = dict(initial_context)
-        expected_context.update({
-            'transcript_translation_url': self.item_descriptor.xmodule_runtime.handler_url(
-                self.item_descriptor, 'transcript', 'translation'
+        expected_context['metadata'].update({
+            'transcriptTranslationUrl': self.item_descriptor.xmodule_runtime.handler_url(
+                self.item_descriptor, 'transcript', 'translation/__lang__'
             ).rstrip('/?'),
-            'transcript_available_translations_url': self.item_descriptor.xmodule_runtime.handler_url(
+            'transcriptAvailableTranslationsUrl': self.item_descriptor.xmodule_runtime.handler_url(
                 self.item_descriptor, 'transcript', 'available_translations'
             ).rstrip('/?'),
-            'ajax_url': self.item_descriptor.xmodule_runtime.ajax_url + '/save_user_state',
+            'saveStateUrl': self.item_descriptor.xmodule_runtime.ajax_url + '/save_user_state',
+            'sources': data['result']['sources'],
+        })
+        expected_context.update({
             'id': self.item_descriptor.location.html_id(),
+            'download_video_link': data['result']['download_video_link'],
+            'metadata': json.dumps(expected_context['metadata'])
         })
-        expected_context.update(data['result'])
 
         self.assertEqual(
             context,
@@ -690,12 +720,10 @@ class TestGetHtmlMethod(BaseTestXmodule):
             """,
             'result': {
                 'download_video_link': u'example_source.mp4',
-                'sources': json.dumps(
-                    [
-                        u'http://cdn_example.com/example.mp4',
-                        u'http://cdn_example.com/example.webm'
-                    ]
-                ),
+                'sources': [
+                    u'http://cdn_example.com/example.mp4',
+                    u'http://cdn_example.com/example.webm'
+                ],
             },
         }
 
@@ -712,31 +740,21 @@ class TestGetHtmlMethod(BaseTestXmodule):
                 'url': 'http://www.xuetangx.com'
             },
             'license': None,
+            'bumper_metadata': 'null',
             'cdn_eval': False,
             'cdn_exp_group': None,
-            'data_dir': getattr(self, 'data_dir', None),
-            'show_captions': 'true',
-            'handout': None,
             'display_name': u'A Name',
             'download_video_link': None,
-            'end': 3610.0,
+            'handout': None,
             'id': None,
-            'sources': '[]',
-            'speed': 'null',
-            'general_speed': 1.0,
-            'start': 3603.0,
-            'saved_video_position': 0.0,
-            'sub': u'a_sub_file.srt.sjson',
+            'metadata': self.default_metadata_dict,
             'track': None,
-            'youtube_streams': '1.00:3_yD_cEKoCk',
-            'autoplay': settings.FEATURES.get('AUTOPLAY_VIDEOS', True),
-            'yt_test_timeout': 1500,
-            'yt_api_url': 'www.youtube.com/iframe_api',
-            'yt_test_url': 'gdata.youtube.com/feeds/api/videos/',
             'transcript_download_format': 'srt',
-            'transcript_download_formats_list': [{'display_name': 'SubRip (.srt) file', 'value': 'srt'}, {'display_name': 'Text (.txt) file', 'value': 'txt'}],
-            'transcript_language': u'en',
-            'transcript_languages': '{"en": "English"}',
+            'transcript_download_formats_list': [
+                {'display_name': 'SubRip (.srt) file', 'value': 'srt'},
+                {'display_name': 'Text (.txt) file', 'value': 'txt'}
+            ],
+            'poster': 'null',
         }
 
         for data in cases:
@@ -748,21 +766,23 @@ class TestGetHtmlMethod(BaseTestXmodule):
             )
             self.initialize_module(data=DATA)
             self.item_descriptor.xmodule_runtime.user_location = 'CN'
-
             context = self.item_descriptor.render('student_view').content
-
             expected_context = dict(initial_context)
-            expected_context.update({
-                'transcript_translation_url': self.item_descriptor.xmodule_runtime.handler_url(
-                    self.item_descriptor, 'transcript', 'translation'
+            expected_context['metadata'].update({
+                'transcriptTranslationUrl': self.item_descriptor.xmodule_runtime.handler_url(
+                    self.item_descriptor, 'transcript', 'translation/__lang__'
                 ).rstrip('/?'),
-                'transcript_available_translations_url': self.item_descriptor.xmodule_runtime.handler_url(
+                'transcriptAvailableTranslationsUrl': self.item_descriptor.xmodule_runtime.handler_url(
                     self.item_descriptor, 'transcript', 'available_translations'
                 ).rstrip('/?'),
-                'ajax_url': self.item_descriptor.xmodule_runtime.ajax_url + '/save_user_state',
+                'saveStateUrl': self.item_descriptor.xmodule_runtime.ajax_url + '/save_user_state',
+                'sources': data['result'].get('sources', []),
+            })
+            expected_context.update({
                 'id': self.item_descriptor.location.html_id(),
+                'download_video_link': data['result'].get('download_video_link'),
+                'metadata': json.dumps(expected_context['metadata'])
             })
-            expected_context.update(data['result'])
 
             self.assertEqual(
                 context,
@@ -948,3 +968,125 @@ class VideoDescriptorTest(TestCase, VideoDescriptorTestBase):
             VideoDescriptor.from_xml(xml_data, module_system, id_generator=Mock())
         with self.assertRaises(ValVideoNotFoundError):
             get_video_info("test_edx_video_id")
+
+
+class TestVideoWithBumper(TestVideo):
+    """
+    Tests rendered content in presence of video bumper.
+    """
+    CATEGORY = "video"
+    METADATA = {}
+    FEATURES = settings.FEATURES
+
+    @patch('xmodule.video_module.bumper_utils.get_bumper_settings')
+    def test_is_bumper_enabled(self, get_bumper_settings):
+        """
+        Check that bumper is (not)shown if ENABLE_VIDEO_BUMPER is (False)True
+
+        Assume that bumper settings are correct.
+        """
+        self.FEATURES.update({
+            "SHOW_BUMPER_PERIODICITY": 1,
+            "ENABLE_VIDEO_BUMPER": True,
+        })
+
+        get_bumper_settings.return_value = {
+            "video_id": "edx_video_id",
+            "transcripts": {},
+        }
+        with override_settings(FEATURES=self.FEATURES):
+            self.assertTrue(bumper_utils.is_bumper_enabled(self.item_descriptor))
+
+        self.FEATURES.update({"ENABLE_VIDEO_BUMPER": False})
+
+        with override_settings(FEATURES=self.FEATURES):
+            self.assertFalse(bumper_utils.is_bumper_enabled(self.item_descriptor))
+
+    @patch('xmodule.video_module.bumper_utils.is_bumper_enabled')
+    @patch('xmodule.video_module.bumper_utils.get_bumper_settings')
+    @patch('edxval.api.get_urls_for_profiles')
+    def test_bumper_metadata(self, get_url_for_profiles, get_bumper_settings, is_bumper_enabled):
+        """
+        Test content with rendered bumper metadata.
+        """
+        get_url_for_profiles.return_value = {
+            "desktop_mp4": "http://test_bumper.mp4",
+            "desktop_webm": "",
+        }
+
+        get_bumper_settings.return_value = {
+            "video_id": "edx_video_id",
+            "transcripts": {},
+        }
+
+        is_bumper_enabled.return_value = True
+
+        content = self.item_descriptor.render(STUDENT_VIEW).content
+        sources = [u'example.mp4', u'example.webm']
+        expected_context = {
+            'branding_info': None,
+            'license': None,
+            'bumper_metadata': json.dumps(OrderedDict({
+                'saveStateUrl': self.item_descriptor.xmodule_runtime.ajax_url + '/save_user_state',
+                "showCaptions": "true",
+                "sources": ["http://test_bumper.mp4"],
+                'streams': '',
+                "transcriptLanguage": "en",
+                "transcriptLanguages": {"en": "English"},
+                "transcriptTranslationUrl": video_utils.set_query_parameter(
+                    self.item_descriptor.xmodule_runtime.handler_url(
+                        self.item_descriptor, 'transcript', 'translation/__lang__'
+                    ).rstrip('/?'), 'is_bumper', 1
+                ),
+                "transcriptAvailableTranslationsUrl": video_utils.set_query_parameter(
+                    self.item_descriptor.xmodule_runtime.handler_url(
+                        self.item_descriptor, 'transcript', 'available_translations'
+                    ).rstrip('/?'), 'is_bumper', 1
+                ),
+            })),
+            'cdn_eval': False,
+            'cdn_exp_group': None,
+            'display_name': u'A Name',
+            'download_video_link': u'example.mp4',
+            'handout': None,
+            'id': self.item_descriptor.location.html_id(),
+            'metadata': json.dumps(OrderedDict({
+                "saveStateUrl": self.item_descriptor.xmodule_runtime.ajax_url + "/save_user_state",
+                "autoplay": False,
+                "streams": "0.75:jNCf2gIqpeE,1.00:ZwkTiUPN0mg,1.25:rsq9auxASqI,1.50:kMyNdzVHHgg",
+                "sub": "a_sub_file.srt.sjson",
+                "sources": sources,
+                "captionDataDir": None,
+                "showCaptions": "true",
+                "generalSpeed": 1.0,
+                "speed": None,
+                "savedVideoPosition": 0.0,
+                "start": 3603.0,
+                "end": 3610.0,
+                "transcriptLanguage": "en",
+                "transcriptLanguages": OrderedDict({"en": "English", "uk": u"Українська"}),
+                "ytTestTimeout": 1500,
+                "ytApiUrl": "www.youtube.com/iframe_api",
+                "ytTestUrl": "gdata.youtube.com/feeds/api/videos/",
+                "transcriptTranslationUrl": self.item_descriptor.xmodule_runtime.handler_url(
+                    self.item_descriptor, 'transcript', 'translation/__lang__'
+                ).rstrip('/?'),
+                "transcriptAvailableTranslationsUrl": self.item_descriptor.xmodule_runtime.handler_url(
+                    self.item_descriptor, 'transcript', 'available_translations'
+                ).rstrip('/?'),
+                "autohideHtml5": False,
+            })),
+            'track': None,
+            'transcript_download_format': 'srt',
+            'transcript_download_formats_list': [
+                {'display_name': 'SubRip (.srt) file', 'value': 'srt'},
+                {'display_name': 'Text (.txt) file', 'value': 'txt'}
+            ],
+            'poster': json.dumps(OrderedDict({
+                "url": "http://img.youtube.com/vi/ZwkTiUPN0mg/0.jpg",
+                "type": "youtube"
+            }))
+        }
+
+        expected_content = self.item_descriptor.xmodule_runtime.render_template('video.html', expected_context)
+        self.assertEqual(content, expected_content)
diff --git a/lms/djangoapps/mobile_api/video_outlines/serializers.py b/lms/djangoapps/mobile_api/video_outlines/serializers.py
index 73f7089df69..167abf3e23d 100644
--- a/lms/djangoapps/mobile_api/video_outlines/serializers.py
+++ b/lms/djangoapps/mobile_api/video_outlines/serializers.py
@@ -206,7 +206,8 @@ def video_summary(video_profiles, course_id, video_descriptor, request, local_ca
     size = default_encoded_video.get('file_size', 0)
 
     # Transcripts...
-    transcript_langs = video_descriptor.available_translations(verify_assets=False)
+    transcripts_info = video_descriptor.get_transcripts_info()
+    transcript_langs = video_descriptor.available_translations(transcripts_info, verify_assets=False)
 
     transcripts = {
         lang: reverse(
@@ -227,7 +228,7 @@ def video_summary(video_profiles, course_id, video_descriptor, request, local_ca
         "duration": duration,
         "size": size,
         "transcripts": transcripts,
-        "language": video_descriptor.get_default_transcript_language(),
+        "language": video_descriptor.get_default_transcript_language(transcripts_info),
         "encoded_videos": video_data.get('profiles')
     }
     ret.update(always_available_data)
diff --git a/lms/djangoapps/mobile_api/video_outlines/views.py b/lms/djangoapps/mobile_api/video_outlines/views.py
index ae1d7361c6a..f0a25a77af8 100644
--- a/lms/djangoapps/mobile_api/video_outlines/views.py
+++ b/lms/djangoapps/mobile_api/video_outlines/views.py
@@ -119,7 +119,8 @@ class VideoTranscripts(generics.RetrieveAPIView):
         )
         try:
             video_descriptor = modulestore().get_item(usage_key)
-            content, filename, mimetype = video_descriptor.get_transcript(lang=lang)
+            transcripts = video_descriptor.get_transcripts_info()
+            content, filename, mimetype = video_descriptor.get_transcript(transcripts, lang=lang)
         except (NotFoundError, ValueError, KeyError):
             raise Http404(u"Transcript not found for {}, lang: {}".format(block_id, lang))
 
diff --git a/lms/envs/bok_choy.py b/lms/envs/bok_choy.py
index 3a1c1a450ed..ce93aac8da5 100644
--- a/lms/envs/bok_choy.py
+++ b/lms/envs/bok_choy.py
@@ -123,6 +123,11 @@ FEATURES['ENABLE_MAX_FAILED_LOGIN_ATTEMPTS'] = False
 FEATURES['SQUELCH_PII_IN_LOGS'] = False
 FEATURES['PREVENT_CONCURRENT_LOGINS'] = False
 FEATURES['ADVANCED_SECURITY'] = False
+
+FEATURES['ENABLE_MOBILE_REST_API'] = True  # Show video bumper in LMS
+FEATURES['ENABLE_VIDEO_BUMPER'] = True  # Show video bumper in LMS
+FEATURES['SHOW_BUMPER_PERIODICITY'] = 1
+
 PASSWORD_MIN_LENGTH = None
 PASSWORD_COMPLEXITY = {}
 
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 3e0d69b7d0c..74dac034260 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -392,6 +392,13 @@ FEATURES = {
 
     # Teams feature
     'ENABLE_TEAMS': False,
+
+    # Show video bumper in LMS
+    'ENABLE_VIDEO_BUMPER': False,
+
+    # How many seconds to show the bumper again, default is 7 days:
+    'SHOW_BUMPER_PERIODICITY': 7 * 24 * 3600,
+
 }
 
 # Ignore static asset files on import which match this pattern
@@ -1665,6 +1672,8 @@ YOUTUBE = {
             'v': 'set_youtube_id_of_11_symbols_here',
         },
     },
+
+    'IMAGE_API': 'http://img.youtube.com/vi/{youtube_id}/0.jpg',  # /maxresdefault.jpg for 1920*1080
 }
 
 ################################### APPS ######################################
diff --git a/lms/templates/video.html b/lms/templates/video.html
index 9936b97b7d4..48c069a8739 100644
--- a/lms/templates/video.html
+++ b/lms/templates/video.html
@@ -7,47 +7,9 @@
 <div
     id="video_${id}"
     class="video closed"
-
-    data-streams="${youtube_streams}"
-
-    % if sub:
-      data-sub="${sub}"
-    % endif
-    % if autoplay:
-      data-autoplay="${autoplay}"
-    % endif
-
-    data-sources='${sources}'
-    data-save-state-url="${ajax_url}"
-    data-caption-data-dir="${data_dir}"
-    data-show-captions="${show_captions}"
-    data-general-speed="${general_speed}"
-    data-speed="${speed}"
-    data-saved-video-position="${saved_video_position}"
-    data-start="${start}"
-    data-end="${end}"
-    data-transcript-language="${transcript_language}"
-    data-transcript-languages='${transcript_languages}'
-    data-autoplay="${autoplay}"
-    data-yt-test-timeout="${yt_test_timeout}"
-    data-yt-api-url="${yt_api_url}"
-    data-yt-test-url="${yt_test_url}"
-    data-transcript-translation-url="${transcript_translation_url}"
-    data-transcript-available-translations-url="${transcript_available_translations_url}"
-
-    ## For now, the option "data-autohide-html5" is hard coded. This option
-    ## either enables or disables autohiding of controls and captions on mouse
-    ## inactivity. If set to true, controls and captions will autohide for
-    ## HTML5 sources (non-YouTube) after a period of mouse inactivity over the
-    ## whole video. When the mouse moves (or a key is pressed while any part of
-    ## the video player is focused), the captions and controls will be shown
-    ## once again.
-    ##
-    ## There is no option in the "Advanced Editor" to set this option. However,
-    ## this option will have an effect if changed to "True". The code on
-    ## front-end exists.
-    data-autohide-html5="False"
-
+    data-metadata='${metadata}'
+    data-bumper-metadata='${bumper_metadata}'
+    data-poster='${poster}'
     tabindex="-1"
 >
     <div class="focus_grabber first"></div>
@@ -65,41 +27,13 @@
           </section>
           <div class="video-player-post"></div>
           <section class="video-controls is-hidden">
-              <div class="slider" title="${_('Video position')}"></div>
               <div>
-                  <ul class="vcr">
-                      <li><a class="video_control" href="#" title="${_('Play')}" role="button" aria-disabled="false"></a></li>
-                      <li><div class="vidtime">0:00 / 0:00</div></li>
-                  </ul>
-                  <div class="secondary-controls">
-                      <div class="speeds menu-container">
-                          <a class="speed-button" href="#" title="${_('Speeds')}" role="button" aria-disabled="false">
-                              <span class="label">${_('Speed')}</span>
-                              <span class="value"></span>
-                          </a>
-                          <ol class="video-speeds menu" role="menu"></ol>
-                      </div>
-                      <div class="volume">
-                          <a href="#" role="button" aria-disabled="false" title="${_('Volume')}" aria-label="${_('Click on this button to mute or unmute this video or press UP or DOWN buttons to increase or decrease volume level.')}"></a>
-                          <div role="presentation" class="volume-slider-container">
-                              <div class="volume-slider"></div>
-                          </div>
-                      </div>
-                      <a href="#" class="add-fullscreen" title="${_('Fill browser')}" role="button" aria-disabled="false">${_('Fill browser')}</a>
-                      <a href="#" class="quality-control is-hidden" title="${_('HD off')}" role="button" aria-disabled="false">${_('HD off')}</a>
-
-                      <div class="lang menu-container">
-                        <a href="#" class="hide-subtitles" title="${_('Turn off captions')}" role="button" aria-disabled="false">${_('Turn off captions')}</a>
-                      </div>
-                  </div>
+                  <div class="vcr"><div class="vidtime">0:00 / 0:00</div></div>
+                  <div class="secondary-controls"></div>
               </div>
           </section>
           <a class="nav-skip sr" id="before-transcript_${id}" href="#after-transcript_${id}">${_('Skip to end of transcript.')}</a>
       </article>
-
-      <ol id="transcript-captions" class="subtitles" tabindex="0" role="group" aria-label="${_('Activating an item in this group will spool the video to the corresponding time point. To skip transcript, go to previous item.')}">
-        <li></li>
-      </ol>
     </div>
 
     <a class="nav-skip sr" id="after-transcript_${id}" href="#before-transcript_${id}">${_('Go back to start of transcript.')}</a>
@@ -116,8 +50,8 @@
           % if transcript_download_format:
             <a href="${track}">${_('Download transcript')}</a>
             <div class="a11y-menu-container">
-                <a class="a11y-menu-button" href="#" title="${'.' + transcript_download_format}">${'.' + transcript_download_format}</a>
-                <ol class="a11y-menu-list">
+                <a class="a11y-menu-button" href="#" title="${'.' + transcript_download_format}" role="button" aria-disabled="false">${'.' + transcript_download_format}</a>
+                <ol class="a11y-menu-list" role="menu">
                 % for item in transcript_download_formats_list:
                   % if item['value'] == transcript_download_format:
                   <li class="a11y-menu-item active">
@@ -126,7 +60,7 @@
                   % endif
                   ## This is necessary so we don't scrape 'display_name' as a string.
                   <% dname = item['display_name'] %>
-                      <a class="a11y-menu-item-link" href="#${item['value']}" title="${_(dname)}" data-value="${item['value']}">
+                      <a class="a11y-menu-item-link" href="#${item['value']}" title="${_(dname)}" data-value="${item['value']}" role="menuitem" aria-disabled="false">
                         ${_(dname)}
                       </a>
                   </li>
-- 
GitLab