Skip to content
Snippets Groups Projects
Commit ef6630d7 authored by Calen Pennington's avatar Calen Pennington
Browse files

Merge pull request #120 from MITx/cpennington/cms

CMS Beginnings
parents 51cd7566 605b1ae0
No related branches found
No related tags found
No related merge requests found
Showing
with 1707 additions and 0 deletions
......@@ -20,3 +20,4 @@ log/
reports/
/src/
\#*\#
*.egg-info
File moved
###
### One-off script for importing courseware form XML format
###
#import mitxmako.middleware
#from courseware import content_parser
#from django.contrib.auth.models import User
import os.path
from StringIO import StringIO
from mako.template import Template
from mako.lookup import TemplateLookup
from django.core.management.base import BaseCommand
from keystore.django import keystore
from lxml import etree
class Command(BaseCommand):
help = \
''' Run FTP server.'''
def handle(self, *args, **options):
print args
data_dir = args[0]
parser = etree.XMLParser(remove_comments = True)
lookup = TemplateLookup(directories=[data_dir])
template = lookup.get_template("course.xml")
course_string = template.render(groups=[])
course = etree.parse(StringIO(course_string), parser=parser)
elements = list(course.iter())
tag_to_category = {
# Custom tags
'videodev': 'Custom',
'slides': 'Custom',
'book': 'Custom',
'image': 'Custom',
'discuss': 'Custom',
# Simple lists
'chapter': 'Week',
'course': 'Course',
'sequential': 'LectureSequence',
'vertical': 'ProblemSet',
'section': {
'Lab': 'Lab',
'Lecture Sequence': 'LectureSequence',
'Homework': 'Homework',
'Tutorial Index': 'TutorialIndex',
'Video': 'VideoSegment',
'Midterm': 'Exam',
'Final': 'Exam',
None: 'Section',
},
# True types
'video': 'VideoSegment',
'html': 'HTML',
'problem': 'Problem',
}
name_index = 0
for e in elements:
name = e.attrib.get('name', None)
for f in elements:
if f != e and f.attrib.get('name', None) == name:
name = None
if not name:
name = "{tag}_{index}".format(tag=e.tag, index=name_index)
name_index = name_index + 1
if e.tag in tag_to_category:
category = tag_to_category[e.tag]
if isinstance(category, dict):
category = category[e.get('format')]
category = category.replace('/', '-')
name = name.replace('/', '-')
e.set('url', 'i4x://mit.edu/6002xs12/{category}/{name}'.format(
category=category,
name=name))
def handle_skip(e):
print "Skipping ", e
results = {}
def handle_custom(e):
data = {'type':'i4x://mit.edu/6002xs12/tag/{tag}'.format(tag=e.tag),
'attrib':dict(e.attrib)}
results[e.attrib['url']] = {'data':data}
def handle_list(e):
if e.attrib.get("class", None) == "tutorials":
return
children = [le.attrib['url'] for le in e.getchildren()]
results[e.attrib['url']] = {'children':children}
def handle_video(e):
url = e.attrib['url']
clip_url = url.replace('VideoSegment', 'VideoClip')
# Take: 0.75:izygArpw-Qo,1.0:p2Q6BrNhdh8,1.25:1EeWXzPdhSA,1.50:rABDYkeK0x8
# Make: [(0.75, 'izygArpw-Qo'), (1.0, 'p2Q6BrNhdh8'), (1.25, '1EeWXzPdhSA'), (1.5, 'rABDYkeK0x8')]
youtube_str = e.attrib['youtube']
youtube_list = [(float(x), y) for x,y in map(lambda x:x.split(':'), youtube_str.split(','))]
clip_infos = [{ "status": "ready",
"format": "youtube",
"sane": True,
"location": "youtube",
"speed": speed,
"id": youtube_id,
"size": None} \
for (speed, youtube_id) \
in youtube_list]
results[clip_url] = {'data':{'clip_infos':clip_infos}}
results[url] = {'children' : [{'url':clip_url}]}
def handle_html(e):
if 'src' in e.attrib:
text = open(data_dir+'html/'+e.attrib['src']).read()
else:
textlist=[e.text]+[etree.tostring(elem) for elem in e]+[e.tail]
textlist=[i for i in textlist if type(i)==str]
text = "".join(textlist)
results[e.attrib['url']] = {'data':{'text':text}}
def handle_problem(e):
data = open(os.path.join(data_dir, 'problems', e.attrib['filename']+'.xml')).read()
results[e.attrib['url']] = {'data':{'statement':data}}
element_actions = {# Inside HTML ==> Skip these
'a': handle_skip,
'h1': handle_skip,
'h2': handle_skip,
'hr': handle_skip,
'strong': handle_skip,
'ul': handle_skip,
'li': handle_skip,
'p': handle_skip,
# Custom tags
'videodev': handle_custom,
'slides': handle_custom,
'book': handle_custom,
'image': handle_custom,
'discuss': handle_custom,
# Simple lists
'chapter': handle_list,
'course': handle_list,
'sequential': handle_list,
'vertical': handle_list,
'section': handle_list,
# True types
'video': handle_video,
'html': handle_html,
'problem': handle_problem,
}
for e in elements:
element_actions[e.tag](e)
for k in results:
keystore().create_item(k, 'Piotr Mitros')
if 'data' in results[k]:
keystore().update_item(k, results[k]['data'])
if 'children' in results[k]:
keystore().update_children(k, results[k]['children'])
from mitxmako.shortcuts import render_to_response
from keystore.django import keystore
from django.contrib.auth.decorators import login_required
def index(request):
# FIXME (cpennington): These need to be read in from the active user
org = 'mit.edu'
course = '6002xs12'
course = keystore().get_item(['i4x', org, course, 'Course', None])
weeks = course.get_children()
return render_to_response('index.html', {'weeks': weeks})
"""
This is the common settings file, intended to set sane defaults. If you have a
piece of configuration that's dependent on a set of feature flags being set,
then create a function that returns the calculated value based on the value of
MITX_FEATURES[...]. Modules that extend this one can change the feature
configuration in an environment specific config file and re-calculate those
values.
We should make a method that calls all these config methods so that you just
make one call at the end of your site-specific dev file to reset all the
dependent variables (like INSTALLED_APPS) for you.
Longer TODO:
1. Right now our treatment of static content in general and in particular
course-specific static content is haphazard.
2. We should have a more disciplined approach to feature flagging, even if it
just means that we stick them in a dict called MITX_FEATURES.
3. We need to handle configuration for multiple courses. This could be as
multiple sites, but we do need a way to map their data assets.
"""
import sys
import tempfile
from path import path
############################# SET PATH INFORMATION #############################
PROJECT_ROOT = path(__file__).abspath().dirname().dirname() # /mitx/cms
COMMON_ROOT = PROJECT_ROOT.dirname() / "common"
ENV_ROOT = PROJECT_ROOT.dirname().dirname() # virtualenv dir /mitx is in
ASKBOT_ROOT = ENV_ROOT / "askbot-devel"
COURSES_ROOT = ENV_ROOT / "data"
# FIXME: To support multiple courses, we should walk the courses dir at startup
DATA_DIR = COURSES_ROOT
sys.path.append(ENV_ROOT)
sys.path.append(ASKBOT_ROOT)
sys.path.append(ASKBOT_ROOT / "askbot" / "deps")
sys.path.append(PROJECT_ROOT / 'djangoapps')
sys.path.append(PROJECT_ROOT / 'lib')
sys.path.append(COMMON_ROOT / 'djangoapps')
sys.path.append(COMMON_ROOT / 'lib')
############################# WEB CONFIGURATION #############################
# This is where we stick our compiled template files.
MAKO_MODULE_DIR = tempfile.mkdtemp('mako')
MAKO_TEMPLATES = {}
MAKO_TEMPLATES['main'] = [PROJECT_ROOT / 'templates']
MITX_ROOT_URL = ''
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.request',
'django.core.context_processors.static',
'django.contrib.messages.context_processors.messages',
'django.core.context_processors.auth', # this is required for admin
'django.core.context_processors.csrf', # necessary for csrf protection
)
################################# Middleware ###################################
# List of finder classes that know how to find static files in
# various locations.
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
)
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
)
MIDDLEWARE_CLASSES = (
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
# Instead of AuthenticationMiddleware, we use a cached backed version
'cache_toolbox.middleware.CacheBackedAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'track.middleware.TrackMiddleware',
'mitxmako.middleware.MakoMiddleware',
'django.middleware.transaction.TransactionMiddleware',
)
############################ SIGNAL HANDLERS ################################
import monitoring.exceptions # noqa
############################ DJANGO_BUILTINS ################################
# Change DEBUG/TEMPLATE_DEBUG in your environment settings files, not here
DEBUG = False
TEMPLATE_DEBUG = False
# Site info
SITE_ID = 1
SITE_NAME = "localhost:8000"
HTTPS = 'on'
ROOT_URLCONF = 'mitx.cms.urls'
IGNORABLE_404_ENDS = ('favicon.ico')
# Email
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
DEFAULT_FROM_EMAIL = 'registration@mitx.mit.edu'
DEFAULT_FEEDBACK_EMAIL = 'feedback@mitx.mit.edu'
ADMINS = (
('MITx Admins', 'admin@mitx.mit.edu'),
)
MANAGERS = ADMINS
# Static content
STATIC_URL = '/static/'
ADMIN_MEDIA_PREFIX = '/static/admin/'
STATIC_ROOT = ENV_ROOT / "staticfiles"
# FIXME: We should iterate through the courses we have, adding the static
# contents for each of them. (Right now we just use symlinks.)
STATICFILES_DIRS = [
PROJECT_ROOT / "static",
# This is how you would use the textbook images locally
# ("book", ENV_ROOT / "book_images")
]
# Locale/Internationalization
TIME_ZONE = 'America/New_York' # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
LANGUAGE_CODE = 'en' # http://www.i18nguy.com/unicode/language-identifiers.html
USE_I18N = True
USE_L10N = True
# Messages
MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
############################ APPS #####################################
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'contentstore',
'instructor',
)
"""
This config file runs the simplest dev environment"""
from .common import *
DEBUG = True
TEMPLATE_DEBUG = DEBUG
KEYSTORE = {
'host': 'localhost',
'db': 'mongo_base',
'collection': 'key_store',
}
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ENV_ROOT / "db" / "mitx.db",
}
}
CACHES = {
# This is the cache used for most things. Askbot will not work without a
# functioning cache -- it relies on caching to load its settings in places.
# In staging/prod envs, the sessions also live here.
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'mitx_loc_mem_cache',
'KEY_FUNCTION': 'util.memcache.safe_key',
},
# The general cache is what you get if you use our util.cache. It's used for
# things like caching the course.xml file for different A/B test groups.
# We set it to be a DummyCache to force reloading of course.xml in dev.
# In staging environments, we would grab VERSION from data uploaded by the
# push process.
'general': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
'KEY_PREFIX': 'general',
'VERSION': 4,
'KEY_FUNCTION': 'util.memcache.safe_key',
}
}
"""
This config file runs the simplest dev environment using sqlite, and db-based
sessions. Assumes structure:
/envroot/
/db # This is where it'll write the database file
/mitx # The location of this repo
/log # Where we're going to write log files
"""
from .common import *
import os
# Nose Test Runner
INSTALLED_APPS += ('django_nose',)
NOSE_ARGS = ['--cover-erase', '--with-xunit', '--with-xcoverage', '--cover-html', '--cover-inclusive']
for app in os.listdir(PROJECT_ROOT / 'djangoapps'):
NOSE_ARGS += ['--cover-package', app]
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
KEYSTORE = {
'host': 'localhost',
'db': 'mongo_base',
'collection': 'key_store',
}
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ENV_ROOT / "db" / "mitx.db",
}
}
CACHES = {
# This is the cache used for most things. Askbot will not work without a
# functioning cache -- it relies on caching to load its settings in places.
# In staging/prod envs, the sessions also live here.
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'mitx_loc_mem_cache',
'KEY_FUNCTION': 'util.memcache.safe_key',
},
# The general cache is what you get if you use our util.cache. It's used for
# things like caching the course.xml file for different A/B test groups.
# We set it to be a DummyCache to force reloading of course.xml in dev.
# In staging environments, we would grab VERSION from data uploaded by the
# push process.
'general': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
'KEY_PREFIX': 'general',
'VERSION': 4,
'KEY_FUNCTION': 'util.memcache.safe_key',
}
}
#!/usr/bin/env python
from django.core.management import execute_manager
import imp
try:
imp.find_module('settings') # Assumed to be in the same directory.
except ImportError:
import sys
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__)
sys.exit(1)
import settings
if __name__ == "__main__":
execute_manager(settings)
This diff is collapsed.
/* line 12, ../sass/_reset.scss */
html, body, div, span, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
abbr, address, cite, code,
del, dfn, em, img, ins, kbd, q, samp,
small, strong, sub, sup, var,
b, i,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, figcaption, figure,
footer, header, hgroup, menu, nav, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
outline: 0;
vertical-align: baseline;
background: transparent; }
/* line 21, ../sass/_reset.scss */
html, body {
font-size: 100%; }
/* line 26, ../sass/_reset.scss */
article, aside, details, figcaption, figure, footer, header, hgroup, nav, section {
display: block; }
/* line 31, ../sass/_reset.scss */
audio, canvas, video {
display: inline-block; }
/* line 36, ../sass/_reset.scss */
audio:not([controls]) {
display: none; }
/* line 41, ../sass/_reset.scss */
[hidden] {
display: none; }
/* line 47, ../sass/_reset.scss */
html {
font-size: 100%;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%; }
/* line 54, ../sass/_reset.scss */
html, button, input, select, textarea {
font-family: sans-serif; }
/* line 60, ../sass/_reset.scss */
a:focus {
outline: thin dotted;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px; }
/* line 69, ../sass/_reset.scss */
a:hover, a:active {
outline: 0; }
/* line 75, ../sass/_reset.scss */
abbr[title] {
border-bottom: 1px dotted; }
/* line 80, ../sass/_reset.scss */
b, strong {
font-weight: bold; }
/* line 84, ../sass/_reset.scss */
blockquote {
margin: 1em 40px; }
/* line 89, ../sass/_reset.scss */
dfn {
font-style: italic; }
/* line 94, ../sass/_reset.scss */
mark {
background: #ff0;
color: #000; }
/* line 101, ../sass/_reset.scss */
pre, code, kbd, samp {
font-family: monospace, serif;
_font-family: 'courier new', monospace;
font-size: 1em; }
/* line 108, ../sass/_reset.scss */
pre {
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word; }
/* line 115, ../sass/_reset.scss */
blockquote, q {
quotes: none; }
/* line 117, ../sass/_reset.scss */
blockquote:before, blockquote:after, q:before, q:after {
content: '';
content: none; }
/* line 123, ../sass/_reset.scss */
small {
font-size: 75%; }
/* line 127, ../sass/_reset.scss */
sub, sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline; }
/* line 134, ../sass/_reset.scss */
sup {
top: -0.5em; }
/* line 138, ../sass/_reset.scss */
sub {
bottom: -0.25em; }
/* line 143, ../sass/_reset.scss */
nav ul, nav ol {
list-style: none;
list-style-image: none; }
/* line 150, ../sass/_reset.scss */
img {
border: 0;
height: auto;
max-width: 100%;
-ms-interpolation-mode: bicubic; }
/* line 158, ../sass/_reset.scss */
svg:not(:root) {
overflow: hidden; }
/* line 163, ../sass/_reset.scss */
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em; }
/* line 169, ../sass/_reset.scss */
legend {
border: 0;
padding: 0;
white-space: normal; }
/* line 175, ../sass/_reset.scss */
button, input, select, textarea {
font-size: 100%;
margin: 0;
vertical-align: baseline; }
/* line 182, ../sass/_reset.scss */
button, input {
line-height: normal; }
/* line 186, ../sass/_reset.scss */
button, input[type="button"], input[type="reset"], input[type="submit"] {
cursor: pointer;
-webkit-appearance: button; }
/* line 192, ../sass/_reset.scss */
button[disabled], input[disabled] {
cursor: default; }
/* line 196, ../sass/_reset.scss */
input[type="checkbox"], input[type="radio"] {
box-sizing: border-box;
padding: 0; }
/* line 201, ../sass/_reset.scss */
input[type="search"] {
-webkit-appearance: textfield;
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box;
box-sizing: content-box; }
/* line 209, ../sass/_reset.scss */
input[type="search"]::-webkit-search-decoration, input[type="search"]::-webkit-search-cancel-button {
-webkit-appearance: none; }
/* line 215, ../sass/_reset.scss */
button::-moz-focus-inner, input::-moz-focus-inner {
border: 0;
padding: 0; }
/* line 220, ../sass/_reset.scss */
textarea {
overflow: auto;
vertical-align: top; }
/* line 226, ../sass/_reset.scss */
table {
border-collapse: collapse;
border-spacing: 0; }
cms/static/img/drag-handle.png

98 B

cms/static/img/indicator.gif

1.52 KiB

cms/static/img/video.jpg

7.05 KiB

0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment