diff --git a/lms/djangoapps/courseware/courses.py b/lms/djangoapps/courseware/courses.py new file mode 100644 index 0000000000..fefb0f9c1e --- /dev/null +++ b/lms/djangoapps/courseware/courses.py @@ -0,0 +1,78 @@ +from collections import namedtuple +import logging +import os + +from path import path +import yaml + +log = logging.getLogger('mitx.courseware.courses') + +_FIELDS = ['number', # 6.002x + 'title', # Circuits and Electronics + 'short_title', # Circuits + 'run_id', # Spring 2012 + 'path', # /some/absolute/filepath/6.002x --> course.xml is in here. + 'instructors', # ['Anant Agarwal'] + 'institution', # "MIT" + 'grader', # a courseware.graders.CourseGrader object + + #'start', # These should be datetime fields + #'end' + ] + +class CourseInfoLoadError(Exception): + pass + +class Course(namedtuple('Course', _FIELDS)): + """Course objects encapsulate general information about a given run of a + course. This includes things like name, grading policy, etc. + """ + @property + def id(self): + return "{0.institution},{0.number},{0.run_id}".format(self) + + @classmethod + def load_from_path(cls, course_path): + course_path = path(course_path) # convert it from string if necessary + try: + with open(course_path / "course_info.yaml") as course_info_file: + course_info = yaml.load(course_info_file) + summary = course_info['course'] + summary.update(path=course_path, grader=None) + return cls(**summary) + except Exception as ex: + log.exception(ex) + raise CourseInfoLoadError("Could not read course info: {0}:{1}" + .format(type(ex).__name__, ex)) + +def load_courses(courses_path): + """Given a directory of courses, returns a list of Course objects. For the + sake of backwards compatibility, if you point it at the top level of a + specific course, it will return a list with one Course object in it. + """ + courses_path = path(courses_path) + def _is_course_path(p): + return os.path.exists(p / "course_info.yaml") + + log.info("Loading courses from {0}".format(courses_path)) + + # Compatibility: courses_path is the path for a single course + if _is_course_path(courses_path): + log.warning("course_info.yaml found in top-level ({0})" + .format(courses_path) + + " -- assuming there is only a single course.") + return [Course.load_from_path(courses_path)] + + # Default: Each dir in courses_path is a separate course + courses = [] + log.info("Reading courses from {0}".format(courses_path)) + for course_dir_name in os.listdir(courses_path): + course_path = courses_path / course_dir_name + if _is_course_path(course_path): + log.info("Initializing course {0}".format(course_path)) + courses.append(Course.load_from_path(course_path)) + + return courses + +def create_lookup_table(courses): + return dict((c.id, c) for c in courses) diff --git a/lms/djangoapps/courseware/views.py b/lms/djangoapps/courseware/views.py index 5cbbe18d7d..34b3c9bb04 100644 --- a/lms/djangoapps/courseware/views.py +++ b/lms/djangoapps/courseware/views.py @@ -33,6 +33,14 @@ etree.set_default_parser(etree.XMLParser(dtd_validation=False, load_dtd=False, template_imports={'urllib':urllib} +@ensure_csrf_cookie +def courses(request): + csrf_token = csrf(request)['csrf_token'] + # TODO: Clean up how 'error' is done. + context = {'courses' : settings.COURSES, + 'csrf' : csrf_token} + return render_to_response("courses.html", context) + @cache_control(no_cache=True, no_store=True, must_revalidate=True) def gradebook(request): if 'course_admin' not in content_parser.user_groups(request.user): diff --git a/lms/djangoapps/student/views.py b/lms/djangoapps/student/views.py index af28654a59..dcfb7bbf82 100644 --- a/lms/djangoapps/student/views.py +++ b/lms/djangoapps/student/views.py @@ -471,8 +471,3 @@ def course_info(request): # TODO: Couse should be a model return render_to_response('course_info.html', {'csrf': csrf_token }) -@ensure_csrf_cookie -def courses(request): - csrf_token = csrf(request)['csrf_token'] - # TODO: Clean up how 'error' is done. - return render_to_response('courses.html', {'csrf': csrf_token }) diff --git a/lms/envs/common.py b/lms/envs/common.py index ec2e1f81de..7e985a691b 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -64,6 +64,11 @@ sys.path.append(PROJECT_ROOT / 'lib') sys.path.append(COMMON_ROOT / 'djangoapps') sys.path.append(COMMON_ROOT / 'lib') +######### EDX dormsbee/portal changes ################# +from courseware.courses import load_courses +COURSES = load_courses(ENV_ROOT / "data") +####################################################### + ################################## MITXWEB ##################################### # This is where we stick our compiled template files. Most of the app uses Mako # templates diff --git a/lms/templates/course.html b/lms/templates/course.html index 0b27e191ea..8841035186 100644 --- a/lms/templates/course.html +++ b/lms/templates/course.html @@ -1,6 +1,6 @@ <%namespace name='static' file='static_content.html'/> -%for i in xrange(6): +%for course in courses:
@@ -10,8 +10,8 @@
-

18th Century History

-

Adam Smith, Harvard University

+

${course.title}

+

${",".join(course.instructors)} — ${course.institution}

Register
diff --git a/lms/templates/courses.html b/lms/templates/courses.html index 5d381b74e3..c821db4737 100644 --- a/lms/templates/courses.html +++ b/lms/templates/courses.html @@ -11,7 +11,8 @@
- <%include file="course_filter.html" /> + ## I'm removing this for now since we aren't using it for the fall. + ## <%include file="course_filter.html" />
<%include file="course.html" />
diff --git a/lms/urls.py b/lms/urls.py index 6064e72d09..021b730c42 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -14,7 +14,6 @@ urlpatterns = ('', url(r'^$', 'student.views.index'), # Main marketing page, or redirect to courseware url(r'^dashboard$', 'student.views.dashboard'), url(r'^course_info$', 'student.views.course_info'), - url(r'^courses$', 'student.views.courses'), url(r'^change_email$', 'student.views.change_email_request'), url(r'^email_confirm/(?P[^/]*)$', 'student.views.confirm_email_change'), url(r'^change_name$', 'student.views.change_name_request'), @@ -74,6 +73,9 @@ if settings.COURSEWARE_ENABLED: url(r'^save_circuit/(?P[^/]*)$', 'circuit.views.save_circuit'), url(r'^calculate$', 'util.views.calculate'), url(r'^heartbeat$', include('heartbeat.urls')), + + # Multicourse related: + url(r'^courses$', 'courseware.views.courses'), ) if settings.ENABLE_MULTICOURSE: diff --git a/requirements.txt b/requirements.txt index 2618336630..092cfdc4ea 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,3 +28,4 @@ django_nose nosexcover rednose -e common/lib/xmodule +PyYAML