def _list_labels(handler): """Prepare a list of labels for use on the Assets page.""" output = safe_dom.NodeList() if not handler.app_context.is_editable_fs(): return output output.append( safe_dom.A('dashboard?action=add_label', className='gcb-button' ).add_text('Add Label') ).append( safe_dom.Element( 'div', style='clear: both; padding-top: 2px;' ) ) labels = models.LabelDAO.get_all() if labels: all_labels_ul = safe_dom.Element('ul') output.append(all_labels_ul) for label_type in sorted( models.LabelDTO.LABEL_TYPES, lambda a, b: cmp(a.menu_order, b.menu_order)): type_li = safe_dom.Element('li').add_child( safe_dom.Element('strong').add_text(label_type.title)) all_labels_ul.add_child(type_li) labels_of_type_ul = safe_dom.Element('ul') type_li.add_child(labels_of_type_ul) for label in sorted( labels, lambda a, b: cmp(a.title, b.title)): if label.type == label_type.type: li = safe_dom.Element('li') labels_of_type_ul.add_child(li) li.add_text( label.title ).add_attribute( title='id: %s, type: %s' % (label.id, label_type)) if label_type not in ( models.LabelDTO.SYSTEM_EDITABLE_LABEL_TYPES): li.add_child( dashboard_utils.create_edit_button( 'dashboard?action=edit_label&key=%s' % label.id, ).add_attribute( id='label_%s' % label.title)) else: output.append( safe_dom.Element( 'div', className='gcb-message').add_text('< none >')) return output
def _list_labels(handler): """Prepare a list of labels for use on the Assets page.""" output = safe_dom.NodeList() if not handler.app_context.is_editable_fs(): return output output.append( safe_dom.A('dashboard?action=add_label', className='gcb-button gcb-pull-right' ).add_text('Add Label') ).append( safe_dom.Element( 'div', style='clear: both; padding-top: 2px;' ) ) output.append( safe_dom.Element('h3').add_text('Labels') ) labels = models.LabelDAO.get_all() if labels: all_labels_ul = safe_dom.Element('ul') output.append(all_labels_ul) for label_type in sorted( models.LabelDTO.LABEL_TYPES, lambda a, b: cmp(a.menu_order, b.menu_order)): type_li = safe_dom.Element('li').add_text(label_type.title) all_labels_ul.add_child(type_li) labels_of_type_ul = safe_dom.Element('ul') type_li.add_child(labels_of_type_ul) for label in sorted( labels, lambda a, b: cmp(a.title, b.title)): if label.type == label_type.type: li = safe_dom.Element('li') labels_of_type_ul.add_child(li) li.add_text( label.title ).add_attribute( title='id: %s, type: %s' % (label.id, label_type)) if label_type not in ( models.LabelDTO.SYSTEM_EDITABLE_LABEL_TYPES): li.add_child( dashboard_utils.create_edit_button( 'dashboard?action=edit_label&key=%s' % label.id, ).add_attribute( id='label_%s' % label.title)) else: output.append(safe_dom.Element('blockquote').add_text('< none >')) return output
def _render_link_outline(handler, unit): actions = [] unit_data = { 'title': unit.title, 'class': 'link', 'href': unit.href or '', 'unit_id': unit.unit_id, 'actions': actions } actions.append(_render_status_icon(handler, unit, unit.unit_id, 'unit')) if handler.app_context.is_editable_fs(): url = handler.canonicalize_url( '/dashboard?%s') % urllib.urlencode({ 'action': 'edit_link', 'key': unit.unit_id}) actions.append(dashboard_utils.create_edit_button(url)) return unit_data
def _list_and_format_file_list( handler, title, subfolder, tab_name, links=False, upload=False, prefix=None, caption_if_empty='< none >', edit_url_template=None, merge_local_files=False, sub_title=None, all_paths=None): """Walks files in folders and renders their names in a section.""" # keep a list of files without merging unmerged_files = {} if merge_local_files: unmerged_files = dashboard_utils.list_files( handler, subfolder, merge_local_files=False, all_paths=all_paths) items = safe_dom.NodeList() count = 0 for filename in dashboard_utils.list_files( handler, subfolder, merge_local_files=merge_local_files, all_paths=all_paths): if prefix and not filename.startswith(prefix): continue # make a <li> item li = safe_dom.Element('li') if links: url = urllib.quote(filename) li.add_child(safe_dom.Element( 'a', href=url).add_text(filename)) else: li.add_text(filename) # add actions if available if (edit_url_template and handler.app_context.fs.impl.is_read_write()): li.add_child(safe_dom.Entity(' ')) edit_url = edit_url_template % ( tab_name, urllib.quote(filename), handler.request.get('action')) # show [overridden] + edit button if override exists if (filename in unmerged_files) or (not merge_local_files): li.add_text('[Overridden]').add_child( dashboard_utils.create_edit_button(edit_url)) # show an [override] link otherwise else: li.add_child(safe_dom.A(edit_url).add_text('[Override]')) count += 1 items.append(li) output = safe_dom.NodeList() if handler.app_context.is_editable_fs() and upload: output.append( safe_dom.Element( 'a', className='gcb-button gcb-icon-button', href='dashboard?%s' % urllib.urlencode( {'action': 'manage_asset', 'from_action': handler.request.get('action'), 'type': tab_name, 'key': subfolder}), id='upload-button', ).append( safe_dom.Element('span', className='icon md-file-upload') ).append( safe_dom.Element('span').add_text(" Upload") ) ) if sub_title: output.append(safe_dom.Element( 'div', className='gcb-message').add_text(sub_title)) if items: output.append(safe_dom.Element('ol').add_children(items)) else: if caption_if_empty: output.append( safe_dom.Element( 'div', className='gcb-message').add_text(caption_if_empty)) return output
def _list_question_groups( handler, all_questions, all_question_groups, locations_map): """Prepare a list of question groups.""" if not handler.app_context.is_editable_fs(): return safe_dom.NodeList() output = safe_dom.NodeList() output.append(safe_dom.Element('h3').add_text( 'Question Groups (%s)' % len(all_question_groups) )) output.append( safe_dom.Element( 'div', className='gcb-button-toolbar' ).append( safe_dom.Element( 'a', className='gcb-button', href='dashboard?action=add_question_group' ).add_text('Add Question Group') ) ).append( safe_dom.Element( 'div', style='clear: both; padding-top: 2px;' ) ) # Create question groups table table = _add_assets_table( output, 'question-group-table', [ ('Description', 25), ('Questions', 25), ('Course Locations', 25), ('Last Modified', 25)] ) tbody = safe_dom.Element('tbody') table.add_child(tbody) if not all_question_groups: table.add_child(_create_empty_footer( 'No question groups available', 4)) quid_to_question = {long(qu.id): qu for qu in all_questions} for question_group in all_question_groups: tr = safe_dom.Element('tr', data_qgid=str(question_group.id)) # Add description including action icons td = safe_dom.Element('td', className='description') tr.add_child(td) td.add_child(dashboard_utils.create_edit_button( 'dashboard?action=edit_question_group&key=%s' % ( question_group.id))) td.add_text(question_group.description) # Add questions tr.add_child(_create_list_cell([ safe_dom.Text(descr) for descr in sorted([ quid_to_question[long(quid)].description for quid in question_group.question_ids]) ]).add_attribute(className='questions')) # Add locations tr.add_child(_create_locations_cell( locations_map.get(question_group.id, {}))) # Add last modified timestamp tr.add_child(safe_dom.Element( 'td', data_timestamp=str(question_group.last_modified), className='timestamp' )) tbody.add_child(tr) return output
def _list_questions(handler, all_questions, all_question_groups, location_maps): """Prepare a list of the question bank contents.""" if not handler.app_context.is_editable_fs(): return safe_dom.NodeList() toolbar_template = handler.get_template( 'question_toolbar.html', [TEMPLATE_DIR]) toolbar_node = safe_dom.Template(toolbar_template, question_count=len(all_questions)) output = safe_dom.NodeList().append(toolbar_node) # Create questions table table = _add_assets_table( output, 'question-table', [ ('Description', 25), ('Question Groups', 25), ('Course Locations', 25), ('Last Modified', 16), ('Type', 9)] ) _attach_filter_data(handler, table) token = crypto.XsrfTokenManager.create_xsrf_token('clone_question') table.add_attribute(data_clone_question_token=token) token = crypto.XsrfTokenManager.create_xsrf_token('add_to_question_group') table.add_attribute(data_qg_xsrf_token=token) tbody = safe_dom.Element('tbody') table.add_child(tbody) table.add_child(_create_empty_footer( 'No questions available', 5, all_questions)) question_to_group = {} for group in all_question_groups: for quid in group.question_ids: question_to_group.setdefault(long(quid), []).append(group) for question in all_questions: tr = safe_dom.Element('tr', data_quid=str(question.id)) # Add description including action icons td = safe_dom.Element('td', className='description') tr.add_child(td) td.add_child(dashboard_utils.create_edit_button( 'dashboard?action=edit_question&key=%s' % question.id)) td.add_child(_create_preview_button()) td.add_child(_create_clone_button(question.id)) td.add_text(question.description) # Add containing question groups used_by_groups = question_to_group.get(question.id, []) cell = safe_dom.Element('td', className='groups') if all_question_groups: cell.add_child(_create_add_to_group_button()) cell.add_child(_create_list( [safe_dom.Text(group.description) for group in sorted( used_by_groups, key=lambda g: g.description)] )) tr.add_child(cell) # Add locations locations = _get_question_locations( question.id, location_maps, used_by_groups) tr.add_child(_create_locations_cell(locations)) # Add last modified timestamp tr.add_child(safe_dom.Element( 'td', data_timestamp=str(question.last_modified), className='timestamp' )) # Add question type tr.add_child(safe_dom.Element('td').add_text( 'MC' if question.type == models.QuestionDTO.MULTIPLE_CHOICE else ( 'SA' if question.type == models.QuestionDTO.SHORT_ANSWER else ( 'Unknown Type')) ).add_attribute(style='text-align: center')) # Add filter information filter_info = {} filter_info['description'] = question.description filter_info['type'] = question.type filter_info['lessons'] = [] unit_ids = set() for (lesson, unit) in locations.get('lessons', ()): unit_ids.add(unit.unit_id) filter_info['lessons'].append(lesson.lesson_id) filter_info['units'] = list(unit_ids) + [ a.unit_id for a in locations.get('assessments', ())] filter_info['groups'] = [qg.id for qg in used_by_groups] filter_info['unused'] = int(not (locations and any(locations.values()))) tr.add_attribute(data_filter=transforms.dumps(filter_info)) tbody.add_child(tr) return output
def _render_unit_outline(handler, course, unit): is_editable = handler.app_context.is_editable_fs() actions = [] unit_data = { 'title': unit.title, 'class': 'unit', 'href': 'unit?unit=%s' % unit.unit_id, 'unit_id': unit.unit_id, 'actions': actions } actions.append(_render_status_icon(handler, unit, unit.unit_id, 'unit')) if is_editable: url = handler.canonicalize_url( '/dashboard?%s') % urllib.urlencode({ 'action': 'edit_unit', 'key': unit.unit_id}) actions.append(dashboard_utils.create_edit_button(url)) if unit.pre_assessment: assessment = course.find_unit_by_id(unit.pre_assessment) if assessment: assessment_outline = _render_assessment_outline(handler, assessment) assessment_outline['class'] = 'pre-assessment' unit_data['pre_assessment'] = assessment_outline lessons = [] for lesson in course.get_lessons(unit.unit_id): actions = [] actions.append( _render_status_icon(handler, lesson, lesson.lesson_id, 'lesson')) if is_editable: url = handler.get_action_url( 'edit_lesson', key=lesson.lesson_id) actions.append(dashboard_utils.create_edit_button(url)) extras = [] for annotator in COURSE_OUTLINE_EXTRA_INFO_ANNOTATORS: extra_info = annotator(course, lesson) if extra_info: extras.append(extra_info) lessons.append({ 'title': lesson.title, 'class': 'lesson', 'href': 'unit?unit=%s&lesson=%s' % ( unit.unit_id, lesson.lesson_id), 'lesson_id': lesson.lesson_id, 'actions': actions, 'auto_index': lesson.auto_index, 'extras': extras}) unit_data['lessons'] = lessons if unit.post_assessment: assessment = course.find_unit_by_id(unit.post_assessment) if assessment: assessment_outline = _render_assessment_outline(handler, assessment) assessment_outline['class'] = 'post-assessment' unit_data['post_assessment'] = assessment_outline return unit_data
def _render_unit_outline(handler, course, unit): is_editable = handler.app_context.is_editable_fs() actions = [] unit_data = { 'title': unit.title, 'class': 'unit', 'href': 'unit?unit=%s' % unit.unit_id, 'unit_id': unit.unit_id, 'actions': actions } actions.append(_render_status_icon(handler, unit, unit.unit_id, 'unit')) if is_editable: url = handler.canonicalize_url('/dashboard?%s') % urllib.urlencode( { 'action': 'edit_unit', 'key': unit.unit_id }) actions.append(dashboard_utils.create_edit_button(url)) if unit.pre_assessment: assessment = course.find_unit_by_id(unit.pre_assessment) if assessment: assessment_outline = _render_assessment_outline( handler, assessment) assessment_outline['class'] = 'pre-assessment' unit_data['pre_assessment'] = assessment_outline lessons = [] for lesson in course.get_lessons(unit.unit_id): actions = [] actions.append( _render_status_icon(handler, lesson, lesson.lesson_id, 'lesson')) if is_editable: url = handler.get_action_url('edit_lesson', key=lesson.lesson_id) actions.append(dashboard_utils.create_edit_button(url)) extras = [] for annotator in COURSE_OUTLINE_EXTRA_INFO_ANNOTATORS: extra_info = annotator(course, lesson) if extra_info: extras.append(extra_info) lessons.append({ 'title': lesson.title, 'class': 'lesson', 'href': 'unit?unit=%s&lesson=%s' % (unit.unit_id, lesson.lesson_id), 'lesson_id': lesson.lesson_id, 'actions': actions, 'auto_index': lesson.auto_index, 'extras': extras }) unit_data['lessons'] = lessons if unit.post_assessment: assessment = course.find_unit_by_id(unit.post_assessment) if assessment: assessment_outline = _render_assessment_outline( handler, assessment) assessment_outline['class'] = 'post-assessment' unit_data['post_assessment'] = assessment_outline return unit_data
def _list_question_groups( handler, all_questions, all_question_groups, locations_map): """Prepare a list of question groups.""" if not handler.app_context.is_editable_fs(): return safe_dom.NodeList() output = safe_dom.NodeList() output.append( safe_dom.Element( 'a', className='gcb-button gcb-pull-right', href='dashboard?action=add_question_group' ).add_text('Add Question Group') ).append( safe_dom.Element( 'div', style='clear: both; padding-top: 2px;' ) ) output.append(safe_dom.Element('h3').add_text( 'Question Groups (%s)' % len(all_question_groups) )) # Create question groups table table = _add_assets_table( output, 'question-group-table', [ ('Description', 25), ('Questions', 25), ('Course Locations', 25), ('Last Modified', 25)] ) tbody = safe_dom.Element('tbody') table.add_child(tbody) if not all_question_groups: table.add_child(_create_empty_footer( 'No question groups available', 4)) quid_to_question = {long(qu.id): qu for qu in all_questions} for question_group in all_question_groups: tr = safe_dom.Element('tr', data_qgid=str(question_group.id)) # Add description including action icons td = safe_dom.Element('td', className='description') tr.add_child(td) td.add_child(dashboard_utils.create_edit_button( 'dashboard?action=edit_question_group&key=%s' % ( question_group.id))) td.add_text(question_group.description) # Add questions tr.add_child(_create_list_cell([ safe_dom.Text(descr) for descr in sorted([ quid_to_question[long(quid)].description for quid in question_group.question_ids]) ]).add_attribute(className='questions')) # Add locations tr.add_child(_create_locations_cell( locations_map.get(question_group.id, {}))) # Add last modified timestamp tr.add_child(safe_dom.Element( 'td', data_timestamp=str(question_group.last_modified), className='timestamp' )) tbody.add_child(tr) return output
def _list_and_format_file_list( handler, title, subfolder, tab_name, links=False, upload=False, prefix=None, caption_if_empty='< none >', edit_url_template=None, merge_local_files=False, sub_title=None, all_paths=None): """Walks files in folders and renders their names in a section.""" # keep a list of files without merging unmerged_files = {} if merge_local_files: unmerged_files = dashboard_utils.list_files( handler, subfolder, merge_local_files=False, all_paths=all_paths) items = safe_dom.NodeList() count = 0 for filename in dashboard_utils.list_files( handler, subfolder, merge_local_files=merge_local_files, all_paths=all_paths): if prefix and not filename.startswith(prefix): continue # make a <li> item li = safe_dom.Element('li') if links: url = urllib.quote(filename) li.add_child(safe_dom.Element( 'a', href=url).add_text(filename)) else: li.add_text(filename) # add actions if available if (edit_url_template and handler.app_context.fs.impl.is_read_write()): li.add_child(safe_dom.Entity(' ')) edit_url = edit_url_template % ( tab_name, urllib.quote(filename)) # show [overridden] + edit button if override exists if (filename in unmerged_files) or (not merge_local_files): li.add_text('[Overridden]').add_child( dashboard_utils.create_edit_button(edit_url)) # show an [override] link otherwise else: li.add_child(safe_dom.A(edit_url).add_text('[Override]')) count += 1 items.append(li) output = safe_dom.NodeList() if handler.app_context.is_editable_fs() and upload: output.append( safe_dom.Element( 'a', className='gcb-button gcb-pull-right', href='dashboard?%s' % urllib.urlencode( {'action': 'manage_asset', 'tab': tab_name, 'key': subfolder}) ).add_text( 'Upload to ' + subfolder.lstrip('/').rstrip('/')) ).append( safe_dom.Element( 'div', style='clear: both; padding-top: 2px;' ) ) if title: h3 = safe_dom.Element('h3') if count: h3.add_text('%s (%s)' % (title, count)) else: h3.add_text(title) output.append(h3) if sub_title: output.append(safe_dom.Element('blockquote').add_text(sub_title)) if items: output.append(safe_dom.Element('ol').add_children(items)) else: if caption_if_empty: output.append( safe_dom.Element('blockquote').add_text(caption_if_empty)) return output
def _list_questions(handler, all_questions, all_question_groups, location_maps): """Prepare a list of the question bank contents.""" if not handler.app_context.is_editable_fs(): return safe_dom.NodeList() output = safe_dom.NodeList().append( safe_dom.Element( 'a', className='gcb-button gcb-pull-right', href='dashboard?action=add_mc_question' ).add_text('Add Multiple Choice') ).append( safe_dom.Element( 'a', className='gcb-button gcb-pull-right', href='dashboard?action=add_sa_question' ).add_text('Add Short Answer') ).append( safe_dom.Element( 'a', className='gcb-button gcb-pull-right', href='dashboard?action=import_gift_questions' ).add_text('Import GIFT Questions') ).append(_create_filter()).append( safe_dom.Element('div', style='clear: both; padding-top: 2px;') ).append(safe_dom.Element('h3').add_text( 'Questions (%s)' % len(all_questions) )) # Create questions table table = _add_assets_table( output, 'question-table', [ ('Description', 25), ('Question Groups', 25), ('Course Locations', 25), ('Last Modified', 16), ('Type', 9)] ) _attach_filter_data(handler, table) token = crypto.XsrfTokenManager.create_xsrf_token('clone_question') table.add_attribute(data_clone_question_token=token) token = crypto.XsrfTokenManager.create_xsrf_token('add_to_question_group') table.add_attribute(data_qg_xsrf_token=token) tbody = safe_dom.Element('tbody') table.add_child(tbody) table.add_child(_create_empty_footer( 'No questions available', 5, all_questions)) question_to_group = {} for group in all_question_groups: for quid in group.question_ids: question_to_group.setdefault(long(quid), []).append(group) for question in all_questions: tr = safe_dom.Element('tr', data_quid=str(question.id)) # Add description including action icons td = safe_dom.Element('td', className='description') tr.add_child(td) td.add_child(dashboard_utils.create_edit_button( 'dashboard?action=edit_question&key=%s' % question.id)) td.add_child(_create_preview_button()) td.add_child(_create_clone_button(question.id)) td.add_text(question.description) # Add containing question groups used_by_groups = question_to_group.get(question.id, []) cell = safe_dom.Element('td', className='groups') if all_question_groups: cell.add_child(_create_add_to_group_button()) cell.add_child(_create_list( [safe_dom.Text(group.description) for group in sorted( used_by_groups, key=lambda g: g.description)] )) tr.add_child(cell) # Add locations locations = _get_question_locations( question.id, location_maps, used_by_groups) tr.add_child(_create_locations_cell(locations)) # Add last modified timestamp tr.add_child(safe_dom.Element( 'td', data_timestamp=str(question.last_modified), className='timestamp' )) # Add question type tr.add_child(safe_dom.Element('td').add_text( 'MC' if question.type == models.QuestionDTO.MULTIPLE_CHOICE else ( 'SA' if question.type == models.QuestionDTO.SHORT_ANSWER else ( 'Unknown Type')) ).add_attribute(style='text-align: center')) # Add filter information filter_info = {} filter_info['description'] = question.description filter_info['type'] = question.type filter_info['lessons'] = [] unit_ids = set() for (lesson, unit) in locations.get('lessons', ()): unit_ids.add(unit.unit_id) filter_info['lessons'].append(lesson.lesson_id) filter_info['units'] = list(unit_ids) + [ a.unit_id for a in locations.get('assessments', ())] filter_info['groups'] = [qg.id for qg in used_by_groups] filter_info['unused'] = 0 if locations else 1 tr.add_attribute(data_filter=transforms.dumps(filter_info)) tbody.add_child(tr) return output