Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
1d8d507f
Commit
1d8d507f
authored
Jun 20, 2013
by
Peter Fogg
Browse files
Options
Browse Files
Download
Plain Diff
Merge in master (again).
parents
ea56a0cd
1a5b58ac
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
35 changed files
with
491 additions
and
91 deletions
+491
-91
AUTHORS
+1
-0
CHANGELOG.rst
+18
-0
Gemfile
+1
-0
cms/djangoapps/contentstore/features/advanced-settings.py
+3
-4
cms/djangoapps/contentstore/features/grading.feature
+53
-0
cms/djangoapps/contentstore/features/grading.py
+108
-0
cms/djangoapps/contentstore/tests/test_course_settings.py
+62
-3
cms/djangoapps/contentstore/views/course.py
+2
-1
cms/static/sass/elements/_system-help.scss
+38
-0
cms/static/sass/views/_settings.scss
+7
-1
cms/templates/settings.html
+0
-0
common/djangoapps/terrain/ui_helpers.py
+13
-13
common/lib/xmodule/xmodule/static_content.py
+23
-5
common/static/sass/_mixins.scss
+7
-0
doc/development.md
+19
-0
lms/djangoapps/courseware/features/video.feature
+7
-3
lms/djangoapps/courseware/features/video.py
+22
-0
lms/djangoapps/courseware/model_data.py
+1
-1
lms/djangoapps/courseware/tests/factories.py
+1
-1
lms/djangoapps/courseware/tests/test_model_data.py
+6
-1
lms/templates/video.html
+30
-0
lms/templates/videoalpha.html
+32
-28
rakefile
+8
-6
rakelib/assets.rake
+17
-22
rakelib/deploy.rake
+0
-0
rakelib/deprecated.rake
+0
-0
rakelib/django.rake
+0
-0
rakelib/docs.rake
+0
-0
rakelib/helpers.rb
+12
-0
rakelib/i18n.rake
+0
-0
rakelib/jasmine.rake
+0
-0
rakelib/prereqs.rake
+0
-2
rakelib/quality.rake
+0
-0
rakelib/tests.rake
+0
-0
rakelib/workspace.rake
+0
-0
No files found.
AUTHORS
View file @
1d8d507f
...
@@ -77,3 +77,4 @@ Slater Victoroff <slater.r.victoroff@gmail.com>
...
@@ -77,3 +77,4 @@ Slater Victoroff <slater.r.victoroff@gmail.com>
Peter Fogg <peter.p.fogg@gmail.com>
Peter Fogg <peter.p.fogg@gmail.com>
Bethany LaPenta <lapentab@mit.edu>
Bethany LaPenta <lapentab@mit.edu>
Renzo Lucioni <renzolucioni@gmail.com>
Renzo Lucioni <renzolucioni@gmail.com>
Felix Sun <felixsun@mit.edu>
CHANGELOG.rst
View file @
1d8d507f
...
@@ -8,6 +8,22 @@ the top. Include a label indicating the component affected.
...
@@ -8,6 +8,22 @@ the top. Include a label indicating the component affected.
Studio: Remove XML from the video component editor. All settings are
Studio: Remove XML from the video component editor. All settings are
moved to be edited as metadata.
moved to be edited as metadata.
XModule: Only write out assets files if the contents have changed.
XModule: Don't delete generated xmodule asset files when compiling (for
instance, when XModule provides a coffeescript file, don't delete
the associated javascript)
Studio: For courses running on edx.org (marketing site), disable fields in
Course Settings that do not apply.
Common: Make asset watchers run as singletons (so they won't start if the
watcher is already running in another shell).
Common: Use coffee directly when watching for coffeescript file changes.
Common: Make rake provide better error messages if packages are missing.
Common: Repairs development documentation generation by sphinx.
Common: Repairs development documentation generation by sphinx.
LMS: Problem rescoring. Added options on the Grades tab of the
LMS: Problem rescoring. Added options on the Grades tab of the
...
@@ -17,6 +33,8 @@ students' number of attempts to zero. Provides a list of background
...
@@ -17,6 +33,8 @@ students' number of attempts to zero. Provides a list of background
tasks that are currently running for the course, and an option to
tasks that are currently running for the course, and an option to
see a history of background tasks for a given problem.
see a history of background tasks for a given problem.
LMS: Fixed the preferences scope for storing data in xmodules.
LMS: Forums. Added handling for case where discussion module can get `None` as
LMS: Forums. Added handling for case where discussion module can get `None` as
value of lms.start in `lms/djangoapps/django_comment_client/utils.py`
value of lms.start in `lms/djangoapps/django_comment_client/utils.py`
...
...
Gemfile
View file @
1d8d507f
...
@@ -4,3 +4,4 @@ gem 'sass', '3.1.15'
...
@@ -4,3 +4,4 @@ gem 'sass', '3.1.15'
gem
'
bourbon
'
,
'~> 1.3.6'
gem
'
bourbon
'
,
'~> 1.3.6'
gem
'
colorize
'
,
'~> 0.5.8'
gem
'
colorize
'
,
'~> 0.5.8'
gem
'
launchy
'
,
'~> 2.1.2'
gem
'
launchy
'
,
'~> 2.1.2'
gem
'
sys-proctable
'
,
'~> 0.9.3'
cms/djangoapps/contentstore/features/advanced-settings.py
View file @
1d8d507f
...
@@ -31,11 +31,10 @@ def press_the_notification_button(step, name):
...
@@ -31,11 +31,10 @@ def press_the_notification_button(step, name):
# Save was clicked if either the save notification bar is gone, or we have a error notification
# Save was clicked if either the save notification bar is gone, or we have a error notification
# overlaying it (expected in the case of typing Object into display_name).
# overlaying it (expected in the case of typing Object into display_name).
save_clicked
=
lambda
:
world
.
is_css_not_present
(
'.is-shown.wrapper-notification-warning'
)
or
\
save_clicked
=
lambda
:
world
.
is_css_not_present
(
'.is-shown.wrapper-notification-warning'
)
or
\
world
.
is_css_present
(
'.is-shown.wrapper-notification-error'
)
world
.
is_css_present
(
'.is-shown.wrapper-notification-error'
)
assert_true
(
world
.
css_click
(
css
,
success_condition
=
save_clicked
),
assert_true
(
world
.
css_click
(
css
,
success_condition
=
save_clicked
),
'Save button not clicked after 5 attempts.'
)
'The save button was not clicked after 5 attempts.'
)
@step
(
u'I edit the value of a policy key$'
)
@step
(
u'I edit the value of a policy key$'
)
...
...
cms/djangoapps/contentstore/features/grading.feature
0 → 100644
View file @
1d8d507f
Feature
:
Course Grading
As a course author, I want to be able to configure how my course is graded
Scenario
:
Users can add grading ranges
Given
I have opened a new course in Studio
And
I am viewing the grading settings
When
I add
"1"
new grade
Then
I see I now have
"3"
grades
Scenario
:
Users can only have up to 5 grading ranges
Given
I have opened a new course in Studio
And
I am viewing the grading settings
When
I add
"6"
new grades
Then
I see I now have
"5"
grades
#Cannot reliably make the delete button appear so using javascript instead
Scenario
:
Users can delete grading ranges
Given
I have opened a new course in Studio
And
I am viewing the grading settings
When
I add
"1"
new grade
And
I delete a grade
Then
I see I now have
"2"
grades
Scenario
:
Users can move grading ranges
Given
I have opened a new course in Studio
And
I am viewing the grading settings
When
I move a grading section
Then
I see that the grade range has changed
Scenario
:
Users can modify Assignment types
Given
I have opened a new course in Studio
And
I have populated the course
And
I am viewing the grading settings
When
I change assignment type
"Homework"
to
"New Type"
And
I go back to the main course page
Then
I do see the assignment name
"New Type"
And
I do not see the assignment name
"Homework"
Scenario
:
Users can delete Assignment types
Given
I have opened a new course in Studio
And
I have populated the course
And
I am viewing the grading settings
When
I delete the assignment type
"Homework"
And
I go back to the main course page
Then
I do not see the assignment name
"Homework"
Scenario
:
Users can add Assignment types
Given
I have opened a new course in Studio
And
I have populated the course
And
I am viewing the grading settings
When
I add a new assignment type
"New Type"
And
I go back to the main course page
Then
I do see the assignment name
"New Type"
cms/djangoapps/contentstore/features/grading.py
0 → 100644
View file @
1d8d507f
#pylint: disable=C0111
#pylint: disable=W0621
from
lettuce
import
world
,
step
from
common
import
*
@step
(
u'I am viewing the grading settings'
)
def
view_grading_settings
(
step
):
world
.
click_course_settings
()
link_css
=
'li.nav-course-settings-grading a'
world
.
css_click
(
link_css
)
@step
(
u'I add "([^"]*)" new grade'
)
def
add_grade
(
step
,
many
):
grade_css
=
'.new-grade-button'
for
i
in
range
(
int
(
many
)):
world
.
css_click
(
grade_css
)
@step
(
u'I delete a grade'
)
def
delete_grade
(
step
):
#grade_css = 'li.grade-specific-bar > a.remove-button'
#range_css = '.grade-specific-bar'
#world.css_find(range_css)[1].mouseover()
#world.css_click(grade_css)
world
.
browser
.
execute_script
(
'document.getElementsByClassName("remove-button")[0].click()'
)
@step
(
u'I see I now have "([^"]*)" grades$'
)
def
view_grade_slider
(
step
,
how_many
):
grade_slider_css
=
'.grade-specific-bar'
all_grades
=
world
.
css_find
(
grade_slider_css
)
assert
len
(
all_grades
)
==
int
(
how_many
)
@step
(
u'I move a grading section'
)
def
move_grade_slider
(
step
):
moveable_css
=
'.ui-resizable-e'
f
=
world
.
css_find
(
moveable_css
)
.
first
f
.
action_chains
.
drag_and_drop_by_offset
(
f
.
_element
,
100
,
0
)
.
perform
()
@step
(
u'I see that the grade range has changed'
)
def
confirm_change
(
step
):
range_css
=
'.range'
all_ranges
=
world
.
css_find
(
range_css
)
for
i
in
range
(
len
(
all_ranges
)):
assert
all_ranges
[
i
]
.
html
!=
'0-50'
@step
(
u'I change assignment type "([^"]*)" to "([^"]*)"$'
)
def
change_assignment_name
(
step
,
old_name
,
new_name
):
name_id
=
'#course-grading-assignment-name'
index
=
get_type_index
(
old_name
)
f
=
world
.
css_find
(
name_id
)[
index
]
assert
index
!=
-
1
for
count
in
range
(
len
(
old_name
)):
f
.
_element
.
send_keys
(
Keys
.
END
,
Keys
.
BACK_SPACE
)
f
.
_element
.
send_keys
(
new_name
)
@step
(
u'I go back to the main course page'
)
def
main_course_page
(
step
):
main_page_link_css
=
'a[href="/MITx/999/course/Robot_Super_Course"]'
world
.
css_click
(
main_page_link_css
)
@step
(
u'I do( not)? see the assignment name "([^"]*)"$'
)
def
see_assignment_name
(
step
,
do_not
,
name
):
assignment_menu_css
=
'ul.menu > li > a'
assignment_menu
=
world
.
css_find
(
assignment_menu_css
)
allnames
=
[
item
.
html
for
item
in
assignment_menu
]
if
do_not
:
assert
not
name
in
allnames
else
:
assert
name
in
allnames
@step
(
u'I delete the assignment type "([^"]*)"$'
)
def
delete_assignment_type
(
step
,
to_delete
):
delete_css
=
'.remove-grading-data'
world
.
css_click
(
delete_css
,
index
=
get_type_index
(
to_delete
))
@step
(
u'I add a new assignment type "([^"]*)"$'
)
def
add_assignment_type
(
step
,
new_name
):
add_button_css
=
'.add-grading-data'
world
.
css_click
(
add_button_css
)
name_id
=
'#course-grading-assignment-name'
f
=
world
.
css_find
(
name_id
)[
4
]
f
.
_element
.
send_keys
(
new_name
)
@step
(
u'I have populated the course'
)
def
populate_course
(
step
):
step
.
given
(
'I have added a new section'
)
step
.
given
(
'I have added a new subsection'
)
def
get_type_index
(
name
):
name_id
=
'#course-grading-assignment-name'
f
=
world
.
css_find
(
name_id
)
for
i
in
range
(
len
(
f
)):
if
f
[
i
]
.
value
==
name
:
return
i
return
-
1
cms/djangoapps/contentstore/tests/test_course_settings.py
View file @
1d8d507f
"""
Tests for Studio Course Settings.
"""
import
datetime
import
datetime
import
json
import
json
import
copy
import
copy
import
mock
from
django.contrib.auth.models
import
User
from
django.contrib.auth.models
import
User
from
django.test.client
import
Client
from
django.test.client
import
Client
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
django.utils.timezone
import
UTC
from
django.utils.timezone
import
UTC
from
django.test.utils
import
override_settings
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
models.settings.course_details
import
(
CourseDetails
,
CourseSettingsEncoder
)
from
models.settings.course_details
import
(
CourseDetails
,
CourseSettingsEncoder
)
...
@@ -21,6 +26,9 @@ from xmodule.fields import Date
...
@@ -21,6 +26,9 @@ from xmodule.fields import Date
class
CourseTestCase
(
ModuleStoreTestCase
):
class
CourseTestCase
(
ModuleStoreTestCase
):
"""
Base class for test classes below.
"""
def
setUp
(
self
):
def
setUp
(
self
):
"""
"""
These tests need a user in the DB so that the django Test Client
These tests need a user in the DB so that the django Test Client
...
@@ -51,6 +59,9 @@ class CourseTestCase(ModuleStoreTestCase):
...
@@ -51,6 +59,9 @@ class CourseTestCase(ModuleStoreTestCase):
class
CourseDetailsTestCase
(
CourseTestCase
):
class
CourseDetailsTestCase
(
CourseTestCase
):
"""
Tests the first course settings page (course dates, overview, etc.).
"""
def
test_virgin_fetch
(
self
):
def
test_virgin_fetch
(
self
):
details
=
CourseDetails
.
fetch
(
self
.
course_location
)
details
=
CourseDetails
.
fetch
(
self
.
course_location
)
self
.
assertEqual
(
details
.
course_location
,
self
.
course_location
,
"Location not copied into"
)
self
.
assertEqual
(
details
.
course_location
,
self
.
course_location
,
"Location not copied into"
)
...
@@ -81,9 +92,9 @@ class CourseDetailsTestCase(CourseTestCase):
...
@@ -81,9 +92,9 @@ class CourseDetailsTestCase(CourseTestCase):
Test the encoder out of its original constrained purpose to see if it functions for general use
Test the encoder out of its original constrained purpose to see if it functions for general use
"""
"""
details
=
{
'location'
:
Location
([
'tag'
,
'org'
,
'course'
,
'category'
,
'name'
]),
details
=
{
'location'
:
Location
([
'tag'
,
'org'
,
'course'
,
'category'
,
'name'
]),
'number'
:
1
,
'number'
:
1
,
'string'
:
'string'
,
'string'
:
'string'
,
'datetime'
:
datetime
.
datetime
.
now
(
UTC
())}
'datetime'
:
datetime
.
datetime
.
now
(
UTC
())}
jsondetails
=
json
.
dumps
(
details
,
cls
=
CourseSettingsEncoder
)
jsondetails
=
json
.
dumps
(
details
,
cls
=
CourseSettingsEncoder
)
jsondetails
=
json
.
loads
(
jsondetails
)
jsondetails
=
json
.
loads
(
jsondetails
)
...
@@ -118,8 +129,50 @@ class CourseDetailsTestCase(CourseTestCase):
...
@@ -118,8 +129,50 @@ class CourseDetailsTestCase(CourseTestCase):
jsondetails
.
effort
,
"After set effort"
jsondetails
.
effort
,
"After set effort"
)
)
@override_settings
(
MKTG_URLS
=
{
'ROOT'
:
'dummy-root'
})
def
test_marketing_site_fetch
(
self
):
settings_details_url
=
reverse
(
'settings_details'
,
kwargs
=
{
'org'
:
self
.
course_location
.
org
,
'name'
:
self
.
course_location
.
name
,
'course'
:
self
.
course_location
.
course
})
with
mock
.
patch
.
dict
(
'django.conf.settings.MITX_FEATURES'
,
{
'ENABLE_MKTG_SITE'
:
True
}):
response
=
self
.
client
.
get
(
settings_details_url
)
self
.
assertContains
(
response
,
"Course Summary Page"
)
self
.
assertContains
(
response
,
"course summary page will not be viewable"
)
self
.
assertContains
(
response
,
"Course Start Date"
)
self
.
assertContains
(
response
,
"Course End Date"
)
self
.
assertNotContains
(
response
,
"Enrollment Start Date"
)
self
.
assertNotContains
(
response
,
"Enrollment End Date"
)
self
.
assertContains
(
response
,
"not the dates shown on your course summary page"
)
self
.
assertNotContains
(
response
,
"Introducing Your Course"
)
self
.
assertNotContains
(
response
,
"Requirements"
)
def
test_regular_site_fetch
(
self
):
settings_details_url
=
reverse
(
'settings_details'
,
kwargs
=
{
'org'
:
self
.
course_location
.
org
,
'name'
:
self
.
course_location
.
name
,
'course'
:
self
.
course_location
.
course
})
with
mock
.
patch
.
dict
(
'django.conf.settings.MITX_FEATURES'
,
{
'ENABLE_MKTG_SITE'
:
False
}):
response
=
self
.
client
.
get
(
settings_details_url
)
self
.
assertContains
(
response
,
"Course Summary Page"
)
self
.
assertNotContains
(
response
,
"course summary page will not be viewable"
)
self
.
assertContains
(
response
,
"Course Start Date"
)
self
.
assertContains
(
response
,
"Course End Date"
)
self
.
assertContains
(
response
,
"Enrollment Start Date"
)
self
.
assertContains
(
response
,
"Enrollment End Date"
)
self
.
assertNotContains
(
response
,
"not the dates shown on your course summary page"
)
self
.
assertContains
(
response
,
"Introducing Your Course"
)
self
.
assertContains
(
response
,
"Requirements"
)
class
CourseDetailsViewTest
(
CourseTestCase
):
class
CourseDetailsViewTest
(
CourseTestCase
):
"""
Tests for modifying content on the first course settings page (course dates, overview, etc.).
"""
def
alter_field
(
self
,
url
,
details
,
field
,
val
):
def
alter_field
(
self
,
url
,
details
,
field
,
val
):
setattr
(
details
,
field
,
val
)
setattr
(
details
,
field
,
val
)
# Need to partially serialize payload b/c the mock doesn't handle it correctly
# Need to partially serialize payload b/c the mock doesn't handle it correctly
...
@@ -181,6 +234,9 @@ class CourseDetailsViewTest(CourseTestCase):
...
@@ -181,6 +234,9 @@ class CourseDetailsViewTest(CourseTestCase):
class
CourseGradingTest
(
CourseTestCase
):
class
CourseGradingTest
(
CourseTestCase
):
"""
Tests for the course settings grading page.
"""
def
test_initial_grader
(
self
):
def
test_initial_grader
(
self
):
descriptor
=
get_modulestore
(
self
.
course_location
)
.
get_item
(
self
.
course_location
)
descriptor
=
get_modulestore
(
self
.
course_location
)
.
get_item
(
self
.
course_location
)
test_grader
=
CourseGradingModel
(
descriptor
)
test_grader
=
CourseGradingModel
(
descriptor
)
...
@@ -256,6 +312,9 @@ class CourseGradingTest(CourseTestCase):
...
@@ -256,6 +312,9 @@ class CourseGradingTest(CourseTestCase):
class
CourseMetadataEditingTest
(
CourseTestCase
):
class
CourseMetadataEditingTest
(
CourseTestCase
):
"""
Tests for CourseMetadata.
"""
def
setUp
(
self
):
def
setUp
(
self
):
CourseTestCase
.
setUp
(
self
)
CourseTestCase
.
setUp
(
self
)
# add in the full class too
# add in the full class too
...
...
cms/djangoapps/contentstore/views/course.py
View file @
1d8d507f
...
@@ -227,7 +227,8 @@ def get_course_settings(request, org, course, name):
...
@@ -227,7 +227,8 @@ def get_course_settings(request, org, course, name):
kwargs
=
{
"org"
:
org
,
kwargs
=
{
"org"
:
org
,
"course"
:
course
,
"course"
:
course
,
"name"
:
name
,
"name"
:
name
,
"section"
:
"details"
})
"section"
:
"details"
}),
'about_page_editable'
:
not
settings
.
MITX_FEATURES
.
get
(
'ENABLE_MKTG_SITE'
,
False
)
})
})
...
...
cms/static/sass/elements/_system-help.scss
View file @
1d8d507f
// studio - elements - system help
// studio - elements - system help
// ====================
// ====================
// notices - in-context: to be used as notices to users within the context of a form/action
.notice-incontext
{
@extend
.ui-well
;
@include
border-radius
((
$baseline
/
10
));
.title
{
@extend
.t-title7
;
margin-bottom
:
(
$baseline
/
4
);
font-weight
:
600
;
}
.copy
{
@extend
.t-copy-sub1
;
@include
transition
(
opacity
0
.25s
ease-in-out
0
);
opacity
:
0
.75
;
}
strong
{
font-weight
:
600
;
}
&
:hover
{
.copy
{
opacity
:
1
.0
;
}
}
}
// particular warnings around a workflow for something
.notice-workflow
{
background
:
$yellow-l5
;
.copy
{
color
:
$gray-d1
;
}
}
cms/static/sass/views/_settings.scss
View file @
1d8d507f
...
@@ -21,7 +21,7 @@ body.course.settings {
...
@@ -21,7 +21,7 @@ body.course.settings {
font-size
:
14px
;
font-size
:
14px
;
}
}
.message-status
{
.message-status
{
display
:
none
;
display
:
none
;
@include
border-top-radius
(
2px
);
@include
border-top-radius
(
2px
);
@include
box-sizing
(
border-box
);
@include
box-sizing
(
border-box
);
...
@@ -52,6 +52,12 @@ body.course.settings {
...
@@ -52,6 +52,12 @@ body.course.settings {
}
}
}
}
// notices - used currently for edx mktg
.notice-workflow
{
margin-top
:
(
$baseline
);
}
// in form - elements
// in form - elements
.group-settings
{
.group-settings
{
margin
:
0
0
(
$baseline
*
2
)
0
;
margin
:
0
0
(
$baseline
*
2
)
0
;
...
...
cms/templates/settings.html
View file @
1d8d507f
This diff is collapsed.
Click to expand it.
common/djangoapps/terrain/ui_helpers.py
View file @
1d8d507f
...
@@ -49,7 +49,7 @@ def css_has_text(css_selector, text):
...
@@ -49,7 +49,7 @@ def css_has_text(css_selector, text):
@world.absorb
@world.absorb
def
css_find
(
css
,
wait_time
=
5
):
def
css_find
(
css
,
wait_time
=
5
):
def
is_visible
(
driver
):
def
is_visible
(
_
driver
):
return
EC
.
visibility_of_element_located
((
By
.
CSS_SELECTOR
,
css
,))
return
EC
.
visibility_of_element_located
((
By
.
CSS_SELECTOR
,
css
,))
world
.
browser
.
is_element_present_by_css
(
css
,
wait_time
=
wait_time
)
world
.
browser
.
is_element_present_by_css
(
css
,
wait_time
=
wait_time
)
...
@@ -58,7 +58,7 @@ def css_find(css, wait_time=5):
...
@@ -58,7 +58,7 @@ def css_find(css, wait_time=5):
@world.absorb
@world.absorb
def
css_click
(
css_selector
,
index
=
0
,
attempts
=
5
,
success_condition
=
lambda
:
True
):
def
css_click
(
css_selector
,
index
=
0
,
attempts
=
5
,
success_condition
=
lambda
:
True
):
"""
"""
Perform a click on a CSS selector, retrying if it initially fails.
Perform a click on a CSS selector, retrying if it initially fails.
...
@@ -90,15 +90,15 @@ def css_click(css_selector, index=0, attempts=5, success_condition=lambda:True):
...
@@ -90,15 +90,15 @@ def css_click(css_selector, index=0, attempts=5, success_condition=lambda:True):
@world.absorb
@world.absorb
def
css_click_at
(
css
,
x
=
10
,
y
=
10
):
def
css_click_at
(
css
,
x
_cord
=
10
,
y_cord
=
10
):
'''
'''
A method to click at x,y coordinates of the element
A method to click at x,y coordinates of the element
rather than in the center of the element
rather than in the center of the element
'''
'''
e
=
css_find
(
css
)
.
first
e
lement
=
css_find
(
css
)
.
first
e
.
action_chains
.
move_to_element_with_offset
(
e
.
_element
,
x
,
y
)
e
lement
.
action_chains
.
move_to_element_with_offset
(
element
.
_element
,
x_cord
,
y_cord
)
e
.
action_chains
.
click
()
e
lement
.
action_chains
.
click
()
e
.
action_chains
.
perform
()
e
lement
.
action_chains
.
perform
()
@world.absorb
@world.absorb
...
@@ -143,7 +143,7 @@ def css_visible(css_selector):
...
@@ -143,7 +143,7 @@ def css_visible(css_selector):
@world.absorb
@world.absorb
def
dialogs_closed
():
def
dialogs_closed
():
def
are_dialogs_closed
(
driver
):
def
are_dialogs_closed
(
_
driver
):
'''
'''
Return True when no modal dialogs are visible
Return True when no modal dialogs are visible
'''
'''
...
@@ -154,12 +154,12 @@ def dialogs_closed():
...
@@ -154,12 +154,12 @@ def dialogs_closed():
@world.absorb
@world.absorb
def
save_the_html
(
path
=
'/tmp'
):
def
save_the_html
(
path
=
'/tmp'
):
u
=
world
.
browser
.
url
u
rl
=
world
.
browser
.
url
html
=
world
.
browser
.
html
.
encode
(
'ascii'
,
'ignore'
)
html
=
world
.
browser
.
html
.
encode
(
'ascii'
,
'ignore'
)
filename
=
'
%
s.html'
%
quote_plus
(
u
)
filename
=
'
%
s.html'
%
quote_plus
(
u
rl
)
f
=
open
(
'
%
s/
%
s'
%
(
path
,
filename
),
'w'
)
f
ile
=
open
(
'
%
s/
%
s'
%
(
path
,
filename
),
'w'
)
f
.
write
(
html
)
f
ile
.
write
(
html
)
f
.
close
()
f
ile
.
close
()
@world.absorb
@world.absorb
...
...
common/lib/xmodule/xmodule/static_content.py
View file @
1d8d507f
...
@@ -4,6 +4,7 @@ This module has utility functions for gathering up the static content
...
@@ -4,6 +4,7 @@ This module has utility functions for gathering up the static content
that is defined by XModules and XModuleDescriptors (javascript and css)
that is defined by XModules and XModuleDescriptors (javascript and css)
"""
"""
import
logging
import
hashlib
import
hashlib
import
os
import
os
import
errno
import
errno
...
@@ -15,6 +16,9 @@ from path import path
...
@@ -15,6 +16,9 @@ from path import path
from
xmodule.x_module
import
XModuleDescriptor
from
xmodule.x_module
import
XModuleDescriptor
LOG
=
logging
.
getLogger
(
__name__
)
def
write_module_styles
(
output_root
):
def
write_module_styles
(
output_root
):
return
_write_styles
(
'.xmodule_display'
,
output_root
,
_list_modules
())
return
_write_styles
(
'.xmodule_display'
,
output_root
,
_list_modules
())
...
@@ -121,18 +125,32 @@ def _write_js(output_root, classes):
...
@@ -121,18 +125,32 @@ def _write_js(output_root, classes):
type
=
filetype
)
type
=
filetype
)
contents
[
filename
]
=
fragment
contents
[
filename
]
=
fragment
_write_files
(
output_root
,
contents
)
_write_files
(
output_root
,
contents
,
{
'.coffee'
:
'.js'
}
)
return
[
output_root
/
filename
for
filename
in
contents
.
keys
()]
return
[
output_root
/
filename
for
filename
in
contents
.
keys
()]
def
_write_files
(
output_root
,
contents
):
def
_write_files
(
output_root
,
contents
,
generated_suffix_map
=
None
):
_ensure_dir
(
output_root
)
_ensure_dir
(
output_root
)
for
extra_file
in
set
(
output_root
.
files
())
-
set
(
contents
.
keys
()):
to_delete
=
set
(
file
.
basename
()
for
file
in
output_root
.
files
())
-
set
(
contents
.
keys
())
extra_file
.
remove_p
()
if
generated_suffix_map
:
for
output_file
in
contents
.
keys
():
for
suffix
,
generated_suffix
in
generated_suffix_map
.
items
():
if
output_file
.
endswith
(
suffix
):
to_delete
.
discard
(
output_file
.
replace
(
suffix
,
generated_suffix
))
for
extra_file
in
to_delete
:
(
output_root
/
extra_file
)
.
remove_p
()
for
filename
,
file_content
in
contents
.
iteritems
():
for
filename
,
file_content
in
contents
.
iteritems
():
(
output_root
/
filename
)
.
write_bytes
(
file_content
)
output_file
=
output_root
/
filename
if
not
output_file
.
isfile
()
or
output_file
.
read_md5
()
!=
hashlib
.
md5
(
file_content
)
.
digest
():
LOG
.
debug
(
"Writing
%
s"
,
output_file
)
output_file
.
write_bytes
(
file_content
)
else
:
LOG
.
debug
(
"
%
s unchanged, skipping"
,
output_file
)
def
main
():
def
main
():
...
...
common/static/sass/_mixins.scss
View file @
1d8d507f
...
@@ -189,3 +189,10 @@
...
@@ -189,3 +189,10 @@
}
}
}
}
// UI archetypes - well
.ui-well
{
@include
box-shadow
(
inset
0
1px
2px
1px
$shadow-l1
);
padding
:
(
$baseline
*
0
.75
);
}
doc/development.md
View file @
1d8d507f
...
@@ -63,6 +63,25 @@ To get a full list of available rake tasks, use:
...
@@ -63,6 +63,25 @@ To get a full list of available rake tasks, use:
rake -T
rake -T
### Troubleshooting
#### Reference Error: XModule is not defined (javascript)
This means that the javascript defining an xmodule hasn't loaded correctly. There are a number
of different things that could be causing this:
1.
See
`Error: watch EMFILE`
#### Error: watch EMFILE (coffee)
When running a development server, we also start a watcher process alongside to recompile coffeescript
and sass as changes are made. On Mac OSX systems, the coffee watcher process takes more file handles
than are allowed by default. This will result in
`EMFILE`
errors when coffeescript is running, and
will prevent javascript from compiling, leading to the error 'XModule is not defined'
To work around this issue, we use
`Process::setrlimit`
to set the number of allowed open files.
Coffee watches both directories and files, so you will need to set this fairly high (anecdotally,
8000 seems to do the trick on OSX 10.7.5, 10.8.3, and 10.8.4)
## Running Tests
## Running Tests
See
`testing.md`
for instructions on running the test suite.
See
`testing.md`
for instructions on running the test suite.
...
...
lms/djangoapps/courseware/features/video.feature
View file @
1d8d507f
Feature
:
Video component
Feature
:
Video component
As a student, I want to view course videos in LMS.
As a student, I want to view course videos in LMS.
Scenario
:
Autoplay is enabled in LMS
Scenario
:
Autoplay is enabled in LMS for a Video component
Given
the course has a Video component
Given
the course has a Video component
Then
when I view the video it has autoplay enabled
Then
when I view the video it has autoplay enabled
Scenario
:
Autoplay is enabled in the LMS for a VideoAlpha component
Given
the course has a VideoAlpha component
Then
when I view the video it has autoplay enabled
lms/djangoapps/courseware/features/video.py
View file @
1d8d507f
...
@@ -27,8 +27,30 @@ def view_video(_step):
...
@@ -27,8 +27,30 @@ def view_video(_step):
world
.
browser
.
visit
(
url
)
world
.
browser
.
visit
(
url
)
@step
(
'the course has a VideoAlpha component'
)
def
view_videoalpha
(
step
):
coursename
=
TEST_COURSE_NAME
.
replace
(
' '
,
'_'
)
i_am_registered_for_the_course
(
step
,
coursename
)
# Make sure we have a videoalpha
add_videoalpha_to_course
(
coursename
)
chapter_name
=
TEST_SECTION_NAME
.
replace
(
" "
,
"_"
)
section_name
=
chapter_name
url
=
django_url
(
'/courses/edx/Test_Course/Test_Course/courseware/
%
s/
%
s'
%
(
chapter_name
,
section_name
))
world
.
browser
.
visit
(
url
)
def
add_video_to_course
(
course
):
def
add_video_to_course
(
course
):
template_name
=
'i4x://edx/templates/video/default'
template_name
=
'i4x://edx/templates/video/default'
world
.
ItemFactory
.
create
(
parent_location
=
section_location
(
course
),
world
.
ItemFactory
.
create
(
parent_location
=
section_location
(
course
),
template
=
template_name
,
template
=
template_name
,
display_name
=
'Video'
)
display_name
=
'Video'
)
def
add_videoalpha_to_course
(
course
):
template_name
=
'i4x://edx/templates/videoalpha/Video_Alpha'
world
.
ItemFactory
.
create
(
parent_location
=
section_location
(
course
),
template
=
template_name
,
display_name
=
'Video Alpha'
)
lms/djangoapps/courseware/model_data.py
View file @
1d8d507f
...
@@ -163,7 +163,7 @@ class ModelDataCache(object):
...
@@ -163,7 +163,7 @@ class ModelDataCache(object):
return
self
.
_chunked_query
(
return
self
.
_chunked_query
(
XModuleStudentPrefsField
,
XModuleStudentPrefsField
,
'module_type__in'
,
'module_type__in'
,
set
(
descriptor
.
location
.
category
for
descriptor
in
self
.
descriptors
),
set
(
descriptor
.
module_class
.
__name__
for
descriptor
in
self
.
descriptors
),
student
=
self
.
user
.
pk
,
student
=
self
.
user
.
pk
,
field_name__in
=
set
(
field
.
name
for
field
in
fields
),
field_name__in
=
set
(
field
.
name
for
field
in
fields
),
)
)
...
...
lms/djangoapps/courseware/tests/factories.py
View file @
1d8d507f
...
@@ -75,7 +75,7 @@ class StudentPrefsFactory(DjangoModelFactory):
...
@@ -75,7 +75,7 @@ class StudentPrefsFactory(DjangoModelFactory):
field_name
=
'existing_field'
field_name
=
'existing_field'
value
=
json
.
dumps
(
'old_value'
)
value
=
json
.
dumps
(
'old_value'
)
student
=
SubFactory
(
UserFactory
)
student
=
SubFactory
(
UserFactory
)
module_type
=
'
problem
'
module_type
=
'
MockProblemModule
'
class
StudentInfoFactory
(
DjangoModelFactory
):
class
StudentInfoFactory
(
DjangoModelFactory
):
...
...
lms/djangoapps/courseware/tests/test_model_data.py
View file @
1d8d507f
...
@@ -29,6 +29,7 @@ def mock_descriptor(fields=[], lms_fields=[]):
...
@@ -29,6 +29,7 @@ def mock_descriptor(fields=[], lms_fields=[]):
descriptor
.
location
=
location
(
'def_id'
)
descriptor
.
location
=
location
(
'def_id'
)
descriptor
.
module_class
.
fields
=
fields
descriptor
.
module_class
.
fields
=
fields
descriptor
.
module_class
.
lms
.
fields
=
lms_fields
descriptor
.
module_class
.
lms
.
fields
=
lms_fields
descriptor
.
module_class
.
__name__
=
'MockProblemModule'
return
descriptor
return
descriptor
location
=
partial
(
Location
,
'i4x'
,
'edX'
,
'test_course'
,
'problem'
)
location
=
partial
(
Location
,
'i4x'
,
'edX'
,
'test_course'
,
'problem'
)
...
@@ -37,7 +38,7 @@ course_id = 'edX/test_course/test'
...
@@ -37,7 +38,7 @@ course_id = 'edX/test_course/test'
content_key
=
partial
(
LmsKeyValueStore
.
Key
,
Scope
.
content
,
None
,
location
(
'def_id'
))
content_key
=
partial
(
LmsKeyValueStore
.
Key
,
Scope
.
content
,
None
,
location
(
'def_id'
))
settings_key
=
partial
(
LmsKeyValueStore
.
Key
,
Scope
.
settings
,
None
,
location
(
'def_id'
))
settings_key
=
partial
(
LmsKeyValueStore
.
Key
,
Scope
.
settings
,
None
,
location
(
'def_id'
))
user_state_key
=
partial
(
LmsKeyValueStore
.
Key
,
Scope
.
user_state
,
'user'
,
location
(
'def_id'
))
user_state_key
=
partial
(
LmsKeyValueStore
.
Key
,
Scope
.
user_state
,
'user'
,
location
(
'def_id'
))
prefs_key
=
partial
(
LmsKeyValueStore
.
Key
,
Scope
.
preferences
,
'user'
,
'
problem
'
)
prefs_key
=
partial
(
LmsKeyValueStore
.
Key
,
Scope
.
preferences
,
'user'
,
'
MockProblemModule
'
)
user_info_key
=
partial
(
LmsKeyValueStore
.
Key
,
Scope
.
user_info
,
'user'
,
None
)
user_info_key
=
partial
(
LmsKeyValueStore
.
Key
,
Scope
.
user_info
,
'user'
,
None
)
...
@@ -190,6 +191,10 @@ class StorageTestBase(object):
...
@@ -190,6 +191,10 @@ class StorageTestBase(object):
self
.
mdc
=
ModelDataCache
([
mock_descriptor
([
mock_field
(
self
.
scope
,
'existing_field'
)])],
course_id
,
self
.
user
)
self
.
mdc
=
ModelDataCache
([
mock_descriptor
([
mock_field
(
self
.
scope
,
'existing_field'
)])],
course_id
,
self
.
user
)
self
.
kvs
=
LmsKeyValueStore
(
self
.
desc_md
,
self
.
mdc
)
self
.
kvs
=
LmsKeyValueStore
(
self
.
desc_md
,
self
.
mdc
)
def
test_set_and_get_existing_field
(
self
):
self
.
kvs
.
set
(
self
.
key_factory
(
'existing_field'
),
'test_value'
)
self
.
assertEquals
(
'test_value'
,
self
.
kvs
.
get
(
self
.
key_factory
(
'existing_field'
)))
def
test_get_existing_field
(
self
):
def
test_get_existing_field
(
self
):
"Test that getting an existing field in an existing Storage Field works"
"Test that getting an existing field in an existing Storage Field works"
self
.
assertEquals
(
'old_value'
,
self
.
kvs
.
get
(
self
.
key_factory
(
'existing_field'
)))
self
.
assertEquals
(
'old_value'
,
self
.
kvs
.
get
(
self
.
key_factory
(
'existing_field'
)))
...
...
lms/templates/video.html
View file @
1d8d507f
...
@@ -2,6 +2,7 @@
...
@@ -2,6 +2,7 @@
<h2>
${display_name}
</h2>
<h2>
${display_name}
</h2>
% endif
% endif
<
<<<<<<
HEAD
%
if
settings
.
MITX_FEATURES
['
STUB_VIDEO_FOR_TESTING
']
:
%
if
settings
.
MITX_FEATURES
['
STUB_VIDEO_FOR_TESTING
']
:
<
div
id=
"stub_out_video_for_testing"
>
<
div
id=
"stub_out_video_for_testing"
>
<div
class=
"video"
data-autoplay=
"${settings.MITX_FEATURES['AUTOPLAY_VIDEOS']}"
>
<div
class=
"video"
data-autoplay=
"${settings.MITX_FEATURES['AUTOPLAY_VIDEOS']}"
>
...
@@ -43,6 +44,35 @@
...
@@ -43,6 +44,35 @@
data-end=
"${end}"
data-end=
"${end}"
data-caption-asset-path=
"${caption_asset_path}"
data-caption-asset-path=
"${caption_asset_path}"
data-autoplay=
"${settings.MITX_FEATURES['AUTOPLAY_VIDEOS']}"
>
data-autoplay=
"${settings.MITX_FEATURES['AUTOPLAY_VIDEOS']}"
>
=======
%if settings.MITX_FEATURES.get('USE_YOUTUBE_OBJECT_API') and normal_speed_video_id:
<object
width=
"640"
height=
"390"
>
<param
name=
"movie"
%
if
not
settings
.
MITX_FEATURES
['
STUB_VIDEO_FOR_TESTING
']
:
value=
"https://www.youtube.com/v/${normal_speed_video_id}?version=3&autoplay=1&rel=0"
>
% endif
</param>
<param
name=
"allowScriptAccess"
value=
"always"
></param>
<embed
%
if
not
settings
.
MITX_FEATURES
['
STUB_VIDEO_FOR_TESTING
']
:
src=
"https://www.youtube.com/v/${normal_speed_video_id}?version=3&autoplay=1&rel=0"
%
endif
type=
"application/x-shockwave-flash"
allowscriptaccess=
"always"
width=
"640"
height=
"390"
></embed>
</object>
%else:
<div
id=
"video_${id}"
class=
"video"
%
if
not
settings
.
MITX_FEATURES
['
STUB_VIDEO_FOR_TESTING
']
:
data-streams=
"${streams}"
%
endif
data-show-captions=
"${show_captions}"
data-start=
"${start}"
data-end=
"${end}"
data-caption-asset-path=
"${caption_asset_path}"
data-autoplay=
"${settings.MITX_FEATURES['AUTOPLAY_VIDEOS']}"
>
>>>>>>> master
<div
class=
"tc-wrapper"
>
<div
class=
"tc-wrapper"
>
<article
class=
"video-wrapper"
>
<article
class=
"video-wrapper"
>
<section
class=
"video-player"
>
<section
class=
"video-player"
>
...
...
lms/templates/videoalpha.html
View file @
1d8d507f
...
@@ -2,34 +2,38 @@
...
@@ -2,34 +2,38 @@
<h2>
${display_name}
</h2>
<h2>
${display_name}
</h2>
% endif
% endif
%if settings.MITX_FEATURES['STUB_VIDEO_FOR_TESTING']:
<div
<div
id=
"stub_out_video_for_testing"
></div>
id=
"video_${id}"
%else:
class=
"video"
<div
id=
"video_${id}"
%
if
not
settings
.
MITX_FEATURES
['
STUB_VIDEO_FOR_TESTING
']
:
class=
"video"
data-streams=
"${youtube_streams}"
data-streams=
"${youtube_streams}"
%
endif
${'
data-sub=
"{}"
'.
format
(
sub
)
if
sub
else
''}
${'
data-mp4-source=
"{}"
'.
format
(
sources
.
get
('
mp4
'))
if
sources
.
get
('
mp4
')
else
''}
${'
data-sub=
"{}"
'.
format
(
sub
)
if
sub
else
''}
${'
data-webm-source=
"{}"
'.
format
(
sources
.
get
('
webm
'))
if
sources
.
get
('
webm
')
else
''}
${'
data-ogg-source=
"{}"
'.
format
(
sources
.
get
('
ogv
'))
if
sources
.
get
('
ogv
')
else
''}
%
if
not
settings
.
MITX_FEATURES
['
STUB_VIDEO_FOR_TESTING
']
:
data-caption-data-dir=
"${data_dir}"
${'
data-mp4-source=
"{}"
'.
format
(
sources
.
get
('
mp4
'))
if
sources
.
get
('
mp4
')
else
''}
data-show-captions=
"${show_captions}"
${'
data-webm-source=
"{}"
'.
format
(
sources
.
get
('
webm
'))
if
sources
.
get
('
webm
')
else
''}
data-start=
"${start}"
${'
data-ogg-source=
"{}"
'.
format
(
sources
.
get
('
ogv
'))
if
sources
.
get
('
ogv
')
else
''}
data-end=
"${end}"
%
endif
data-caption-asset-path=
"${caption_asset_path}"
data-autoplay=
"${autoplay}"
data-caption-data-dir=
"${data_dir}"
>
data-show-captions=
"${show_captions}"
<div
class=
"tc-wrapper"
>
data-start=
"${start}"
<article
class=
"video-wrapper"
>
data-end=
"${end}"
<section
class=
"video-player"
>
data-caption-asset-path=
"${caption_asset_path}"
<div
id=
"${id}"
></div>
data-autoplay=
"${autoplay}"
</section>
>
<section
class=
"video-controls"
></section>
<div
class=
"tc-wrapper"
>
</article>
<article
class=
"video-wrapper"
>
</div>
<section
class=
"video-player"
>
</div>
<div
id=
"${id}"
></div>
%endif
</section>
<section
class=
"video-controls"
></section>
</article>
</div>
</div>
% if sources.get('main'):
% if sources.get('main'):
<div
class=
"video-sources"
>
<div
class=
"video-sources"
>
...
...
rakefile
View file @
1d8d507f
require
'json'
begin
require
'rake/clean'
require
'json'
require
'./rakefiles/helpers.rb'
require
'rake/clean'
require
'./rakelib/helpers.rb'
Dir
[
'rakefiles/*.rake'
].
each
do
|
rakefile
|
rescue
LoadError
=>
error
import
rakefile
puts
"Import faild (
#{
error
}
)"
puts
"Please run `bundle install` to bootstrap ruby dependencies"
exit
1
end
end
# Build Constants
# Build Constants
...
...
rake
files
/assets.rake
→
rake
lib
/assets.rake
View file @
1d8d507f
...
@@ -6,6 +6,8 @@ if USE_CUSTOM_THEME
...
@@ -6,6 +6,8 @@ if USE_CUSTOM_THEME
THEME_SASS
=
File
.
join
(
THEME_ROOT
,
"static"
,
"sass"
)
THEME_SASS
=
File
.
join
(
THEME_ROOT
,
"static"
,
"sass"
)
end
end
MINIMAL_DARWIN_NOFILE_LIMIT
=
8000
def
xmodule_cmd
(
watch
=
false
,
debug
=
false
)
def
xmodule_cmd
(
watch
=
false
,
debug
=
false
)
xmodule_cmd
=
'xmodule_assets common/static/xmodule'
xmodule_cmd
=
'xmodule_assets common/static/xmodule'
if
watch
if
watch
...
@@ -21,24 +23,14 @@ def xmodule_cmd(watch=false, debug=false)
...
@@ -21,24 +23,14 @@ def xmodule_cmd(watch=false, debug=false)
end
end
def
coffee_cmd
(
watch
=
false
,
debug
=
false
)
def
coffee_cmd
(
watch
=
false
,
debug
=
false
)
if
watch
if
watch
&&
Launchy
::
Application
.
new
.
host_os_family
.
darwin?
# On OSx, coffee fails with EMFILE when
available_files
=
Process
::
getrlimit
(
:NOFILE
)[
0
]
# trying to watch all of our coffee files at the same
if
available_files
<
MINIMAL_DARWIN_NOFILE_LIMIT
# time.
Process
.
setrlimit
(
:NOFILE
,
MINIMAL_DARWIN_NOFILE_LIMIT
)
#
# Ref: https://github.com/joyent/node/issues/2479
end
#
# So, instead, we use watchmedo, which works around the problem
"watchmedo shell-command "
+
"--command 'node_modules/.bin/coffee -c ${watch_src_path}' "
+
"--recursive "
+
"--patterns '*.coffee' "
+
"--ignore-directories "
+
"--wait "
+
"."
else
'node_modules/.bin/coffee --compile .'
end
end
"node_modules/.bin/coffee --compile
#{
watch
?
'--watch'
:
''
}
."
end
end
def
sass_cmd
(
watch
=
false
,
debug
=
false
)
def
sass_cmd
(
watch
=
false
,
debug
=
false
)
...
@@ -55,8 +47,9 @@ def sass_cmd(watch=false, debug=false)
...
@@ -55,8 +47,9 @@ def sass_cmd(watch=false, debug=false)
"
#{
watch
?
'--watch'
:
'--update'
}
-E utf-8
#{
sass_watch_paths
.
join
(
' '
)
}
"
"
#{
watch
?
'--watch'
:
'--update'
}
-E utf-8
#{
sass_watch_paths
.
join
(
' '
)
}
"
end
end
# This task takes arguments purely to pass them via dependencies to the preprocess task
desc
"Compile all assets"
desc
"Compile all assets"
multitask
:assets
=>
'assets:all'
task
:assets
,
[
:system
,
:env
]
=>
'assets:all'
namespace
:assets
do
namespace
:assets
do
...
@@ -80,8 +73,9 @@ namespace :assets do
...
@@ -80,8 +73,9 @@ namespace :assets do
{
:xmodule
=>
[
:install_python_prereqs
],
{
:xmodule
=>
[
:install_python_prereqs
],
:coffee
=>
[
:install_node_prereqs
,
:'assets:coffee:clobber'
],
:coffee
=>
[
:install_node_prereqs
,
:'assets:coffee:clobber'
],
:sass
=>
[
:install_ruby_prereqs
,
:preprocess
]}.
each_pair
do
|
asset_type
,
prereq_tasks
|
:sass
=>
[
:install_ruby_prereqs
,
:preprocess
]}.
each_pair
do
|
asset_type
,
prereq_tasks
|
# This task takes arguments purely to pass them via dependencies to the preprocess task
desc
"Compile all
#{
asset_type
}
assets"
desc
"Compile all
#{
asset_type
}
assets"
task
asset_type
=>
prereq_tasks
do
task
asset_type
,
[
:system
,
:env
]
=>
prereq_tasks
do
|
t
,
args
|
cmd
=
send
(
asset_type
.
to_s
+
"_cmd"
,
watch
=
false
,
debug
=
false
)
cmd
=
send
(
asset_type
.
to_s
+
"_cmd"
,
watch
=
false
,
debug
=
false
)
if
cmd
.
kind_of?
(
Array
)
if
cmd
.
kind_of?
(
Array
)
cmd
.
each
{
|
c
|
sh
(
c
)}
cmd
.
each
{
|
c
|
sh
(
c
)}
...
@@ -90,7 +84,8 @@ namespace :assets do
...
@@ -90,7 +84,8 @@ namespace :assets do
end
end
end
end
multitask
:all
=>
asset_type
# This task takes arguments purely to pass them via dependencies to the preprocess task
multitask
:all
,
[
:system
,
:env
]
=>
asset_type
multitask
:debug
=>
"assets:
#{
asset_type
}
:debug"
multitask
:debug
=>
"assets:
#{
asset_type
}
:debug"
multitask
:_watch
=>
"assets:
#{
asset_type
}
:_watch"
multitask
:_watch
=>
"assets:
#{
asset_type
}
:_watch"
...
@@ -111,9 +106,9 @@ namespace :assets do
...
@@ -111,9 +106,9 @@ namespace :assets do
task
:_watch
=>
(
prereq_tasks
+
[
"assets:
#{
asset_type
}
:debug"
])
do
task
:_watch
=>
(
prereq_tasks
+
[
"assets:
#{
asset_type
}
:debug"
])
do
cmd
=
send
(
asset_type
.
to_s
+
"_cmd"
,
watch
=
true
,
debug
=
true
)
cmd
=
send
(
asset_type
.
to_s
+
"_cmd"
,
watch
=
true
,
debug
=
true
)
if
cmd
.
kind_of?
(
Array
)
if
cmd
.
kind_of?
(
Array
)
cmd
.
each
{
|
c
|
background
_process
(
c
)}
cmd
.
each
{
|
c
|
singleton
_process
(
c
)}
else
else
background
_process
(
cmd
)
singleton
_process
(
cmd
)
end
end
end
end
end
end
...
...
rake
files
/deploy.rake
→
rake
lib
/deploy.rake
View file @
1d8d507f
File moved
rake
files
/deprecated.rake
→
rake
lib
/deprecated.rake
View file @
1d8d507f
File moved
rake
files
/django.rake
→
rake
lib
/django.rake
View file @
1d8d507f
File moved
rake
files
/docs.rake
→
rake
lib
/docs.rake
View file @
1d8d507f
File moved
rake
files
/helpers.rb
→
rake
lib
/helpers.rb
View file @
1d8d507f
require
'digest/md5'
require
'digest/md5'
require
'sys/proctable'
require
'colorize'
def
find_executable
(
exec
)
def
find_executable
(
exec
)
path
=
%x(which
#{
exec
}
)
.
strip
path
=
%x(which
#{
exec
}
)
.
strip
...
@@ -84,6 +86,16 @@ def background_process(*command)
...
@@ -84,6 +86,16 @@ def background_process(*command)
end
end
end
end
# Runs a command as a background process, as long as no other processes
# tagged with the same tag are running
def
singleton_process
(
*
command
)
if
Sys
::
ProcTable
.
ps
.
select
{
|
proc
|
proc
.
cmdline
.
include?
(
command
.
join
(
' '
))}.
empty?
background_process
(
*
command
)
else
puts
"Process '
#{
command
.
join
(
' '
)
}
already running, skipping"
.
blue
end
end
def
environments
(
system
)
def
environments
(
system
)
Dir
[
"
#{
system
}
/envs/**/*.py"
].
select
{
|
file
|
!
(
/__init__.py$/
=~
file
)}.
map
do
|
env_file
|
Dir
[
"
#{
system
}
/envs/**/*.py"
].
select
{
|
file
|
!
(
/__init__.py$/
=~
file
)}.
map
do
|
env_file
|
env_file
.
gsub
(
"
#{
system
}
/envs/"
,
''
).
gsub
(
/\.py/
,
''
).
gsub
(
'/'
,
'.'
)
env_file
.
gsub
(
"
#{
system
}
/envs/"
,
''
).
gsub
(
/\.py/
,
''
).
gsub
(
'/'
,
'.'
)
...
...
rake
files
/i18n.rake
→
rake
lib
/i18n.rake
View file @
1d8d507f
File moved
rake
files
/jasmine.rake
→
rake
lib
/jasmine.rake
View file @
1d8d507f
File moved
rake
files
/prereqs.rake
→
rake
lib
/prereqs.rake
View file @
1d8d507f
require
'./rakefiles/helpers.rb'
PREREQS_MD5_DIR
=
ENV
[
"PREREQ_CACHE_DIR"
]
||
File
.
join
(
REPO_ROOT
,
'.prereqs_cache'
)
PREREQS_MD5_DIR
=
ENV
[
"PREREQ_CACHE_DIR"
]
||
File
.
join
(
REPO_ROOT
,
'.prereqs_cache'
)
CLOBBER
.
include
(
PREREQS_MD5_DIR
)
CLOBBER
.
include
(
PREREQS_MD5_DIR
)
...
...
rake
files
/quality.rake
→
rake
lib
/quality.rake
View file @
1d8d507f
File moved
rake
files
/tests.rake
→
rake
lib
/tests.rake
View file @
1d8d507f
File moved
rake
files
/workspace.rake
→
rake
lib
/workspace.rake
View file @
1d8d507f
File moved
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment