Commit 932a9be7 by Victor Shnayder Committed by Calen Pennington

Make tests pass again

* test enrolls in course before testing pages
* support github edit links with new file structure
* Some pep8 cleanups
parent 55edb1ef
...@@ -90,10 +90,12 @@ def add_histogram(get_html, module): ...@@ -90,10 +90,12 @@ def add_histogram(get_html, module):
# TODO (ichuang): Remove after fall 2012 LMS migration done # TODO (ichuang): Remove after fall 2012 LMS migration done
if settings.MITX_FEATURES.get('ENABLE_LMS_MIGRATION'): if settings.MITX_FEATURES.get('ENABLE_LMS_MIGRATION'):
[filepath, filename] = module.definition.get('filename','') [filepath, filename] = module.definition.get('filename', ['', None])
osfs = module.system.filestore osfs = module.system.filestore
if filename is not None and osfs.exists(filename): if filename is not None and osfs.exists(filename):
filepath = filename # if original, unmangled filename exists then use it (github doesn't like symlinks) # if original, unmangled filename exists then use it (github
# doesn't like symlinks)
filepath = filename
data_dir = osfs.root_path.rsplit('/')[-1] data_dir = osfs.root_path.rsplit('/')[-1]
edit_link = "https://github.com/MITx/%s/tree/master/%s" % (data_dir,filepath) edit_link = "https://github.com/MITx/%s/tree/master/%s" % (data_dir,filepath)
else: else:
......
...@@ -26,12 +26,14 @@ def strip_metadata(descriptor, key): ...@@ -26,12 +26,14 @@ def strip_metadata(descriptor, key):
for d in descriptor.get_children(): for d in descriptor.get_children():
strip_metadata(d, key) strip_metadata(d, key)
def check_gone(descriptor, key): def strip_filenames(descriptor):
'''Make sure that the metadata of this descriptor or any """
descendants does not include key''' Recursively strips 'filename' from all children's definitions.
assert_true(key not in descriptor.metadata) """
print "strip filename from {desc}".format(desc=descriptor.location.url())
descriptor.definition.pop('filename', None)
for d in descriptor.get_children(): for d in descriptor.get_children():
check_gone(d, key) strip_filenames(d)
...@@ -70,6 +72,11 @@ class RoundTripTestCase(unittest.TestCase): ...@@ -70,6 +72,11 @@ class RoundTripTestCase(unittest.TestCase):
strip_metadata(initial_course, 'data_dir') strip_metadata(initial_course, 'data_dir')
strip_metadata(exported_course, 'data_dir') strip_metadata(exported_course, 'data_dir')
# HACK: filenames change when changing file formats
# during imports from old-style courses. Ignore them.
strip_filenames(initial_course)
strip_filenames(exported_course)
self.assertEquals(initial_course, exported_course) self.assertEquals(initial_course, exported_course)
print "Checking key equality" print "Checking key equality"
......
...@@ -70,7 +70,7 @@ class XmlDescriptor(XModuleDescriptor): ...@@ -70,7 +70,7 @@ class XmlDescriptor(XModuleDescriptor):
# Related: What's the right behavior for clean_metadata? # Related: What's the right behavior for clean_metadata?
metadata_attributes = ('format', 'graceperiod', 'showanswer', 'rerandomize', metadata_attributes = ('format', 'graceperiod', 'showanswer', 'rerandomize',
'start', 'due', 'graded', 'display_name', 'url_name', 'hide_from_toc', 'start', 'due', 'graded', 'display_name', 'url_name', 'hide_from_toc',
'ispublic', # if True, then course is listed for all users; see 'ispublic', # if True, then course is listed for all users; see
# VS[compat] Remove once unused. # VS[compat] Remove once unused.
'name', 'slug') 'name', 'slug')
...@@ -171,8 +171,7 @@ class XmlDescriptor(XModuleDescriptor): ...@@ -171,8 +171,7 @@ class XmlDescriptor(XModuleDescriptor):
# again in the correct format. This should go away once the CMS is # again in the correct format. This should go away once the CMS is
# online and has imported all current (fall 2012) courses from xml # online and has imported all current (fall 2012) courses from xml
if not system.resources_fs.exists(filepath) and hasattr( if not system.resources_fs.exists(filepath) and hasattr(
cls, cls, 'backcompat_paths'):
'backcompat_paths'):
candidates = cls.backcompat_paths(filepath) candidates = cls.backcompat_paths(filepath)
for candidate in candidates: for candidate in candidates:
if system.resources_fs.exists(candidate): if system.resources_fs.exists(candidate):
...@@ -233,13 +232,19 @@ class XmlDescriptor(XModuleDescriptor): ...@@ -233,13 +232,19 @@ class XmlDescriptor(XModuleDescriptor):
# VS[compat] -- detect new-style each-in-a-file mode # VS[compat] -- detect new-style each-in-a-file mode
if is_pointer_tag(xml_object): if is_pointer_tag(xml_object):
# new style: # new style:
# read the actual defition file--named using url_name # read the actual definition file--named using url_name
filepath = cls._format_filepath(xml_object.tag, url_name) filepath = cls._format_filepath(xml_object.tag, url_name)
definition_xml = cls.load_file(filepath, system.resources_fs, location) definition_xml = cls.load_file(filepath, system.resources_fs, location)
else: else:
definition_xml = xml_object definition_xml = xml_object
definition = cls.load_definition(definition_xml, system, location) definition = cls.load_definition(definition_xml, system, location)
# VS[compat] -- make Ike's github preview links work in both old and
# new file layouts
if is_pointer_tag(xml_object):
# new style -- contents actually at filepath
definition['filename'] = [filepath, filepath]
metadata = cls.load_metadata(definition_xml) metadata = cls.load_metadata(definition_xml)
return cls( return cls(
system, system,
......
...@@ -46,12 +46,15 @@ def check_course(course_id, course_must_be_open=True, course_required=True): ...@@ -46,12 +46,15 @@ def check_course(course_id, course_must_be_open=True, course_required=True):
def course_image_url(course): def course_image_url(course):
return staticfiles_storage.url(course.metadata['data_dir'] + "/images/course_image.jpg") return staticfiles_storage.url(course.metadata['data_dir'] +
"/images/course_image.jpg")
def get_course_about_section(course, section_key): def get_course_about_section(course, section_key):
""" """
This returns the snippet of html to be rendered on the course about page, given the key for the section. This returns the snippet of html to be rendered on the course about page,
given the key for the section.
Valid keys: Valid keys:
- overview - overview
- title - title
...@@ -70,18 +73,23 @@ def get_course_about_section(course, section_key): ...@@ -70,18 +73,23 @@ def get_course_about_section(course, section_key):
- more_info - more_info
""" """
# Many of these are stored as html files instead of some semantic markup. This can change without effecting # Many of these are stored as html files instead of some semantic
# this interface when we find a good format for defining so many snippets of text/html. # markup. This can change without effecting this interface when we find a
# good format for defining so many snippets of text/html.
# TODO: Remove number, instructors from this list # TODO: Remove number, instructors from this list
if section_key in ['short_description', 'description', 'key_dates', 'video', 'course_staff_short', 'course_staff_extended', if section_key in ['short_description', 'description', 'key_dates', 'video',
'requirements', 'syllabus', 'textbook', 'faq', 'more_info', 'number', 'instructors', 'overview', 'course_staff_short', 'course_staff_extended',
'effort', 'end_date', 'prerequisites']: 'requirements', 'syllabus', 'textbook', 'faq', 'more_info',
'number', 'instructors', 'overview',
'effort', 'end_date', 'prerequisites']:
try: try:
with course.system.resources_fs.open(path("about") / section_key + ".html") as htmlFile: with course.system.resources_fs.open(path("about") / section_key + ".html") as htmlFile:
return replace_urls(htmlFile.read().decode('utf-8'), course.metadata['data_dir']) return replace_urls(htmlFile.read().decode('utf-8'),
course.metadata['data_dir'])
except ResourceNotFoundError: except ResourceNotFoundError:
log.warning("Missing about section {key} in course {url}".format(key=section_key, url=course.location.url())) log.warning("Missing about section {key} in course {url}".format(
key=section_key, url=course.location.url()))
return None return None
elif section_key == "title": elif section_key == "title":
return course.metadata.get('display_name', course.url_name) return course.metadata.get('display_name', course.url_name)
...@@ -95,7 +103,9 @@ def get_course_about_section(course, section_key): ...@@ -95,7 +103,9 @@ def get_course_about_section(course, section_key):
def get_course_info_section(course, section_key): def get_course_info_section(course, section_key):
""" """
This returns the snippet of html to be rendered on the course info page, given the key for the section. This returns the snippet of html to be rendered on the course info page,
given the key for the section.
Valid keys: Valid keys:
- handouts - handouts
- guest_handouts - guest_handouts
...@@ -103,43 +113,51 @@ def get_course_info_section(course, section_key): ...@@ -103,43 +113,51 @@ def get_course_info_section(course, section_key):
- guest_updates - guest_updates
""" """
# Many of these are stored as html files instead of some semantic markup. This can change without effecting # Many of these are stored as html files instead of some semantic
# this interface when we find a good format for defining so many snippets of text/html. # markup. This can change without effecting this interface when we find a
# good format for defining so many snippets of text/html.
if section_key in ['handouts', 'guest_handouts', 'updates', 'guest_updates']: if section_key in ['handouts', 'guest_handouts', 'updates', 'guest_updates']:
try: try:
with course.system.resources_fs.open(path("info") / section_key + ".html") as htmlFile: with course.system.resources_fs.open(path("info") / section_key + ".html") as htmlFile:
return replace_urls(htmlFile.read().decode('utf-8'), course.metadata['data_dir']) return replace_urls(htmlFile.read().decode('utf-8'),
course.metadata['data_dir'])
except ResourceNotFoundError: except ResourceNotFoundError:
log.exception("Missing info section {key} in course {url}".format(key=section_key, url=course.location.url())) log.exception("Missing info section {key} in course {url}".format(
key=section_key, url=course.location.url()))
return "! Info section missing !" return "! Info section missing !"
raise KeyError("Invalid about key " + str(section_key)) raise KeyError("Invalid about key " + str(section_key))
def course_staff_group_name(course): def course_staff_group_name(course):
''' '''
course should be either a CourseDescriptor instance, or a string (the .course entry of a Location) course should be either a CourseDescriptor instance, or a string (the
.course entry of a Location)
''' '''
if isinstance(course,str): if isinstance(course, str) or isinstance(course, unicode):
coursename = course coursename = course
else: else:
coursename = course.metadata.get('data_dir','UnknownCourseName') # should be a CourseDescriptor, so grab its location.course:
if not coursename: # Fall 2012: not all course.xml have metadata correct yet coursename = course.location.course
coursename = course.metadata.get('course','')
return 'staff_%s' % coursename return 'staff_%s' % coursename
def has_staff_access_to_course(user,course): def has_staff_access_to_course(user, course):
''' '''
Returns True if the given user has staff access to the course. Returns True if the given user has staff access to the course.
This means that user is in the staff_* group, or is an overall admin. This means that user is in the staff_* group, or is an overall admin.
course is the course field of the location being accessed.
''' '''
if user is None or (not user.is_authenticated()) or course is None: if user is None or (not user.is_authenticated()) or course is None:
return False return False
if user.is_staff: if user.is_staff:
return True return True
user_groups = [x[1] for x in user.groups.values_list()] # note this is the Auth group, not UserTestGroup
# note this is the Auth group, not UserTestGroup
user_groups = [x[1] for x in user.groups.values_list()]
staff_group = course_staff_group_name(course) staff_group = course_staff_group_name(course)
log.debug('course %s user %s groups %s' % (staff_group, user, user_groups)) log.debug('course %s, staff_group %s, user %s, groups %s' % (
course, staff_group, user, user_groups))
if staff_group in user_groups: if staff_group in user_groups:
return True return True
return False return False
...@@ -154,7 +172,8 @@ def get_courses_by_university(user): ...@@ -154,7 +172,8 @@ def get_courses_by_university(user):
Returns dict of lists of courses available, keyed by course.org (ie university). Returns dict of lists of courses available, keyed by course.org (ie university).
Courses are sorted by course.number. Courses are sorted by course.number.
if ACCESS_REQUIRE_STAFF_FOR_COURSE then list only includes those accessible to user. if ACCESS_REQUIRE_STAFF_FOR_COURSE then list only includes those accessible
to user.
''' '''
# TODO: Clean up how 'error' is done. # TODO: Clean up how 'error' is done.
# filter out any courses that errored. # filter out any courses that errored.
...@@ -168,4 +187,4 @@ def get_courses_by_university(user): ...@@ -168,4 +187,4 @@ def get_courses_by_university(user):
continue continue
universities[course.org].append(course) universities[course.org].append(course)
return universities return universities
...@@ -135,10 +135,25 @@ class ActivateLoginTestCase(TestCase): ...@@ -135,10 +135,25 @@ class ActivateLoginTestCase(TestCase):
class PageLoader(ActivateLoginTestCase): class PageLoader(ActivateLoginTestCase):
''' Base class that adds a function to load all pages in a modulestore ''' ''' Base class that adds a function to load all pages in a modulestore '''
def enroll(self, course):
resp = self.client.post('/change_enrollment', {
'enrollment_action': 'enroll',
'course_id': course.id,
})
data = parse_json(resp)
self.assertTrue(data['success'])
def check_pages_load(self, course_name, data_dir, modstore): def check_pages_load(self, course_name, data_dir, modstore):
print "Checking course {0} in {1}".format(course_name, data_dir) print "Checking course {0} in {1}".format(course_name, data_dir)
import_from_xml(modstore, data_dir, [course_name]) import_from_xml(modstore, data_dir, [course_name])
# enroll in the course before trying to access pages
courses = modstore.get_courses()
self.assertEqual(len(courses), 1)
course = courses[0]
self.enroll(course)
n = 0 n = 0
num_bad = 0 num_bad = 0
all_ok = True all_ok = True
......
...@@ -157,6 +157,9 @@ COURSE_SETTINGS = {'6.002x_Fall_2012': {'number' : '6.002x', ...@@ -157,6 +157,9 @@ COURSE_SETTINGS = {'6.002x_Fall_2012': {'number' : '6.002x',
} }
} }
# IP addresses that are allowed to reload the course, etc.
# TODO (vshnayder): Will probably need to change as we get real access control in.
LMS_MIGRATION_ALLOWED_IPS = []
############################### XModule Store ################################## ############################### XModule Store ##################################
MODULESTORE = { MODULESTORE = {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment