def linked_user_comments(user, maxlength=0, avatar=20): if not isinstance(user, model.User): user_name = unicode(user) user = model.User.get(user_name) if not user: return user_name if user: name = user.name if model.User.VALID_NAME.match(user.name) else user.id displayname = user.display_name user_name = user.name if model.User.VALID_NAME.match(user.name) else user.id if maxlength and len(user.display_name) > maxlength: displayname = displayname[:maxlength] + '...' if maxlength and len(user.name) > maxlength: user_name = user_name[:maxlength] + '...' return tags.literal(u'{icon} {link}'.format( icon=gravatar( email_hash=user.email_hash, size=avatar ), link=tags.link_to( user_name, url_for(controller='user', action='read', id=name) ) ))
def errorlist(self, name=None, **attrs): """ Renders errors in a <ul> element if there are multiple, otherwise will use a div. Unless specified in attrs, class will be "Alert". If no errors present returns an empty string. `name` : errors for name. If **None** all errors will be rendered. """ if name is None: errors = self.all_errors() else: errors = self.errors_for(name) if not errors: return '' if 'class_' not in attrs: attrs['class_'] = "Alert" if len(errors) > 1: content = "\n".join(HTML.tag("li", error) for error in errors) return HTML.tag("ul", tags.literal(content), **attrs) return HTML.tag("div", errors[0], **attrs)
def errorlist(self, name=None, **attrs): _ = self.form.request.translate """ Renders errors in a <ul> element if there are multiple, otherwise will use a div. Unless specified in attrs, class will be "Alert". If no errors present returns an empty string. `name` : errors for name. If **None** all errors will be rendered. """ if name is None: errors = self.all_errors() else: errors = self.errors_for(name) if not errors: return '' if 'class_' not in attrs: attrs['class_'] = "Alert" if len(errors) > 1: content = Markup("\n").join( HTML.tag("li", error) for error in errors) return HTML.tag("ul", tags.literal(content), **attrs) return Markup(''' <div class="ui-widget clearfix" style="margin: 0.25em;"> <div class="ui-state-error error-field-wrapper"> <span class="ui-icon ui-icon-alert error-notice-icon">%s</span>%s </div> </div> ''') % (_('Error'), errors[0])
def _fetch_drupal_content(self, identifier, language=None, fallback=True): """ This helper fetches content from Drupal database using url alias identifier. Return content as dictionary containing language, title, body, node_id and edit link. None if not found. Tries to fallback to different language if fallback is True. Not cached. """ if not language: language = helpers.lang() query = """SELECT url_alias.language, node.title, field_revision_body.body_value, node.nid from url_alias INNER JOIN node ON node.nid = split_part(url_alias.source, '/', 2)::integer INNER JOIN field_revision_body ON field_revision_body.entity_id = split_part(url_alias.source, '/', 2)::integer WHERE url_alias.alias = %(identifier)s""" # noqa: E501 results = {} for content_language, title, body, node_id in self.engine.execute(query, {'identifier': identifier}): results[content_language] = {'language': content_language, 'title': title, 'body': body, 'node_id': node_id} result = results.get(language, None) if not result and fallback and results: for fallback_language in self._language_fallback_order: result = results.get(fallback_language) if result: break if not result: result = results.itervalues().next() if result: result['edit'] = urllib.quote("/%s/node/%s/edit" % (language, str(result['node_id']))) result['body'] = literal(result['body']) return result
def errorlist(self, name=None, **attrs): _ = self.form.request.translate """ Renders errors in a <ul> element if there are multiple, otherwise will use a div. Unless specified in attrs, class will be "Alert". If no errors present returns an empty string. `name` : errors for name. If **None** all errors will be rendered. """ if name is None: errors = self.all_errors() else: errors = self.errors_for(name) if not errors: return '' if 'class_' not in attrs: attrs['class_'] = "Alert" if len(errors) > 1: content = Markup("\n").join(HTML.tag("li", error) for error in errors) return HTML.tag("ul", tags.literal(content), **attrs) return Markup(''' <div class="ui-widget clearfix" style="margin: 0.25em;"> <div class="ui-state-error error-field-wrapper"> <span class="ui-icon ui-icon-alert error-notice-icon">%s</span>%s </div> </div> ''') % (_('Error'),errors[0])
def column(self, name, content, cols, inner_cols=None, errors=True): "Wraps content in a Foundation column" error_content = '' if errors: error_content = self.error_small(name, cols=inner_cols) return HTML.tag( 'div', tags.literal(content), error_content, class_='small-%d columns %s' % ( cols, 'error' if error_content else ''))
def column(self, name, content, cols, inner_cols=None, errors=True): "Wraps content in a Foundation column" error_content = '' if errors: error_content = self.error_small(name, cols=inner_cols) return HTML.tag('div', tags.literal(content), error_content, class_='small-%d columns %s' % (cols, 'error' if error_content else ''))
def hidden_tag(self, *names): """ Convenience for printing all hidden fields in a form inside a hidden DIV. Will also render the CSRF hidden field. :versionadded: 0.4 """ inputs = [self.hidden(name) for name in names] inputs.append(self.csrf()) return HTML.tag("div", tags.literal("".join(inputs)), style="display:none;")
def hidden_tag(self, *names): """ Convenience for printing all hidden fields in a form inside a hidden DIV. Will also render the CSRF hidden field. :versionadded: 0.4 """ inputs = [self.hidden(name) for name in names] inputs.append(self.csrf()) return HTML.tag("div", tags.literal("".join(inputs)), style="display:none;")
def linked_organization(org): organization = helpers.get_organization(org) if organization: return tags.literal(u'{icon} {link}'.format( icon=helpers.icon_html( organization['image_display_url'], alt='', inline=False), link=tags.link_to(organization['title'], url_for( controller='organization', action='read', id=organization['name'])))) return 'Not Existed'
def hidden_tag(self, *names): """ Convenience for printing all hidden fields in a form inside a hidden DIV. Will also render the CSRF and referal hidden fields if they haven't been output explicitly already. :versionadded: 0.4 """ inputs = [self.hidden(name) for name in names] if not self._csrf_done: inputs.append(self.csrf()) if not self._came_from_done: inputs.append(self.came_from()) return HTML.tag( 'div', tags.literal(''.join(inputs)), style='display:none;')
def hidden_tag(self, *names): """ Convenience for printing all hidden fields in a form inside a hidden DIV. Will also render the CSRF and referal hidden fields if they haven't been output explicitly already. :versionadded: 0.4 """ inputs = [self.hidden(name) for name in names] if not self._csrf_done: inputs.append(self.csrf()) if not self._came_from_done: inputs.append(self.came_from()) return HTML.tag('div', tags.literal(''.join(inputs)), style='display:none;')
def _fetch_drupal_content(self, identifier, language=None, fallback=True): """ This helper fetches content from Drupal database using url alias identifier. Return content as dictionary containing language, title, body, node_id and edit link. None if not found. Tries to fallback to different language if fallback is True. Not cached. """ if not language: language = helpers.lang() query = """SELECT url_alias.language, node.title, field_revision_body.body_value, node.nid from url_alias INNER JOIN node ON node.nid = split_part(url_alias.source, '/', 2)::integer INNER JOIN field_revision_body ON field_revision_body.entity_id = split_part(url_alias.source, '/', 2)::integer WHERE url_alias.alias = %(identifier)s""" results = {} for content_language, title, body, node_id in self.engine.execute( query, {'identifier': identifier}): results[content_language] = { 'language': content_language, 'title': title, 'body': body, 'node_id': node_id } result = results.get(language, None) if not result and fallback and results: for fallback_language in self._language_fallback_order: result = results.get(fallback_language) if result: break if not result: result = results.itervalues().next() if result: result['edit'] = urllib.quote("/%s/node/%s/edit" % (language, str(result['node_id']))) result['body'] = literal(result['body']) return result
def show_linked_user(user, maxlength=0, avatar=20): if not isinstance(user, model.User): user_name = text_type(user) user = model.User.get(user_name) if not user: return user_name if user: name = user.name if model.User.VALID_NAME.match(user.name) else user.id displayname = user.display_name if maxlength and len(user.display_name) > maxlength: displayname = displayname[:maxlength] + '...' return tags.literal(u'{icon} {link}'.format( icon=h.gravatar( email_hash=user.email_hash, size=avatar ), link=tags.link_to( displayname, ('/user/show/' + name) ) ))
def errorlist(self, name=None, **attrs): """ Renders errors in a <ul> element. Unless specified in attrs, class will be "error". If no errors present returns an empty string. `name` : errors for name. If **None** all errors will be rendered. """ if name is None: errors = self.all_errors() else: errors = self.errors_for(name) if not errors: return '' content = "\n".join(HTML.tag("li", error) for error in errors) if 'class_' not in attrs: attrs['class_'] = "error" return HTML.tag("ul", tags.literal(content), **attrs)
def errorlist(self, name=None, **attrs): """ Renders errors in a <ul> element. Unless specified in attrs, class will be "error". If no errors present returns an empty string. `name` : errors for name. If **None** all errors will be rendered. """ if name is None: errors = self.all_errors() else: errors = self.errors_for(name) if not errors: return '' content = "\n".join(HTML.tag("li", error) for error in errors) if 'class_' not in attrs: attrs['class_'] = "error" return HTML.tag("ul", tags.literal(content), **attrs)
def _user_image(user, size): url = _get_user_image(user) or "" return literal( '<img src="%s" width="%s" height="%s" class="media-image" />' % (url, size, size))
class TeamsCrudController(FilterCrudRestController): '''CrudController for Teams''' model = Team __table_options__ = { #'__omit_fields__': ['lesson_id'], '__field_order__': ['name', 'lesson', 'members', 'email', 'submissions'], '__omit_fields__': ['id', 'lesson_id'], '__search_fields__': ['id', 'lesson_id', 'name'], '__xml_fields__': ['name', 'lesson', 'members', 'email', 'submissions'], 'name': lambda filler, obj: literal(u'<span title="id=%d">%s</span>' % (obj.id, obj.name)), 'lesson': lambda filler, obj: link_to(obj.lesson.name, '../lessons/%d/edit' % obj.lesson.id, title='lesson_id=%d' % (obj.lesson_id)), 'members': lambda filler, obj: ', '.join(link_to(student.display_name, '../students/%d/edit' % student.id) for student in obj.members), 'email': _email_team, 'submissions': _submissions, '__base_widget_args__': {'sortList': [[2, 0]]}, } __form_options__ = { '__omit_fields__': ['id'], '__field_order__': ['id', 'name', 'lesson', 'members'], '__possible_field_names__': ['user_name', '_name', 'name', 'title'], } def _actions(self, obj): actions = super(TeamsCrudController, self)._actions(obj) actions.insert(1, (u'<a href="%d/rename" class="btn btn-mini" title="Rename Team with its member usernames">' u'<i class="icon-screenshot"></i></a>') % (obj.id)) return actions def _bulk_actions(self): bulk_actions = super(TeamsCrudController, self)._bulk_actions() bulk_actions.append( (u'<a href="rename" class="btn" title="Rename all Teams with their member usernames">' u'<i class="icon-screenshot"></i> Rename all</a>')) return bulk_actions @expose() def rename(self, obj=None): if obj is not None: try: obj = EntityValidator(self.provider, self.model).to_python(obj) except Invalid: flash('Could not rename Team "%s"' % (obj), 'error') return redirect('../') else: oldname = obj.name newname = obj.rename() flash('Renamed Team "%s" to "%s"' % (oldname, newname), 'ok') return redirect('../') elif obj is None: q = self.query_modifier(Team.query) l = q.count() for t in q: t.rename() flash('Renamed %d teams' % (l), 'ok') return redirect('./') flash('Could not rename Team "%s"' % (obj), 'error') return redirect('./')
def _user_image(user, size): url = _get_user_image(user) or "" return literal('<img src="%s" width="%s" height="%s" class="media-image" />' % (url, size, size))
class TestsCrudController(FilterCrudRestController): '''CrudController for Tests''' model = Test __table_options__ = { '__omit_fields__': [ 'assignment_id', 'argv', '_visible', 'input_data', 'output_data', 'input_filename', 'output_filename', 'ignore_case', 'ignore_returncode', 'show_partial_match', 'splitlines', 'split', 'comment_prefix', 'separator', 'strip_parse_errors', 'parse_int', 'parse_float', 'float_precision', 'sort', 'user_id', 'user', 'testruns', ], '__field_order__': [ 'id', 'assignment', 'name', 'visibility', '_timeout', 'input_type', 'output_type', ], '__search_fields__': ['id', 'assignment_id', 'name'], # '__headers__': {'_timeout': 'Timeout'}, '__xml_fields__': ['assignment'], 'assignment': lambda filler, obj: literal( u'''<a href="%d/test" class="btn btn-mini btn-inverse" title="Re-run all tests for this assignment" onclick="show_processing_modal('Testing %d Submission(s) in %d Test(s)...'); return true;"> <i class="icon-repeat icon-white"></i> </a> ''' % (obj.id, len(obj.assignment.submissions), len(obj.assignment.submissions) * len(obj.assignment.tests))) + link_to(obj.assignment.name, '../assignments/%d/edit' % obj.assignment.id, title='assignment_id=%d' % (obj.assignment_id)), '__base_widget_args__': { 'sortList': [[1, 0], [2, 0], [3, 0]] }, } __form_options__ = { '__omit_fields__': ['id', 'testruns', '_visible'], '__hide_fields__': ['user'], '__add_fields__': { 'docs': twb.Label( 'docs', text='Please read the <a href="%s">' % lurl('/docs/tests') + 'Test configuration documentation</a>!', css_class='bold', escape=False), 'ignore_opts': twb.Label('ignore_opts', text='Output ignore options', css_class='label'), 'split_opts': twb.Label('split_opts', text='Output splitting options', css_class='label'), 'parse_opts': twb.Label('parse_opts', text='Output parsing options', css_class='label'), }, '__field_order__': [ 'id', 'docs', 'assignment', 'name', 'visibility', 'input_data', 'output_data', 'input_type', 'output_type', 'input_filename', 'output_filename', '_timeout', 'argv', 'ignore_opts', 'ignore_case', 'comment_prefix', 'ignore_returncode', 'show_partial_match', 'split_opts', 'splitlines', 'split', 'separator', 'sort', 'parse_opts', 'strip_parse_errors', 'parse_int', 'parse_float', 'float_precision', ], '__field_widget_types__': { # 'name': twb.TextField, 'argv': twb.TextField, 'input_filename': twb.TextField, 'output_filename': twb.TextField, 'input_type': twjc.ChosenSingleSelectField, 'output_type': twjc.ChosenSingleSelectField, 'visibility': twb.RadioButtonTable, # 'input_data': FileField, # 'output_data': FileField, 'input_data': SourceEditor, 'output_data': SourceEditor, }, '__field_widget_args__': { 'argv': { 'help_text': u''' Command line arguments Possible variables are: {path}: Absolute path to temporary working directory {infile}: Full path to test input file {outfile}: Full path to test output file''' }, 'visibility': dict( help_text= u'Whether testrun results and/or data is shown to students or not', options=[('visible', 'Visible'), ('invisible', 'Invisible'), ('result_only', 'Show only the testrun result'), ('data_only', 'Show only the testrun data')], value='visible', prompt_text=None, cols=2, name='visibility', id='visibility'), '_timeout': { 'help_text': u'Timeout value, leave empty to use value from assignment' }, 'input_type': dict(options=[('stdin', 'stdin'), ('file', 'file')], value='stdin', prompt_text=None), 'output_type': dict(options=[('stdout', 'stdout'), ('file', 'file')], value='stdout', prompt_text=None), 'input_data': dict(css_class='span7', rows=8), 'output_data': dict(css_class='span7', rows=8), 'input_filename': dict(css_class='span7'), 'output_filename': dict(css_class='span7'), 'argv': dict(css_class='span7'), 'separator': { 'help_text': u'The separator string used for splitting and joining, default is None (whitespace)' }, 'ignore_case': { 'help_text': u'Call .lower() on output before comparison', 'default': True }, 'ignore_returncode': { 'help_text': u'Ignore test process returncode', 'default': True }, 'comment_prefix': { 'help_text': u'Ignore all lines that start with comment_prefix' }, 'show_partial_match': { 'help_text': u'Recognize partial match and show to user', 'default': True }, 'splitlines': { 'help_text': u'Call .splitlines() on full output before comparison', 'default': False }, 'split': { 'help_text': u'Call .split() on full output of output before comparison or on each line from .splitlines() if splitlines is set' }, 'strip_parse_errors': { 'help_text': u'Strip (True) or leave (False) unparsed fragments' }, 'parse_int': { 'help_text': u'Parse every substring in output to int before comparison', 'default': False }, 'parse_float': { 'help_text': u'Parse every substring in output to float before comparison', 'default': False }, 'float_precision': { 'help_text': u'''The precision (number of decimal digits) to compare for floats''' }, 'parallel_sort': { 'help_text': u'''If set, output will be sorted with the help of the thread ID inside of '[]' ''' }, 'sort': { 'help_text': u'''Sort output and test data before comparison. Parsing is performed first, if enabled.''', 'default': False }, }, } __setters__ = { 'test': ('null', lambda test: run_tests(test.assignment.submissions)), }
class SheetsCrudController(FilterCrudRestController): '''CrudController for Sheets''' model = Sheet __table_options__ = { '__omit_fields__': [ 'id', 'description', 'event_id', 'event', 'teacher', '_teacher', 'teacher_id', '_url', '_start_time', '_end_time', ], '__field_order__': [ 'sheet_id', 'name', 'public', 'start_time', 'end_time', 'assignments', 'submissions', ], '__search_fields__': ['id', 'sheet_id', 'name', ('assignments', 'assignment_id')], '__xml_fields__': ['sheet_id', 'name', 'assignments', 'submissions'], '__headers__': { 'sheet_id': '' }, 'start_time': lambda filler, obj: h.strftime(obj.start_time, False), 'end_time': lambda filler, obj: h.strftime(obj.end_time, False), 'sheet_id': lambda filler, obj: literal(u'<span title="sheet_id=%d">%d</span>' % (obj.sheet_id, obj.sheet_id)), 'name': lambda filler, obj: literal(u'<span title="sheet_id=%d">%s</span>' % (obj.sheet_id, obj.name)), 'assignments': lambda filler, obj: ', '.join( link_to(ass.name, '../assignments/%d/edit' % ass.id) for ass in obj.assignments), 'submissions': _submissions, '__base_widget_args__': { 'sortList': [[1, 0]] }, } __form_options__ = { '__omit_fields__': [ 'id', '_url', 'assignments', 'teacher', '_teacher', ], '__hide_fields__': ['event'], '__field_order__': [ 'id', 'sheet_id', 'name', 'description', 'public', '_start_time', '_end_time', ], '__field_widget_args__': { '_start_time': { 'help_text': u'Leave empty to use value from event', }, '_end_time': { 'help_text': u'Leave empty to use value from event', }, 'sheet_id': { 'label': u'Sheet Id', 'help_text': u'This id will be part of the url and has to be unique for the parent event', }, 'public': { 'help_text': u'Make sheet visible for students', }, }, '__require_fields__': ['sheet_id'], } __setters__ = { 'test': ('null', lambda sheet: run_tests( (submission for assignment in sheet.assignments for submission in assignment.submissions))), } def _actions(self, obj): actions = super(SheetsCrudController, self)._actions(obj) actions.insert( 1, u''' <a href="%d/test" class="btn btn-mini btn-inverse" title="Re-run all tests for this assignment" onclick="show_processing_modal('Testing %d Submission(s) in %d Test(s)...'); return true;"> <i class="icon-repeat icon-white"></i> </a>''' % (obj.id, sum((len(assignment.submissions) for assignment in obj.assignments)), sum((len(assignment.submissions) * len(assignment.tests) for assignment in obj.assignments)))) return actions
class AssignmentsCrudController(FilterCrudRestController): '''CrudController for Assignments''' model = Assignment __table_options__ = { '__omit_fields__': [ 'id', 'event_id', '_event', '_url', 'teacher_id', 'teacher', #'allowed_languages', '_teacher', 'description', 'tests', 'show_compiler_msg', '_start_time', '_end_time', 'submission_filename', 'submission_template', 'submission_scaffold_show', 'submission_scaffold_head', 'submission_scaffold_foot', '_lti', ], '__field_order__': [ 'sheet_id', 'sheet', 'assignment_id', 'name', 'public', 'start_time', 'end_time', 'timeout', 'allowed_languages', 'submissions', ] + (['lti_url'] if _lti else []), '__search_fields__': ['id', 'sheet_id', 'assignment_id', 'name'], '__xml_fields__': [ 'name', 'sheet_id', 'assignment_id', 'sheet', 'allowed_languages', 'submissions', 'lti_url' ], '__headers__': { 'sheet_id': '', 'assignment_id': '', }, 'start_time': lambda filler, obj: h.strftime(obj.start_time, False), 'end_time': lambda filler, obj: h.strftime(obj.end_time, False), 'name': lambda filler, obj: literal(u'<span title="assignment_id=%d">%s</span>' % (obj.assignment_id, obj.name)), 'sheet_id': lambda filler, obj: literal(u'<span title="sheet_id=%d">%d</span>' % (obj.sheet_id, obj.sheet_id)), 'assignment_id': lambda filler, obj: literal(u'<span title="assignment_id=%d">%d</span>' % (obj.assignment_id, obj.assignment_id)), 'sheet': lambda filler, obj: link_to(obj.sheet.name, '../sheets/%d/edit' % obj.sheet.id, title='sheet_id=%d' % (obj.sheet_id)), 'allowed_languages': lambda filler, obj: ', '.join( link_to(l.name, '/languages/%d' % l.id) for l in obj.allowed_languages), 'lti_url': lambda filler, obj: u'<span title="%s:%s">%s</span>' % (obj.lti.oauth_key, obj.lti.oauth_secret, url(obj.lti_url, qualified=True)) if obj.lti else u'', 'submissions': _submissions, '__base_widget_args__': { 'sortList': [[1, 0], [3, 0]] }, } __form_options__ = { '__omit_fields__': [ 'id', 'tests', 'submissions', '_event', 'teacher', '_url', '_teacher', '_lti', ], '__add_fields__': { 'submission_note': twb.Label( 'submission_note', label='Note', css_class='bold', text='For obvious reasons, it might not be the best idea to ' 'pre-define submission data here, ' 'when multiple languages are allowed.'), }, '__field_order__': [ 'id', 'sheet', 'assignment_id', 'name', 'description', 'public', '_start_time', '_end_time', 'timeout', 'allowed_languages', 'show_compiler_msg', 'submission_note', 'submission_filename', 'submission_template', 'submission_scaffold_show', 'submission_scaffold_head', 'submission_scaffold_foot', ], '__field_widget_types__': { 'submission_template': SourceEditor, 'submission_scaffold_head': SourceEditor, 'submission_scaffold_foot': SourceEditor, }, '__field_widget_args__': { 'assignment_id': { 'label': u'Assignment Id', 'help_text': u'Will be part of the url and has to be unique for the parent sheet', }, 'public': { 'help_text': u'Make assignment visible for students', }, '_start_time': { 'help_text': u'Leave empty to use value from sheet', }, '_end_time': { 'help_text': u'Leave empty to use value from sheet', }, 'timeout': { 'help_text': u'Default timeout value for test cases, leave empty for no time limit', }, 'show_compiler_msg': { 'help_text': u'Show error messages or warnings from the compiler run', }, 'submission_filename': { 'help_text': u'Default filename for submission', }, 'submission_template': { 'help_text': u'Template for submission source body', 'css_class': 'span7', 'cols': 80, 'rows': 6, }, 'submission_scaffold_show': { 'help_text': u'Whether to show head and foot scaffold to student', }, 'submission_scaffold_head': { 'help_text': u'Enforced head for submission source', 'css_class': 'span7', 'cols': 80, 'rows': 6, }, 'submission_scaffold_foot': { 'help_text': u'Enforced foot for submission source', 'css_class': 'span7', 'cols': 80, 'rows': 6, }, }, '__require_fields__': ['assignment_id', 'sheet'], } __setters__ = { 'test': ('null', lambda assignment: run_tests(assignment.submissions)), } def _actions(self, obj): actions = super(AssignmentsCrudController, self)._actions(obj) actions.insert( 1, u''' <a href="%d/test" class="btn btn-mini btn-inverse" title="Re-run all tests for this assignment" onclick="show_processing_modal('Testing %d Submission(s) in %d Test(s)...'); return true;"> <i class="icon-repeat icon-white"></i> </a>''' % (obj.id, len( obj.submissions), len(obj.submissions) * len(obj.tests))) return actions