def parse_chapter(docname, doc, parent): for config_file in [ e.yaml_write for e in doc.traverse(aplus_nodes.html) if e.has_yaml(u'exercise') ]: config = yaml_writer.read(config_file) if config.get(u'_external', False): exercise = config.copy() del exercise[u'_external'] else: exercise = { u'key': config[u'key'], u'config': config[u'key'] + u'.yaml', u'max_submissions': config.get(u'max_submissions', 0), u'max_points': config.get(u'max_points', 0), u'difficulty': config.get(u'difficulty', ''), u'points_to_pass': config.get(u'points_to_pass', 0), u'category': config[u'category'], u'min_group_size': config.get(u'min_group_size', 1), u'max_group_size': config.get(u'max_group_size', 1), u'confirm_the_level': config.get(u'confirm_the_level', False), } exercise.update({ u'allow_assistant_grading': False, u'status': u'unlisted', }) if u'scale_points' in config: exercise[u'max_points'] = config.pop(u'scale_points') parent.append(exercise) if not config[u'category'] in category_keys: category_keys.append(config[u'category']) category = u'chapter' for name, hidden, child in traverse_tocs(doc): meta = first_meta(child) status = u'hidden' if 'hidden' in meta else ( u'unlisted' if hidden else u'ready') chapter = { u'key': name.split(u'/')[-1], #name.replace('/', '_'), u'status': status, u'name': first_title(child), u'static_content': name + u'.html', u'category': category, u'use_wide_column': app.config.use_wide_column, u'children': [], } if meta: audience = meta.get('audience') if audience: chapter[u'audience'] = yaml_writer.ensure_unicode(audience) if category in override: chapter.update(override[category]) parent.append(chapter) if not u'chapter' in category_keys: category_keys.append(u'chapter') parse_chapter(name, child, chapter[u'children'])
def run(self): env = self.state.document.settings.env if not 'config' in self.options: raise SphinxError('Config option is required') import os path = os.path.join(env.app.srcdir, self.options['config']) if not os.path.exists(path): raise SphinxError('Missing config path {}'.format( self.options['config'])) item_list = yaml_writer.read(path) itemnodes = [] for item in item_list: title, info, img = [ item.get(u"title", u""), item.get(u"info", u""), item.get(u"image_url", u"") ] _, node, data = self.create_question( title_text=self.arguments[0].replace(u"$title", title), points=False) more = u"" if img: e = aplus_nodes.html(u"p", {u"class": u"indent"}) e.append( aplus_nodes.html(u"img", { u"src": img, u"alt": title, u"style": u"max-height:100px;" })) node.append(e) more += str(e) if info: e = aplus_nodes.html(u"p", {u"class": u"indent"}) e.append(nodes.Text(info)) node.append(e) more += str(e) data[u'options'] = self.generate_options(env, node) data[u'more'] = more itemnodes.append(node) return itemnodes
def write(app, exception): ''' Writes the table of contents level configuration. ''' if exception: return course_title = app.config.course_title course_open = app.config.course_open_date course_close = app.config.course_close_date course_late = app.config.default_late_date course_penalty = app.config.default_late_penalty override = app.config.override modules = [] category_keys = [] def traverse_tocs(doc): names = [] for toc in doc.traverse(addnodes.toctree): hidden = toc.attributes['hidden'] for _, docname in toc.get('entries', []): names.append((docname, hidden)) return [(name, hidden, app.env.get_doctree(name)) for name, hidden in names] def first_title(doc): titles = doc.traverse(nodes.title) return titles[0].astext() if titles else u'Unnamed' def first_meta(doc): metas = doc.traverse(directives.meta.aplusmeta) return metas[0].options if metas else {} # Tries to parse date from natural text. def parse_date(src): parts = src.split(u' ', 1) d = parts[0] t = parts[1] if len(parts) > 1 else '' if re.match(r'^\d\d.\d\d.\d\d\d\d$', d): ds = d.split('.') d = ds[2] + u'-' + ds[1] + u'-' + ds[0] elif not re.match(r'^\d\d\d\d-\d\d-\d\d$', d): raise SphinxError(u'Invalid date ' + d) if not re.match(r'^\d\d(:\d\d(:\d\d)?)?$', t): t = u'12:00' return d + u' ' + t def parse_float(src, default): return float(src) if src else default # Recursive chapter parsing. def parse_chapter(docname, doc, parent): for config_file in [ e.yaml_write for e in doc.traverse(aplus_nodes.html) if e.has_yaml(u'exercise') ]: config = yaml_writer.read(config_file) if config.get(u'_external', False): exercise = config.copy() del exercise[u'_external'] else: exercise = { u'key': config[u'key'], u'config': config[u'key'] + u'.yaml', u'max_submissions': config.get(u'max_submissions', 0), u'max_points': config.get(u'max_points', 0), u'difficulty': config.get(u'difficulty', ''), u'points_to_pass': config.get(u'points_to_pass', 0), u'category': config[u'category'], u'min_group_size': config.get(u'min_group_size', 1), u'max_group_size': config.get(u'max_group_size', 1), u'confirm_the_level': config.get(u'confirm_the_level', False), } exercise.update({ u'allow_assistant_grading': False, u'status': u'unlisted', }) if u'scale_points' in config: exercise[u'max_points'] = config.pop(u'scale_points') parent.append(exercise) if not config[u'category'] in category_keys: category_keys.append(config[u'category']) category = u'chapter' for name, hidden, child in traverse_tocs(doc): meta = first_meta(child) status = u'hidden' if 'hidden' in meta else ( u'unlisted' if hidden else u'ready') chapter = { u'key': name.split(u'/')[-1], #name.replace('/', '_'), u'status': status, u'name': first_title(child), u'static_content': name + u'.html', u'category': category, u'use_wide_column': app.config.use_wide_column, u'children': [], } if meta: audience = meta.get('audience') if audience: chapter[u'audience'] = yaml_writer.ensure_unicode(audience) if category in override: chapter.update(override[category]) parent.append(chapter) if not u'chapter' in category_keys: category_keys.append(u'chapter') parse_chapter(name, child, chapter[u'children']) root = app.env.get_doctree(app.config.master_doc) if not course_title: course_title = first_title(root) # Traverse the documents using toctree directives. app.info('Traverse document elements to write configuration index.') title_date_re = re.compile(r'.*\(DL (.+)\)') for docname, hidden, doc in traverse_tocs(root): title = first_title(doc) title_date_match = title_date_re.match(title) meta = first_meta(doc) status = u'hidden' if 'hidden' in meta else ( u'unlisted' if hidden else u'ready') open_src = meta.get('open-time', course_open) close_src = meta.get( 'close-time', title_date_match.group(1) if title_date_match else course_close) late_src = meta.get('late-time', course_late) module = { u'key': docname.split(u'/')[0], u'status': status, u'name': title, u'children': [], } if open_src: module[u'open'] = parse_date(open_src) if close_src: module[u'close'] = parse_date(close_src) if late_src: module[u'late_close'] = parse_date(late_src) module[u'late_penalty'] = parse_float( meta.get('late-penalty', course_penalty), 0.0) modules.append(module) parse_chapter(docname, doc, module[u'children']) # Create categories. category_names = app.config.category_names categories = { key: { u'name': category_names.get(key, key), } for key in category_keys } for key in ['chapter', 'feedback']: if key in categories: categories[key][u'status'] = u'nototal' # Get relative out dir. i = 0 while i < len(app.outdir) and i < len( app.confdir) and app.outdir[i] == app.confdir[i]: i += 1 outdir = app.outdir.replace("\\", "/") if outdir[i] == '/': i += 1 outdir = outdir[i:] # Write the configuration index. config = { u'name': course_title, u'language': app.config.language, u'static_dir': outdir, u'modules': modules, u'categories': categories, } if course_open: config[u'start'] = parse_date(course_open) if course_close: config[u'end'] = parse_date(course_close) # Append directly configured content. def recursive_merge(config, append): if type(append) == dict: for key, val in append.items(): if not key in config: config[key] = val else: recursive_merge(config[key], append[key]) elif type(append) == list: for entry in append: add = True if 'key' in entry: for old in config: if 'key' in old and old['key'] == entry['key']: recursive_merge(old, entry) add = False if add: config.append(entry) for path in app.config.append_content: recursive_merge(config, yaml_writer.read(path)) yaml_writer.write(yaml_writer.file_path(app.env, 'index'), config) # Mark links to other modules. app.info('Retouch all files to append chapter link attributes.') keys = [m['key'] for m in modules] keys.extend(['toc', 'user', 'account']) for html_file in html_tools.walk(os.path.dirname(app.outdir)): html_tools.annotate_file_links(html_file, [u'a'], [u'href'], keys, u'data-aplus-chapter="yes" ')
def run(self): key, difficulty, points = self.extract_exercise_arguments() env = self.state.document.settings.env name = u"{}_{}".format(env.docname.replace(u'/', u'_'), key) override = env.config.override classes = [u'exercise'] if 'class' in self.options: classes.extend(self.options['class']) # Add document nodes. args = { u'class': u' '.join(classes), u'data-aplus-exercise': u'yes', } if 'quiz' in self.options: args[u'data-aplus-quiz'] = u'yes' if 'ajax' in self.options: args[u'data-aplus-ajax'] = u'yes' node = aplus_nodes.html(u'div', args) paragraph = aplus_nodes.html(u'p', {}) paragraph.append( nodes.Text(translations.get(env, 'submit_placeholder'))) node.append(paragraph) key_title = u"{} {}".format(translations.get(env, 'exercise'), key) # Load or create exercise configuration. if 'config' in self.options: path = os.path.join(env.app.srcdir, self.options['config']) if not os.path.exists(path): raise SphinxError('Missing config path {}'.format( self.options['config'])) data = yaml_writer.read(path) config_title = data.get(u'title', None) else: data = {u'_external': True} if 'url' in self.options: data[u'url'] = ensure_unicode(self.options['url']) if 'lti' in self.options: data.update({ u'lti': ensure_unicode(self.options['lti']), u'lti_context_id': ensure_unicode(self.options.get('lti_context_id', u'')), u'lti_resource_link_id': ensure_unicode( self.options.get('lti_resource_link_id', u'')), }) config_title = None config_title = self.options.get('title', config_title) category = u'submit' data.update({ u'key': name, u'title': env.config.submit_title.format(key_title=key_title, config_title=config_title), u'category': u'submit', u'scale_points': points, u'difficulty': difficulty or '', u'max_submissions': self.options.get( 'submissions', data.get('max_submissions', env.config.program_default_submissions)), u'min_group_size': data.get('min_group_size', env.config.default_min_group_size), u'max_group_size': data.get('max_group_size', env.config.default_max_group_size), u'points_to_pass': self.options.get('points-to-pass', data.get('points_to_pass', 0)), }) if category in override: data.update(override[category]) if 'url' in data: data['url'] = data['url'].format(key=name) node.write_yaml(env, name, data, 'exercise') return [node]