Skip to content

Commit

Permalink
fix: setup.py update using script
Browse files Browse the repository at this point in the history
  • Loading branch information
edx-requirements-bot committed Sep 6, 2023
1 parent 939ad5b commit 81a6472
Show file tree
Hide file tree
Showing 10 changed files with 510 additions and 6 deletions.
6 changes: 6 additions & 0 deletions build/lib/invideoquiz/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""
Runtime will load the XBlock class from here.
"""
from .invideoquiz import InVideoQuizXBlock

__version__ = '1.3.1'
194 changes: 194 additions & 0 deletions build/lib/invideoquiz/invideoquiz.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
"""
This XBlock allows for edX components to be displayed to users inside of
videos at specific time points.
"""

import os
import json
import pkg_resources

from xblock.core import XBlock
from xblock.fields import Scope
from xblock.fields import String
from xblock.fragment import Fragment
from xblock.validation import ValidationMessage
from xblockutils.studio_editable import StudioEditableXBlockMixin

from .utils import _


def get_resource_string(path):
"""
Retrieve string contents for the file path
"""
path = os.path.join('public', path)
resource_string = pkg_resources.resource_string(__name__, path)
return resource_string.decode('utf8')


class InVideoQuizXBlock(StudioEditableXBlockMixin, XBlock):
# pylint: disable=too-many-ancestors
"""
Display CAPA problems within a video component at a specified time.
"""

show_in_read_only_mode = True

display_name = String(
display_name=_('Display Name'),
default=_('In-Video Quiz XBlock'),
scope=Scope.settings,
)

video_id = String(
display_name=_('Video Location'),
default='',
scope=Scope.settings,
help=_(
'This is the component ID for the video in which '
'you want to insert your quiz questions. It can be '
'obtained from staff debug info of the video in the LMS.'
),
)

timemap = String(
display_name=_('Problem Timemap'),
default='{}',
scope=Scope.settings,
help=_(
'A simple string field to define problem IDs '
'and their time maps (in seconds) as JSON. '
'Example: {"60": "50srvqlii4ru9gonprp35gkcfyd5weju"} '
'Problem IDs can be obatined from staff debug info of '
'the problems in the LMS.'
),
multiline_editor=True,
)

editable_fields = [
'video_id',
'timemap',
]

def validate_field_data(self, validation, data):
"""
Validate the user-submitted timemap.
"""
try:
json.loads(data.timemap)
except ValueError:
_ = self.runtime.service(self, "i18n").ugettext
validation.add(ValidationMessage(ValidationMessage.ERROR, str(
_("Invalid Timemap")
)))

# Decorate the view in order to support multiple devices e.g. mobile
# See: https://openedx.atlassian.net/wiki/display/MA/Course+Blocks+API
# section 'View @supports(multi_device) decorator'
@XBlock.supports('multi_device')
def student_view(self, context=None): # pylint: disable=unused-argument
"""
Show to students when viewing courses
"""
fragment = self.build_fragment(
path_html='html/invideoquiz.html',
paths_css=[
'css/invideoquiz.css',
],
paths_js=[
'js/src/invideoquiz.js',
],
fragment_js='InVideoQuizXBlock',
context={
'video_id': self.video_id,
'user_mode': self.user_mode,
},
)
config = get_resource_string('js/src/config.js')
config = config.format(
video_id=self.video_id,
timemap=self.timemap,
)
fragment.add_javascript(config)
return fragment

@property
def user_mode(self):
"""
Check user's permission mode for this XBlock.
Returns:
user permission mode
"""
try:
if self.xmodule_runtime.user_is_staff:
return 'staff'
except AttributeError:
pass
return 'student'

@staticmethod
def workbench_scenarios():
"""
A canned scenario for display in the workbench.
"""
return [
("InVideoQuizXBlock",
"""<invideoquiz video_id='###' timemap='{ 10: "###" }' />
"""),
("Multiple InVideoQuizXBlock",
"""<vertical_demo>
<invideoquiz video_id='###' timemap='{ 10: "###" }' />
<invideoquiz video_id='###' timemap='{ 10: "###" }' />
<invideoquiz video_id='###' timemap='{ 10: "###" }' />
</vertical_demo>
"""),
]

def get_resource_url(self, path):
"""
Retrieve a public URL for the file path
"""
path = os.path.join('public', path)
resource_url = self.runtime.local_resource_url(self, path)
return resource_url

def build_fragment(
self,
path_html='',
paths_css=None,
paths_js=None,
urls_css=None,
urls_js=None,
fragment_js=None,
context=None,
): # pylint: disable=too-many-arguments
"""
Assemble the HTML, JS, and CSS for an XBlock fragment
"""
paths_css = paths_css or []
paths_js = paths_js or []
urls_css = urls_css or []
urls_js = urls_js or []
# If no context is provided, convert self.fields into a dict
context = context or {
key: getattr(self, key)
for key in self.editable_fields
}
html_source = get_resource_string(path_html)
html_source = html_source.format(
**context
)
fragment = Fragment(html_source)
for path in paths_css:
url = self.get_resource_url(path)
fragment.add_css_url(url)
for path in paths_js:
url = self.get_resource_url(path)
fragment.add_javascript_url(url)
for url in urls_css:
fragment.add_css_url(url)
for url in urls_js:
fragment.add_javascript_url(url)
if fragment_js:
fragment.initialize_js(fragment_js)
return fragment
19 changes: 19 additions & 0 deletions build/lib/invideoquiz/public/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
This static directory is for files that should be included in your kit as plain
static files.

You can ask the runtime for a URL that will retrieve these files with:

url = self.runtime.local_resource_url(self, "static/js/lib.js")

The default implementation is very strict though, and will not serve files from
the static directory. It will serve files from a directory named "public".
Create a directory alongside this one named "public", and put files there.
Then you can get a url with code like this:

url = self.runtime.local_resource_url(self, "public/js/lib.js")

The sample code includes a function you can use to read the content of files
in the static directory, like this:

frag.add_javascript(self.resource_string("static/js/my_block.js"))

59 changes: 59 additions & 0 deletions build/lib/invideoquiz/public/css/invideoquiz.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/* CSS for InVideoQuizXBlock */

#course-content .vert-mod,
#seq_content .vert-mod {
position: relative;
}
#course-content .vert-mod .in-video-alert,
#seq_content .vert-mod .in-video-alert {
padding: 5px 10px;
background: #eee;
border-radius: 5px;
}

#course-content .vert-mod .vert.in-video-problem-wrapper,
#seq_content .vert-mod .vert.in-video-problem-wrapper {
padding-bottom: 0;
margin-bottom: 0;
border-bottom: none;
}
#course-content .vert-mod .vert.in-video-problem-wrapper .in-video-problem,
#seq_content .vert-mod .vert.in-video-problem-wrapper .in-video-problem {
position: absolute;
top: 35px;
padding: 25px 25px 0 25px;
background: white;
box-sizing: border-box;
width: 100%;
height: 467px;
overflow-y: scroll;
z-index: 99;
}
.video-fullscreen #course-content .vert-mod .vert.in-video-problem-wrapper .in-video-problem,
.video-fullscreen #seq_content .vert-mod .vert.in-video-problem-wrapper .in-video-problem {
position: fixed;
height: auto;
top: 0;
left: 0;
right: 0;
bottom: 53px;
z-index: 10000;
}
.video-controls {
z-index: 100;
}
.video-fullscreen #course-content .vert-mod .video-controls .slider,
.video-fullscreen #seq_content .vert-mod .video-controls .slider {
height: 13px;
}
.video-fullscreen #course-content .vert-mod .video-controls .slider .ui-slider-handle,
.video-fullscreen #seq_content .vert-mod .video-controls .slider .ui-slider-handle {
height: 13px;
width: 13px;
}
.in-video-continue {
float: right;
height: 40px;
margin-bottom: 25px;
text-transform: uppercase;
}
1 change: 1 addition & 0 deletions build/lib/invideoquiz/public/html/invideoquiz.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div class="in-video-quiz-block" data-videoid="{video_id}" data-mode='{user_mode}'></div>
18 changes: 18 additions & 0 deletions build/lib/invideoquiz/public/js/src/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Curly braces are all doubled because this file gets called and formatted by python

var InVideoQuizXBlock = InVideoQuizXBlock || {{}};

(function () {{
InVideoQuizXBlock.config = InVideoQuizXBlock.config || {{}};

var videoId = '{video_id}';
// This is (temporary) error handling for previous-submitted invalid timemap.
try {{
if (videoId) {{
InVideoQuizXBlock.config[videoId] = JSON.parse(`{timemap}`);
}}
}}
catch {{
InVideoQuizXBlock.config[videoId] = {{}};
}}
}}());
Loading

0 comments on commit 81a6472

Please sign in to comment.