Merge remote-tracking branch 'origin/master' into feature/bridger/new_wiki
This commit is contained in:
@@ -24,6 +24,7 @@ MODULESTORE = {
|
||||
'db': 'xmodule',
|
||||
'collection': 'modulestore',
|
||||
'fs_root': GITHUB_REPO_ROOT,
|
||||
'render_template': 'mitxmako.shortcuts.render_to_string',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ MODULESTORE = {
|
||||
'db': 'test_xmodule',
|
||||
'collection': 'modulestore',
|
||||
'fs_root': GITHUB_REPO_ROOT,
|
||||
'render_template': 'mitxmako.shortcuts.render_to_string',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ def try_staticfiles_lookup(path):
|
||||
try:
|
||||
url = staticfiles_storage.url(path)
|
||||
except Exception as err:
|
||||
log.warning("staticfiles_storage couldn't find path {}: {}".format(
|
||||
log.warning("staticfiles_storage couldn't find path {0}: {1}".format(
|
||||
path, str(err)))
|
||||
# Just return the original path; don't kill everything.
|
||||
url = path
|
||||
|
||||
@@ -279,7 +279,8 @@ def replicate_enrollment_save(sender, **kwargs):
|
||||
|
||||
enrollment_obj = kwargs['instance']
|
||||
log.debug("Replicating user because of new enrollment")
|
||||
replicate_user(enrollment_obj.user, enrollment_obj.course_id)
|
||||
for course_db_name in db_names_to_replicate_to(enrollment_obj.user.id):
|
||||
replicate_user(enrollment_obj.user, course_db_name)
|
||||
|
||||
log.debug("Replicating enrollment because of new enrollment")
|
||||
replicate_model(CourseEnrollment.save, enrollment_obj, enrollment_obj.user_id)
|
||||
|
||||
@@ -873,6 +873,7 @@ def sympy_check2():
|
||||
msg = '<font color="red">No answer entered!</font>' if self.xml.get('empty_answer_err') else ''
|
||||
return CorrectMap(idset[0], 'incorrect', msg=msg)
|
||||
|
||||
# NOTE: correct = 'unknown' could be dangerous. Inputtypes such as textline are not expecting 'unknown's
|
||||
correct = ['unknown'] * len(idset)
|
||||
messages = [''] * len(idset)
|
||||
|
||||
@@ -898,6 +899,7 @@ def sympy_check2():
|
||||
if type(self.code) == str:
|
||||
try:
|
||||
exec self.code in self.context['global_context'], self.context
|
||||
correct = self.context['correct']
|
||||
except Exception as err:
|
||||
print "oops in customresponse (code) error %s" % err
|
||||
print "context = ", self.context
|
||||
@@ -1271,7 +1273,7 @@ main()
|
||||
|
||||
def setup_response(self):
|
||||
xml = self.xml
|
||||
self.url = xml.get('url') or "http://eecs1.mit.edu:8889/pyloncapa" # FIXME - hardcoded URL
|
||||
self.url = xml.get('url') or "http://qisx.mit.edu:8889/pyloncapa" # FIXME - hardcoded URL
|
||||
|
||||
# answer = xml.xpath('//*[@id=$id]//answer',id=xml.get('id'))[0] # FIXME - catch errors
|
||||
answer = xml.find('answer')
|
||||
|
||||
@@ -38,5 +38,7 @@
|
||||
% if msg:
|
||||
<span class="message">${msg|n}</span>
|
||||
% endif
|
||||
% if state in ['unsubmitted', 'correct', 'incorrect', 'incomplete'] or hidden:
|
||||
</div>
|
||||
% endif
|
||||
</section>
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="mitxmako",
|
||||
version="0.1",
|
||||
packages=find_packages(exclude=["tests"]),
|
||||
install_requires=['distribute'],
|
||||
)
|
||||
@@ -10,7 +10,6 @@ setup(
|
||||
},
|
||||
requires=[
|
||||
'capa',
|
||||
'mitxmako'
|
||||
],
|
||||
|
||||
# See http://guide.python-distribute.org/creation.html#entry-points
|
||||
|
||||
@@ -68,7 +68,7 @@ class SemanticSectionDescriptor(XModuleDescriptor):
|
||||
the child element
|
||||
"""
|
||||
xml_object = etree.fromstring(xml_data)
|
||||
system.error_tracker("WARNING: the <{}> tag is deprecated. Please do not use in new content."
|
||||
system.error_tracker("WARNING: the <{0}> tag is deprecated. Please do not use in new content."
|
||||
.format(xml_object.tag))
|
||||
|
||||
if len(xml_object) == 1:
|
||||
|
||||
@@ -108,11 +108,9 @@ class CapaModule(XModule):
|
||||
self.grace_period = None
|
||||
self.close_date = self.display_due_date
|
||||
|
||||
self.max_attempts = only_one(dom2.xpath('/problem/@attempts'))
|
||||
if len(self.max_attempts) > 0:
|
||||
self.max_attempts = self.metadata.get('attempts', None)
|
||||
if self.max_attempts is not None:
|
||||
self.max_attempts = int(self.max_attempts)
|
||||
else:
|
||||
self.max_attempts = None
|
||||
|
||||
self.show_answer = self.metadata.get('showanswer', 'closed')
|
||||
|
||||
@@ -244,7 +242,14 @@ class CapaModule(XModule):
|
||||
|
||||
# We using strings as truthy values, because the terminology of the
|
||||
# check button is context-specific.
|
||||
check_button = "Grade" if self.max_attempts else "Check"
|
||||
|
||||
# Put a "Check" button if unlimited attempts or still some left
|
||||
if self.max_attempts is None or self.attempts < self.max_attempts-1:
|
||||
check_button = "Check"
|
||||
else:
|
||||
# Will be final check so let user know that
|
||||
check_button = "Final Check"
|
||||
|
||||
reset_button = True
|
||||
save_button = True
|
||||
|
||||
|
||||
@@ -1,298 +1,295 @@
|
||||
h2 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 15px;
|
||||
width: flex-grid(2, 9);
|
||||
padding-right: flex-gutter(9);
|
||||
border-right: 1px dashed #ddd;
|
||||
@include box-sizing(border-box);
|
||||
display: table-cell;
|
||||
vertical-align: top;
|
||||
margin-top: 0;
|
||||
margin-bottom: 15px;
|
||||
width: flex-grid(2, 9);
|
||||
padding-right: flex-gutter(9);
|
||||
border-right: 1px dashed #ddd;
|
||||
@include box-sizing(border-box);
|
||||
display: table-cell;
|
||||
vertical-align: top;
|
||||
|
||||
&.problem-header {
|
||||
section.staff {
|
||||
margin-top: 30px;
|
||||
font-size: 80%;
|
||||
&.problem-header {
|
||||
section.staff {
|
||||
margin-top: 30px;
|
||||
font-size: 80%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width:1120px) {
|
||||
display: block;
|
||||
width: auto;
|
||||
border-right: 0;
|
||||
}
|
||||
@media screen and (max-width:1120px) {
|
||||
display: block;
|
||||
width: auto;
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
@media print {
|
||||
display: block;
|
||||
width: auto;
|
||||
border-right: 0;
|
||||
}
|
||||
@media print {
|
||||
display: block;
|
||||
width: auto;
|
||||
border-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
section.problem {
|
||||
display: table-cell;
|
||||
width: flex-grid(7, 9);
|
||||
padding-left: flex-gutter(9);
|
||||
display: table-cell;
|
||||
width: flex-grid(7, 9);
|
||||
padding-left: flex-gutter(9);
|
||||
|
||||
@media screen and (max-width:1120px) {
|
||||
display: block;
|
||||
width: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@media print {
|
||||
display: block;
|
||||
width: auto;
|
||||
padding: 0;
|
||||
|
||||
canvas, img {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
div {
|
||||
p.status {
|
||||
text-indent: -9999px;
|
||||
margin: -1px 0 0 10px;
|
||||
}
|
||||
|
||||
&.unanswered {
|
||||
p.status {
|
||||
@include inline-block();
|
||||
background: url('../images/unanswered-icon.png') center center no-repeat;
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
&.processing {
|
||||
p.status {
|
||||
@include inline-block();
|
||||
background: url('../images/spinner.gif') center center no-repeat;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
text-indent: -9999px;
|
||||
}
|
||||
}
|
||||
|
||||
&.correct, &.ui-icon-check {
|
||||
p.status {
|
||||
@include inline-block();
|
||||
background: url('../images/correct-icon.png') center center no-repeat;
|
||||
height: 20px;
|
||||
width: 25px;
|
||||
}
|
||||
|
||||
input {
|
||||
border-color: green;
|
||||
}
|
||||
}
|
||||
|
||||
&.processing {
|
||||
p.status {
|
||||
@include inline-block();
|
||||
background: url('../images/spinner.gif') center center no-repeat;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
input {
|
||||
border-color: #aaa;
|
||||
}
|
||||
}
|
||||
|
||||
&.incorrect, &.ui-icon-close {
|
||||
p.status {
|
||||
@include inline-block();
|
||||
background: url('../images/incorrect-icon.png') center center no-repeat;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
text-indent: -9999px;
|
||||
}
|
||||
|
||||
input {
|
||||
border-color: red;
|
||||
}
|
||||
}
|
||||
|
||||
> span {
|
||||
@media screen and (max-width:1120px) {
|
||||
display: block;
|
||||
margin-bottom: lh(.5);
|
||||
width: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
p.answer {
|
||||
@include inline-block();
|
||||
margin-bottom: 0;
|
||||
margin-left: 10px;
|
||||
|
||||
&:before {
|
||||
content: "Answer: ";
|
||||
font-weight: bold;
|
||||
display: inline;
|
||||
@media print {
|
||||
display: block;
|
||||
width: auto;
|
||||
padding: 0;
|
||||
|
||||
canvas, img {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
&:empty {
|
||||
}
|
||||
|
||||
div {
|
||||
p {
|
||||
&.answer {
|
||||
margin-top: -2px;
|
||||
}
|
||||
&.status {
|
||||
text-indent: -9999px;
|
||||
margin: 8px 0 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&.unanswered {
|
||||
p.status {
|
||||
@include inline-block();
|
||||
background: url('../images/unanswered-icon.png') center center no-repeat;
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
&.processing {
|
||||
p.status {
|
||||
@include inline-block();
|
||||
background: url('../images/spinner.gif') center center no-repeat;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
text-indent: -9999px;
|
||||
}
|
||||
}
|
||||
|
||||
&.correct, &.ui-icon-check {
|
||||
p.status {
|
||||
@include inline-block();
|
||||
background: url('../images/correct-icon.png') center center no-repeat;
|
||||
height: 20px;
|
||||
width: 25px;
|
||||
}
|
||||
|
||||
input {
|
||||
border-color: green;
|
||||
}
|
||||
}
|
||||
|
||||
&.processing {
|
||||
p.status {
|
||||
@include inline-block();
|
||||
background: url('../images/spinner.gif') center center no-repeat;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
input {
|
||||
border-color: #aaa;
|
||||
}
|
||||
}
|
||||
|
||||
&.incorrect, &.ui-icon-close {
|
||||
p.status {
|
||||
@include inline-block();
|
||||
background: url('../images/incorrect-icon.png') center center no-repeat;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
text-indent: -9999px;
|
||||
}
|
||||
|
||||
input {
|
||||
border-color: red;
|
||||
}
|
||||
}
|
||||
|
||||
> span {
|
||||
display: block;
|
||||
margin-bottom: lh(.5);
|
||||
}
|
||||
|
||||
p.answer {
|
||||
@include inline-block();
|
||||
margin-bottom: 0;
|
||||
margin-left: 10px;
|
||||
|
||||
&:before {
|
||||
display: none;
|
||||
content: "Answer: ";
|
||||
font-weight: bold;
|
||||
display: inline;
|
||||
|
||||
}
|
||||
&:empty {
|
||||
&:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.equation {
|
||||
clear: both;
|
||||
padding: 6px;
|
||||
background: #eee;
|
||||
|
||||
span {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
&.unanswered, &.ui-icon-bullet {
|
||||
@include inline-block();
|
||||
background: url('../images/unanswered-icon.png') center center no-repeat;
|
||||
height: 14px;
|
||||
position: relative;
|
||||
top: 4px;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
&.processing, &.ui-icon-processing {
|
||||
@include inline-block();
|
||||
background: url('../images/spinner.gif') center center no-repeat;
|
||||
height: 20px;
|
||||
position: relative;
|
||||
top: 6px;
|
||||
width: 25px;
|
||||
}
|
||||
|
||||
&.correct, &.ui-icon-check {
|
||||
@include inline-block();
|
||||
background: url('../images/correct-icon.png') center center no-repeat;
|
||||
height: 20px;
|
||||
position: relative;
|
||||
top: 6px;
|
||||
width: 25px;
|
||||
}
|
||||
|
||||
&.incorrect, &.ui-icon-close {
|
||||
@include inline-block();
|
||||
background: url('../images/incorrect-icon.png') center center no-repeat;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
position: relative;
|
||||
top: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.equation {
|
||||
clear: both;
|
||||
padding: 6px;
|
||||
background: #eee;
|
||||
ul {
|
||||
list-style: disc outside none;
|
||||
margin-bottom: lh();
|
||||
margin-left: .75em;
|
||||
margin-left: .75rem;
|
||||
}
|
||||
|
||||
span {
|
||||
ol {
|
||||
list-style: decimal outside none;
|
||||
margin-bottom: lh();
|
||||
margin-left: .75em;
|
||||
margin-left: .75rem;
|
||||
}
|
||||
|
||||
dl {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
dl dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
dl dd {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-left: .5em;
|
||||
margin-left: .5rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.4em;
|
||||
margin-bottom: lh(.5);
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
&.unanswered, &.ui-icon-bullet {
|
||||
@include inline-block();
|
||||
background: url('../images/unanswered-icon.png') center center no-repeat;
|
||||
height: 14px;
|
||||
position: relative;
|
||||
top: 4px;
|
||||
width: 14px;
|
||||
p {
|
||||
margin-bottom: lh();
|
||||
}
|
||||
|
||||
table {
|
||||
margin-bottom: lh();
|
||||
border-collapse: collapse;
|
||||
table-layout: auto;
|
||||
|
||||
th {
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&.processing, &.ui-icon-processing {
|
||||
@include inline-block();
|
||||
background: url('../images/spinner.gif') center center no-repeat;
|
||||
height: 20px;
|
||||
position: relative;
|
||||
top: 6px;
|
||||
width: 25px;
|
||||
caption, th, td {
|
||||
padding: .25em .75em .25em 0;
|
||||
padding: .25rem .75rem .25rem 0;
|
||||
}
|
||||
|
||||
&.correct, &.ui-icon-check {
|
||||
@include inline-block();
|
||||
background: url('../images/correct-icon.png') center center no-repeat;
|
||||
height: 20px;
|
||||
position: relative;
|
||||
top: 6px;
|
||||
width: 25px;
|
||||
caption {
|
||||
background: #f1f1f1;
|
||||
margin-bottom: .75em;
|
||||
margin-bottom: .75rem;
|
||||
padding: .75em 0;
|
||||
padding: .75rem 0;
|
||||
}
|
||||
|
||||
&.incorrect, &.ui-icon-close {
|
||||
@include inline-block();
|
||||
background: url('../images/incorrect-icon.png') center center no-repeat;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
position: relative;
|
||||
top: 6px;
|
||||
tr, td, th {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: disc outside none;
|
||||
margin-bottom: lh();
|
||||
margin-left: .75em;
|
||||
margin-left: .75rem;
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style: decimal outside none;
|
||||
margin-bottom: lh();
|
||||
margin-left: .75em;
|
||||
margin-left: .75rem;
|
||||
}
|
||||
|
||||
dl {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
dl dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
dl dd {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-left: .5em;
|
||||
margin-left: .5rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.4em;
|
||||
margin-bottom: lh(.5);
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: lh();
|
||||
}
|
||||
|
||||
table {
|
||||
margin-bottom: lh();
|
||||
width: 100%;
|
||||
// border: 1px solid #ddd;
|
||||
border-collapse: collapse;
|
||||
|
||||
th {
|
||||
// border-bottom: 2px solid #ccc;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td {
|
||||
// border: 1px solid #ddd;
|
||||
hr {
|
||||
background: #ddd;
|
||||
border: none;
|
||||
clear: both;
|
||||
color: #ddd;
|
||||
float: none;
|
||||
height: 1px;
|
||||
margin: 0 0 .75rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
caption, th, td {
|
||||
padding: .25em .75em .25em 0;
|
||||
padding: .25rem .75rem .25rem 0;
|
||||
.hidden {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
caption {
|
||||
background: #f1f1f1;
|
||||
margin-bottom: .75em;
|
||||
margin-bottom: .75rem;
|
||||
padding: .75em 0;
|
||||
padding: .75rem 0;
|
||||
#{$all-text-inputs} {
|
||||
display: inline;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
tr, td, th {
|
||||
vertical-align: middle;
|
||||
// this supports a deprecated element and should be removed once the center tag is removed
|
||||
center {
|
||||
display: block;
|
||||
margin: lh() 0;
|
||||
border: 1px solid #ccc;
|
||||
padding: lh();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
hr {
|
||||
background: #ddd;
|
||||
border: none;
|
||||
clear: both;
|
||||
color: #ddd;
|
||||
float: none;
|
||||
height: 1px;
|
||||
margin: 0 0 .75rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#{$all-text-inputs} {
|
||||
display: inline;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
// this supports a deprecated element and should be removed once the center tag is removed
|
||||
center {
|
||||
display: block;
|
||||
margin: lh() 0;
|
||||
border: 1px solid #ccc;
|
||||
padding: lh();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ div.video {
|
||||
|
||||
section.video-player {
|
||||
height: 0;
|
||||
// overflow: hidden;
|
||||
overflow: hidden;
|
||||
padding-bottom: 56.25%;
|
||||
position: relative;
|
||||
|
||||
@@ -171,7 +171,7 @@ div.video {
|
||||
position: relative;
|
||||
@include transition();
|
||||
-webkit-font-smoothing: antialiased;
|
||||
width: 110px;
|
||||
width: 116px;
|
||||
|
||||
h3 {
|
||||
color: #999;
|
||||
@@ -209,7 +209,7 @@ div.video {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
width: 125px;
|
||||
width: 133px;
|
||||
z-index: 10;
|
||||
|
||||
li {
|
||||
@@ -444,13 +444,13 @@ div.video {
|
||||
height: 100%;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
max-height: 100%;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
vertical-align: middle;
|
||||
|
||||
&.closed {
|
||||
ol.subtitles {
|
||||
@@ -459,30 +459,17 @@ div.video {
|
||||
}
|
||||
}
|
||||
|
||||
a.exit {
|
||||
color: #aaa;
|
||||
display: none;
|
||||
font-style: 12px;
|
||||
left: 20px;
|
||||
letter-spacing: 1px;
|
||||
position: absolute;
|
||||
text-transform: uppercase;
|
||||
top: 20px;
|
||||
|
||||
&::after {
|
||||
content: "✖";
|
||||
@include inline-block();
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $mit-red;
|
||||
}
|
||||
}
|
||||
|
||||
div.tc-wrapper {
|
||||
@include clearfix;
|
||||
display: table;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
article.video-wrapper {
|
||||
width: 100%;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
float: none;
|
||||
}
|
||||
|
||||
object, iframe {
|
||||
|
||||
@@ -39,6 +39,8 @@ class HtmlDescriptor(XmlDescriptor, EditingDescriptor):
|
||||
def backcompat_paths(cls, path):
|
||||
if path.endswith('.html.xml'):
|
||||
path = path[:-9] + '.html' # backcompat--look for html instead of xml
|
||||
if path.endswith('.html.html'):
|
||||
path = path[:-5] # some people like to include .html in filenames..
|
||||
candidates = []
|
||||
while os.sep in path:
|
||||
candidates.append(path)
|
||||
@@ -73,7 +75,9 @@ class HtmlDescriptor(XmlDescriptor, EditingDescriptor):
|
||||
cls.clean_metadata_from_xml(definition_xml)
|
||||
return {'data': stringify_children(definition_xml)}
|
||||
else:
|
||||
filepath = cls._format_filepath(xml_object.tag, filename)
|
||||
# html is special. cls.filename_extension is 'xml', but if 'filename' is in the definition,
|
||||
# that means to load from .html
|
||||
filepath = "{category}/{name}.html".format(category='html', name=filename)
|
||||
|
||||
# VS[compat]
|
||||
# TODO (cpennington): If the file doesn't exist at the right path,
|
||||
|
||||
@@ -130,13 +130,11 @@ class @VideoPlayer extends Subview
|
||||
toggleFullScreen: (event) =>
|
||||
event.preventDefault()
|
||||
if @el.hasClass('fullscreen')
|
||||
@$('.exit').remove()
|
||||
@$('.add-fullscreen').attr('title', 'Fill browser')
|
||||
@el.removeClass('fullscreen')
|
||||
else
|
||||
@el.append('<a href="#" class="exit">Exit</a>').addClass('fullscreen')
|
||||
@el.addClass('fullscreen')
|
||||
@$('.add-fullscreen').attr('title', 'Exit fill browser')
|
||||
@$('.exit').click @toggleFullScreen
|
||||
@caption.resize()
|
||||
|
||||
# Delegates
|
||||
|
||||
@@ -12,15 +12,34 @@ from django.conf import settings
|
||||
|
||||
_MODULESTORES = {}
|
||||
|
||||
FUNCTION_KEYS = ['render_template']
|
||||
|
||||
|
||||
def load_function(path):
|
||||
"""
|
||||
Load a function by name.
|
||||
|
||||
path is a string of the form "path.to.module.function"
|
||||
returns the imported python object `function` from `path.to.module`
|
||||
"""
|
||||
module_path, _, name = path.rpartition('.')
|
||||
return getattr(import_module(module_path), name)
|
||||
|
||||
|
||||
def modulestore(name='default'):
|
||||
global _MODULESTORES
|
||||
|
||||
if name not in _MODULESTORES:
|
||||
class_path = settings.MODULESTORE[name]['ENGINE']
|
||||
module_path, _, class_name = class_path.rpartition('.')
|
||||
class_ = getattr(import_module(module_path), class_name)
|
||||
class_ = load_function(settings.MODULESTORE[name]['ENGINE'])
|
||||
|
||||
options = {}
|
||||
options.update(settings.MODULESTORE[name]['OPTIONS'])
|
||||
for key in FUNCTION_KEYS:
|
||||
if key in options:
|
||||
options[key] = load_function(options[key])
|
||||
|
||||
_MODULESTORES[name] = class_(
|
||||
**settings.MODULESTORE[name]['OPTIONS'])
|
||||
**options
|
||||
)
|
||||
|
||||
return _MODULESTORES[name]
|
||||
|
||||
@@ -9,7 +9,6 @@ from importlib import import_module
|
||||
from xmodule.errortracker import null_error_tracker
|
||||
from xmodule.x_module import XModuleDescriptor
|
||||
from xmodule.mako_module import MakoDescriptorSystem
|
||||
from mitxmako.shortcuts import render_to_string
|
||||
|
||||
from . import ModuleStoreBase, Location
|
||||
from .exceptions import (ItemNotFoundError,
|
||||
@@ -82,7 +81,8 @@ class MongoModuleStore(ModuleStoreBase):
|
||||
"""
|
||||
|
||||
# TODO (cpennington): Enable non-filesystem filestores
|
||||
def __init__(self, host, db, collection, fs_root, port=27017, default_class=None,
|
||||
def __init__(self, host, db, collection, fs_root, render_template,
|
||||
port=27017, default_class=None,
|
||||
error_tracker=null_error_tracker):
|
||||
|
||||
ModuleStoreBase.__init__(self)
|
||||
@@ -108,6 +108,7 @@ class MongoModuleStore(ModuleStoreBase):
|
||||
self.default_class = None
|
||||
self.fs_root = path(fs_root)
|
||||
self.error_tracker = error_tracker
|
||||
self.render_template = render_template
|
||||
|
||||
def _clean_item_data(self, item):
|
||||
"""
|
||||
@@ -160,7 +161,7 @@ class MongoModuleStore(ModuleStoreBase):
|
||||
self.default_class,
|
||||
resource_fs,
|
||||
self.error_tracker,
|
||||
render_to_string,
|
||||
self.render_template,
|
||||
)
|
||||
return system.load_item(item['location'])
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ DB = 'test'
|
||||
COLLECTION = 'modulestore'
|
||||
FS_ROOT = DATA_DIR # TODO (vshnayder): will need a real fs_root for testing load_item
|
||||
DEFAULT_CLASS = 'xmodule.raw_module.RawDescriptor'
|
||||
RENDER_TEMPLATE = lambda t_n, d, ctx=None, nsp='main': ''
|
||||
|
||||
|
||||
class TestMongoModuleStore(object):
|
||||
@@ -48,7 +49,7 @@ class TestMongoModuleStore(object):
|
||||
@staticmethod
|
||||
def initdb():
|
||||
# connect to the db
|
||||
store = MongoModuleStore(HOST, DB, COLLECTION, FS_ROOT, default_class=DEFAULT_CLASS)
|
||||
store = MongoModuleStore(HOST, DB, COLLECTION, FS_ROOT, RENDER_TEMPLATE, default_class=DEFAULT_CLASS)
|
||||
# Explicitly list the courses to load (don't want the big one)
|
||||
courses = ['toy', 'simple']
|
||||
import_from_xml(store, DATA_DIR, courses)
|
||||
|
||||
@@ -187,11 +187,11 @@ class XMLModuleStore(ModuleStoreBase):
|
||||
if not os.path.exists(policy_path):
|
||||
return {}
|
||||
try:
|
||||
log.debug("Loading policy from {}".format(policy_path))
|
||||
log.debug("Loading policy from {0}".format(policy_path))
|
||||
with open(policy_path) as f:
|
||||
return json.load(f)
|
||||
except (IOError, ValueError) as err:
|
||||
msg = "Error loading course policy from {}".format(policy_path)
|
||||
msg = "Error loading course policy from {0}".format(policy_path)
|
||||
tracker(msg)
|
||||
log.warning(msg + " " + str(err))
|
||||
return {}
|
||||
@@ -238,7 +238,7 @@ class XMLModuleStore(ModuleStoreBase):
|
||||
|
||||
url_name = course_data.get('url_name')
|
||||
if url_name:
|
||||
policy_path = self.data_dir / course_dir / 'policies' / '{}.json'.format(url_name)
|
||||
policy_path = self.data_dir / course_dir / 'policies' / '{0}.json'.format(url_name)
|
||||
policy = self.load_policy(policy_path, tracker)
|
||||
else:
|
||||
policy = {}
|
||||
|
||||
@@ -201,7 +201,7 @@ class ImportTestCase(unittest.TestCase):
|
||||
|
||||
def check_for_key(key, node):
|
||||
"recursive check for presence of key"
|
||||
print "Checking {}".format(node.location.url())
|
||||
print "Checking {0}".format(node.location.url())
|
||||
self.assertTrue(key in node.metadata)
|
||||
for c in node.get_children():
|
||||
check_for_key(key, c)
|
||||
|
||||
@@ -629,7 +629,7 @@ class XModuleDescriptor(Plugin, HTMLSnippet):
|
||||
try:
|
||||
return parse_time(self.metadata[key])
|
||||
except ValueError as e:
|
||||
msg = "Descriptor {} loaded with a bad metadata key '{}': '{}'".format(
|
||||
msg = "Descriptor {0} loaded with a bad metadata key '{1}': '{2}'".format(
|
||||
self.location.url(), self.metadata[key], e)
|
||||
log.warning(msg)
|
||||
return None
|
||||
|
||||
@@ -47,12 +47,12 @@ def cleanup(filepath, remove_meta):
|
||||
'ispublic', 'xqa_key')
|
||||
|
||||
try:
|
||||
print "Cleaning {}".format(filepath)
|
||||
print "Cleaning {0}".format(filepath)
|
||||
with open(filepath) as f:
|
||||
parser = etree.XMLParser(remove_comments=False)
|
||||
xml = etree.parse(filepath, parser=parser)
|
||||
except:
|
||||
print "Error parsing file {}".format(filepath)
|
||||
print "Error parsing file {0}".format(filepath)
|
||||
return
|
||||
|
||||
for node in xml.iter(tag=etree.Element):
|
||||
@@ -67,12 +67,12 @@ def cleanup(filepath, remove_meta):
|
||||
del attrs['name']
|
||||
|
||||
if 'url_name' in attrs and 'slug' in attrs:
|
||||
print "WARNING: {} has both slug and url_name"
|
||||
print "WARNING: {0} has both slug and url_name".format(node)
|
||||
|
||||
if ('url_name' in attrs and 'filename' in attrs and
|
||||
len(attrs)==2 and attrs['url_name'] == attrs['filename']):
|
||||
# This is a pointer tag in disguise. Get rid of the filename.
|
||||
print 'turning {}.{} into a pointer tag'.format(node.tag, attrs['url_name'])
|
||||
print 'turning {0}.{1} into a pointer tag'.format(node.tag, attrs['url_name'])
|
||||
del attrs['filename']
|
||||
|
||||
if remove_meta:
|
||||
|
||||
147
doc/xml-format.md
Normal file
147
doc/xml-format.md
Normal file
@@ -0,0 +1,147 @@
|
||||
This doc is a rough spec of our xml format
|
||||
|
||||
Every content element (within a course) should have a unique id. This id is formed as {category}/{url_name}. Categories are the different tag types ('chapter', 'problem', 'html', 'sequential', etc). Url_name is a string containing a-z, A-Z, dot (.) and _. This is what appears in urls that point to this object.
|
||||
|
||||
File layout:
|
||||
|
||||
- Xml files have content
|
||||
- "policy", which is also called metadata in various places, should live in a policy file.
|
||||
|
||||
- each module (except customtag and course, which are special, see below) should live in a file, located at {category}/{url_name].xml
|
||||
To include this module in another one (e.g. to put a problem in a vertical), put in a "pointer tag": <{category} url_name="{url_name}"/>. When we read that, we'll load the actual contents.
|
||||
|
||||
Customtag is already a pointer, you can just use it in place: <customtag url_name="my_custom_tag" impl="blah" attr1="..."/>
|
||||
|
||||
Course tags:
|
||||
- the top level course pointer tag lives in course.xml
|
||||
- have 2 extra required attributes: "org" and "course" -- organization name, and course name. Note that the course name is referring to the platonic ideal of this course, not to any particular run of this course. The url_name should be particular run of this course. E.g.
|
||||
|
||||
If course.xml contains:
|
||||
<course org="HarvardX" course="cs50" url_name="2012"/>
|
||||
|
||||
we would load the actual course definition from course/2012.xml
|
||||
|
||||
To support multiple different runs of the course, you could have a different course.xml, containing
|
||||
|
||||
<course org="HarvardX" course="cs50" url_name="2012H"/>
|
||||
|
||||
which would load the Harvard-internal version from course/2012H.xml
|
||||
|
||||
If there is only one run of the course for now, just have a single course.xml with the right url_name.
|
||||
|
||||
If there is more than one run of the course, the different course root pointer files should live in
|
||||
roots/url_name.xml, and course.xml should be a symbolic link to the one you want to run in your dev instance.
|
||||
|
||||
If you want to run both versions, you need to checkout the repo twice, and have course.xml point to different root/{url_name}.xml files.
|
||||
|
||||
Policies:
|
||||
- the policy for a course url_name lives in policies/{url_name}.json
|
||||
|
||||
The format is called "json", and is best shown by example (though also feel free to google :)
|
||||
|
||||
the file is a dictionary (mapping from keys to values, syntax "{ key : value, key2 : value2, etc}"
|
||||
|
||||
Keys are in the form "{category}/{url_name}", which should uniquely id a content element.
|
||||
Values are dictionaries of the form {"metadata-key" : "metadata-value"}.
|
||||
|
||||
metadata can also live in the xml files, but anything defined in the policy file overrides anything in the xml. This is primarily for backwards compatibility, and you should probably not use both. If you do leave some metadata tags in the xml, please be consistent (e.g. if display_names stay in xml, they should all stay in xml).
|
||||
- note, some xml attributes are not metadata. e.g. in <video youtube="xyz987293487293847"/>, the youtube attribute specifies what video this is, and is logically part of the content, not the policy, so it should stay in video/{url_name}.xml.
|
||||
|
||||
Example policy file:
|
||||
{
|
||||
"course/2012": {
|
||||
"graceperiod": "1 day",
|
||||
"start": "2012-10-15T12:00",
|
||||
"display_name": "Introduction to Computer Science I",
|
||||
"xqa_key": "z1y4vdYcy0izkoPeihtPClDxmbY1ogDK"
|
||||
},
|
||||
"chapter/Week_0": {
|
||||
"display_name": "Week 0"
|
||||
},
|
||||
"sequential/Pre-Course_Survey": {
|
||||
"display_name": "Pre-Course Survey",
|
||||
"format": "Survey"
|
||||
}
|
||||
}
|
||||
|
||||
NOTE: json is picky about commas. If you have trailing commas before closing braces, it will complain and refuse to parse the file. This is irritating.
|
||||
|
||||
|
||||
Valid tag categories:
|
||||
|
||||
abtest
|
||||
chapter
|
||||
course
|
||||
customtag
|
||||
html
|
||||
error -- don't put these in by hand :)
|
||||
problem
|
||||
problemset
|
||||
sequential
|
||||
vertical
|
||||
video
|
||||
videosequence
|
||||
|
||||
Obsolete tags:
|
||||
Use customtag instead:
|
||||
videodev
|
||||
book
|
||||
slides
|
||||
image
|
||||
discuss
|
||||
|
||||
Ex: instead of <book page="12"/>, use <customtag impl="book" page="12"/>
|
||||
|
||||
Use something semantic instead, as makes sense: sequential, vertical, videosequence if it's actually a sequence. If the section would only contain a single element, just include that element directly.
|
||||
section
|
||||
|
||||
In general, prefer the most "semantic" name for containers: e.g. use problemset rather than vertical for a problem set. That way, if we decide to display problem sets differently, we don't have to change the xml.
|
||||
|
||||
How customtags work:
|
||||
When we see <customtag impl="special" animal="unicorn" hat="blue"/>, we will:
|
||||
|
||||
- look for a file called custom_tags/special in your course dir.
|
||||
- render it as a mako template, passing parameters {'animal':'unicorn', 'hat':'blue'}, generating html.
|
||||
|
||||
|
||||
METADATA
|
||||
|
||||
Metadata that we generally understand:
|
||||
Only on course tag in courses/url_name.xml
|
||||
ispublic
|
||||
xqa_key -- set only on course, inherited to everything else
|
||||
|
||||
Everything:
|
||||
display_name
|
||||
format (maybe only content containers, e.g. "Lecture sequence", "problem set", "lab", etc. )
|
||||
start -- modules will not show up to non-course-staff users before the start date (in production)
|
||||
hide_from_toc -- if this is true, don't show in table of contents for the course. Useful on chapters, and chapter subsections that are linked to from somewhere else.
|
||||
|
||||
Used for problems
|
||||
graceperiod
|
||||
showanswer
|
||||
rerandomize
|
||||
graded
|
||||
due
|
||||
|
||||
|
||||
These are _inherited_ : if specified on the course, will apply to everything in the course, except for things that explicitly specify them, and their children.
|
||||
'graded', 'start', 'due', 'graceperiod', 'showanswer', 'rerandomize',
|
||||
# TODO (ichuang): used for Fall 2012 xqa server access
|
||||
'xqa_key',
|
||||
|
||||
Example sketch:
|
||||
<course start="tue">
|
||||
<chap1> -- start tue
|
||||
<problem> --- start tue
|
||||
</chap1>
|
||||
<chap2 start="wed"> -- start wed
|
||||
<problem2 start="thu"> -- start thu
|
||||
<problem3> -- start wed
|
||||
</chap2>
|
||||
</course>
|
||||
|
||||
|
||||
STATIC LINKS:
|
||||
|
||||
if your content links (e.g. in an html file) to "static/blah/ponies.jpg", we will look for this in YOUR_COURSE_DIR/blah/ponies.jpg. Note that this is not looking in a static/ subfolder in your course dir. This may (should?) change at some point.
|
||||
@@ -65,7 +65,7 @@ def has_access(user, obj, action):
|
||||
|
||||
# Passing an unknown object here is a coding error, so rather than
|
||||
# returning a default, complain.
|
||||
raise TypeError("Unknown object type in has_access(): '{}'"
|
||||
raise TypeError("Unknown object type in has_access(): '{0}'"
|
||||
.format(type(obj)))
|
||||
|
||||
|
||||
@@ -255,7 +255,7 @@ def _dispatch(table, action, user, obj):
|
||||
action)
|
||||
return result
|
||||
|
||||
raise ValueError("Unknown action for object type '{}': '{}'".format(
|
||||
raise ValueError("Unknown action for object type '{0}': '{1}'".format(
|
||||
type(obj), action))
|
||||
|
||||
def _course_staff_group_name(location):
|
||||
|
||||
@@ -41,7 +41,7 @@ def import_course(course_dir, verbose=True):
|
||||
course = courses[0]
|
||||
errors = modulestore.get_item_errors(course.location)
|
||||
if len(errors) != 0:
|
||||
sys.stderr.write('ERRORs during import: {}\n'.format('\n'.join(map(str_of_err, errors))))
|
||||
sys.stderr.write('ERRORs during import: {0}\n'.format('\n'.join(map(str_of_err, errors))))
|
||||
|
||||
return course
|
||||
|
||||
|
||||
@@ -75,6 +75,10 @@ def toc_for_course(user, request, course, active_chapter, active_section, course
|
||||
|
||||
chapters = list()
|
||||
for chapter in course.get_display_items():
|
||||
hide_from_toc = chapter.metadata.get('hide_from_toc','false').lower() == 'true'
|
||||
if hide_from_toc:
|
||||
continue
|
||||
|
||||
sections = list()
|
||||
for section in chapter.get_display_items():
|
||||
|
||||
@@ -323,7 +327,7 @@ def xqueue_callback(request, course_id, userid, id, dispatch):
|
||||
user, modulestore().get_item(id), depth=0, select_for_update=True)
|
||||
instance = get_module(user, request, id, student_module_cache)
|
||||
if instance is None:
|
||||
log.debug("No module {} for user {}--access denied?".format(id, user))
|
||||
log.debug("No module {0} for user {1}--access denied?".format(id, user))
|
||||
raise Http404
|
||||
|
||||
instance_module = get_instance_module(user, instance, student_module_cache)
|
||||
@@ -386,7 +390,7 @@ def modx_dispatch(request, dispatch=None, id=None, course_id=None):
|
||||
if instance is None:
|
||||
# Either permissions just changed, or someone is trying to be clever
|
||||
# and load something they shouldn't have access to.
|
||||
log.debug("No module {} for user {}--access denied?".format(id, user))
|
||||
log.debug("No module {0} for user {1}--access denied?".format(id, user))
|
||||
raise Http404
|
||||
|
||||
instance_module = get_instance_module(request.user, instance, student_module_cache)
|
||||
|
||||
@@ -56,6 +56,7 @@ def mongo_store_config(data_dir):
|
||||
'db': 'xmodule',
|
||||
'collection': 'modulestore',
|
||||
'fs_root': data_dir,
|
||||
'render_template': 'mitxmako.shortcuts.render_to_string',
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -176,7 +177,7 @@ class PageLoader(ActivateLoginTestCase):
|
||||
def try_enroll(self, course):
|
||||
"""Try to enroll. Return bool success instead of asserting it."""
|
||||
data = self._enroll(course)
|
||||
print 'Enrollment in {} result: {}'.format(course.location.url(), data)
|
||||
print 'Enrollment in {0} result: {1}'.format(course.location.url(), data)
|
||||
return data['success']
|
||||
|
||||
def enroll(self, course):
|
||||
@@ -308,7 +309,7 @@ class TestViewAuth(PageLoader):
|
||||
|
||||
# shouldn't be able to get to the instructor pages
|
||||
for url in instructor_urls(self.toy) + instructor_urls(self.full):
|
||||
print 'checking for 404 on {}'.format(url)
|
||||
print 'checking for 404 on {0}'.format(url)
|
||||
self.check_for_get_code(404, url)
|
||||
|
||||
# Make the instructor staff in the toy course
|
||||
@@ -321,11 +322,11 @@ class TestViewAuth(PageLoader):
|
||||
|
||||
# Now should be able to get to the toy course, but not the full course
|
||||
for url in instructor_urls(self.toy):
|
||||
print 'checking for 200 on {}'.format(url)
|
||||
print 'checking for 200 on {0}'.format(url)
|
||||
self.check_for_get_code(200, url)
|
||||
|
||||
for url in instructor_urls(self.full):
|
||||
print 'checking for 404 on {}'.format(url)
|
||||
print 'checking for 404 on {0}'.format(url)
|
||||
self.check_for_get_code(404, url)
|
||||
|
||||
|
||||
@@ -336,7 +337,7 @@ class TestViewAuth(PageLoader):
|
||||
|
||||
# and now should be able to load both
|
||||
for url in instructor_urls(self.toy) + instructor_urls(self.full):
|
||||
print 'checking for 200 on {}'.format(url)
|
||||
print 'checking for 200 on {0}'.format(url)
|
||||
self.check_for_get_code(200, url)
|
||||
|
||||
|
||||
@@ -387,7 +388,11 @@ class TestViewAuth(PageLoader):
|
||||
list of urls that students should be able to see only
|
||||
after launch, but staff should see before
|
||||
"""
|
||||
urls = reverse_urls(['info', 'book', 'courseware', 'profile'], course)
|
||||
urls = reverse_urls(['info', 'courseware', 'profile'], course)
|
||||
urls.extend([
|
||||
reverse('book', kwargs={'course_id': course.id, 'book_index': book.title})
|
||||
for book in course.textbooks
|
||||
])
|
||||
return urls
|
||||
|
||||
def light_student_urls(course):
|
||||
@@ -412,22 +417,22 @@ class TestViewAuth(PageLoader):
|
||||
|
||||
def check_non_staff(course):
|
||||
"""Check that access is right for non-staff in course"""
|
||||
print '=== Checking non-staff access for {}'.format(course.id)
|
||||
print '=== Checking non-staff access for {0}'.format(course.id)
|
||||
for url in instructor_urls(course) + dark_student_urls(course):
|
||||
print 'checking for 404 on {}'.format(url)
|
||||
print 'checking for 404 on {0}'.format(url)
|
||||
self.check_for_get_code(404, url)
|
||||
|
||||
for url in light_student_urls(course):
|
||||
print 'checking for 200 on {}'.format(url)
|
||||
print 'checking for 200 on {0}'.format(url)
|
||||
self.check_for_get_code(200, url)
|
||||
|
||||
def check_staff(course):
|
||||
"""Check that access is right for staff in course"""
|
||||
print '=== Checking staff access for {}'.format(course.id)
|
||||
print '=== Checking staff access for {0}'.format(course.id)
|
||||
for url in (instructor_urls(course) +
|
||||
dark_student_urls(course) +
|
||||
light_student_urls(course)):
|
||||
print 'checking for 200 on {}'.format(url)
|
||||
print 'checking for 200 on {0}'.format(url)
|
||||
self.check_for_get_code(200, url)
|
||||
|
||||
# First, try with an enrolled student
|
||||
|
||||
@@ -56,3 +56,7 @@ AWS_SECRET_ACCESS_KEY = AUTH_TOKENS["AWS_SECRET_ACCESS_KEY"]
|
||||
DATABASES = AUTH_TOKENS['DATABASES']
|
||||
|
||||
XQUEUE_INTERFACE = AUTH_TOKENS['XQUEUE_INTERFACE']
|
||||
|
||||
if 'COURSE_ID' in ENV_TOKENS:
|
||||
ASKBOT_URL = "courses/{0}/discussions/".format(ENV_TOKENS['COURSE_ID'])
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ MODULESTORE = {
|
||||
'db': 'xmodule',
|
||||
'collection': 'modulestore',
|
||||
'fs_root': GITHUB_REPO_ROOT,
|
||||
'render_template': 'mitxmako.shortcuts.render_to_string',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
</%block>
|
||||
|
||||
<%block name="js_extra">
|
||||
<script type="text/javascript" src="${static.url('js/vendor/jquery.scrollTo-1.4.2-min.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/vendor/flot/jquery.flot.js')}"></script>
|
||||
|
||||
## codemirror
|
||||
|
||||
@@ -61,6 +61,11 @@ http {
|
||||
location /courses/MITx/6.002x/2012_Fall/ {
|
||||
proxy_pass http://course_mitx_6002_2012_fall;
|
||||
}
|
||||
|
||||
location ~ /courses/([^/]*)/([^/]*)/([^/]*)/(course_wiki|wiki) {
|
||||
proxy_pass http://portal;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
30
rakefile
30
rakefile
@@ -25,7 +25,6 @@ PACKAGE_REPO = "packages@gp.mitx.mit.edu:/opt/pkgrepo.incoming"
|
||||
|
||||
NORMALIZED_DEPLOY_NAME = DEPLOY_NAME.downcase().gsub(/[_\/]/, '-')
|
||||
INSTALL_DIR_PATH = File.join(DEPLOY_DIR, NORMALIZED_DEPLOY_NAME)
|
||||
PIP_REPO_REQUIREMENTS = "#{INSTALL_DIR_PATH}/repo-requirements.txt"
|
||||
# Set up the clean and clobber tasks
|
||||
CLOBBER.include(BUILD_DIR, REPORT_DIR, 'cover*', '.coverage', 'test_root/*_repo', 'test_root/staticfiles')
|
||||
CLEAN.include("#{BUILD_DIR}/*.deb", "#{BUILD_DIR}/util")
|
||||
@@ -193,36 +192,7 @@ task :package do
|
||||
afterremove.close()
|
||||
FileUtils.chmod(0755, afterremove.path)
|
||||
|
||||
postinstall = Tempfile.new('postinstall')
|
||||
postinstall.write <<-POSTINSTALL.gsub(/^\s*/, '')
|
||||
#! /bin/sh
|
||||
set -e
|
||||
set -x
|
||||
|
||||
|
||||
service gunicorn stop || echo "Unable to stop gunicorn. Continuing"
|
||||
rm -f #{LINK_PATH}
|
||||
ln -s #{INSTALL_DIR_PATH} #{LINK_PATH}
|
||||
chown makeitso:makeitso #{LINK_PATH}
|
||||
|
||||
# install python modules that are in the package
|
||||
if [ -r #{PIP_REPO_REQUIREMENTS} ]; then
|
||||
cd #{INSTALL_DIR_PATH}
|
||||
pip install -r #{PIP_REPO_REQUIREMENTS}
|
||||
fi
|
||||
|
||||
chown -R makeitso:makeitso #{INSTALL_DIR_PATH}
|
||||
|
||||
# Delete mako temp files
|
||||
rm -rf /tmp/tmp*mako
|
||||
|
||||
service gunicorn start || echo "Unable to start gunicorn. Continuing"
|
||||
POSTINSTALL
|
||||
postinstall.close()
|
||||
FileUtils.chmod(0755, postinstall.path)
|
||||
|
||||
args = ["fakeroot", "fpm", "-s", "dir", "-t", "deb",
|
||||
"--after-install=#{postinstall.path}",
|
||||
"--after-remove=#{afterremove.path}",
|
||||
"--prefix=#{INSTALL_DIR_PATH}",
|
||||
"--exclude=**/build/**",
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
-e common/lib/capa
|
||||
-e common/lib/mitxmako
|
||||
-e common/lib/xmodule
|
||||
|
||||
Reference in New Issue
Block a user