Skip to content
This repository has been archived by the owner on Jun 18, 2020. It is now read-only.

Commit

Permalink
Merge branch 'release/1.6'
Browse files Browse the repository at this point in the history
  • Loading branch information
moschlar committed Apr 22, 2014
2 parents 8808eca + 154bad7 commit f5bf09d
Show file tree
Hide file tree
Showing 47 changed files with 868 additions and 414 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ python:
- 2.6
- 2.7
env:
- TG2_VERSION=2.1.5
- TG2_VERSION=2.2.0
- TG2_VERSION=2.2.1
- TG2_VERSION=2.2.2
Expand Down
9 changes: 9 additions & 0 deletions Changelog
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
Changelog
=========

Version 1.6
-----------

- **Source code scaffolding** and filename/source templates, all to be set for
an assignment
- LTI improvements
- Bulk actions in admin interface
- Increased textarea/source code editor fields
- Lots of fixes to functionality, speed and design

Version 1.5.4
-------------
Expand Down
40 changes: 40 additions & 0 deletions migration/versions/453b256dce6b_test_strip_parse_errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""test_strip_parse_errors
Revision ID: 453b256dce6b
Revises: 3a8c537e6090
Create Date: 2013-06-30 20:58:50.998667
"""
#
# # SAUCE - System for AUtomated Code Evaluation
# # Copyright (C) 2013 Moritz Schlarb
# #
# # This program is free software: you can redistribute it and/or modify
# # it under the terms of the GNU Affero General Public License as published by
# # the Free Software Foundation, either version 3 of the License, or
# # any later version.
# #
# # This program is distributed in the hope that it will be useful,
# # but WITHOUT ANY WARRANTY; without even the implied warranty of
# # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# # GNU Affero General Public License for more details.
# #
# # You should have received a copy of the GNU Affero General Public License
# # along with this program. If not, see <http://www.gnu.org/licenses/>.
#

# revision identifiers, used by Alembic.
revision = '453b256dce6b'
down_revision = '530b45f11128'

from alembic import op
#from alembic.operations import Operations as op
import sqlalchemy as sa


def upgrade():
op.add_column('tests', sa.Column(u'strip_parse_errors', sa.Boolean(), nullable=False, default=False, server_default='False'))


def downgrade():
op.drop_column('tests', u'strip_parse_errors')
48 changes: 48 additions & 0 deletions migration/versions/4b2435ef2487_scaffold.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""Source code scaffolds
Revision ID: 4b2435ef2487
Revises: 453b256dce6b
Create Date: 2014-03-16 22:05:22.960849
"""
#
# # SAUCE - System for AUtomated Code Evaluation
# # Copyright (C) 2013 Moritz Schlarb
# #
# # This program is free software: you can redistribute it and/or modify
# # it under the terms of the GNU Affero General Public License as published by
# # the Free Software Foundation, either version 3 of the License, or
# # any later version.
# #
# # This program is distributed in the hope that it will be useful,
# # but WITHOUT ANY WARRANTY; without even the implied warranty of
# # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# # GNU Affero General Public License for more details.
# #
# # You should have received a copy of the GNU Affero General Public License
# # along with this program. If not, see <http://www.gnu.org/licenses/>.
#

# revision identifiers, used by Alembic.
revision = '4b2435ef2487'
down_revision = '453b256dce6b'

from alembic import op
#from alembic.operations import Operations as op
import sqlalchemy as sa


def upgrade():
op.add_column('assignments', sa.Column('submission_scaffold_foot', sa.Unicode(10485760), nullable=True))
op.add_column('assignments', sa.Column('submission_scaffold_head', sa.Unicode(10485760), nullable=True))
op.add_column('assignments', sa.Column('submission_filename', sa.Unicode(255), nullable=True))
op.add_column('assignments', sa.Column('submission_template', sa.Unicode(10485760), nullable=True))
op.add_column('assignments', sa.Column('submission_scaffold_show', sa.Boolean(), nullable=False, default=True, server_default='True'))


def downgrade():
op.drop_column('assignments', 'submission_scaffold_show')
op.drop_column('assignments', 'submission_template')
op.drop_column('assignments', 'submission_filename')
op.drop_column('assignments', 'submission_scaffold_head')
op.drop_column('assignments', 'submission_scaffold_foot')
2 changes: 2 additions & 0 deletions sauce/config/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
## along with this program. If not, see <http://www.gnu.org/licenses/>.
#

from tg import flash
from tgext.admin.config import AdminConfig, CrudRestControllerConfig
from sprox.fillerbase import TableFiller

Expand All @@ -33,6 +34,7 @@ class SAUCECrudRestControllerConfig(CrudRestControllerConfig):
'''

def _post_init(self):
flash('ATTENTION: Be very careful in this administration interface!', 'warn')
class MyTableFiller(TableFiller):
__entity__ = self.model
self.table_filler_type = MyTableFiller
Expand Down
22 changes: 16 additions & 6 deletions sauce/controllers/assignments.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@
from repoze.what.predicates import Any, not_anonymous, has_permission
from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
from sqlalchemy.exc import SQLAlchemyError
from tw2.pygmentize import Pygmentize

# project specific imports
from sauce.lib.authz import is_public, has_teacher
from sauce.lib.authz import user_is_in, is_public
from sauce.model import Assignment, Submission, DBSession
from sauce.lib.menu import menu
from sauce.controllers.lessons import SubmissionsController
Expand Down Expand Up @@ -64,9 +65,8 @@ def __init__(self, assignment):

self.allow_only = Any(
is_public(self.assignment),
has_teacher(self.assignment),
has_teacher(self.sheet),
has_teacher(self.event),
user_is_in('teachers', self.event),
user_is_in('tutors', self.event),
has_permission('manage'),
msg=u'This Assignment is not public'
)
Expand Down Expand Up @@ -108,6 +108,9 @@ def index(self, page=1, *args, **kwargs):
user_id=teammate.id,
))

lexer_name = self.assignment.allowed_languages[0].lexer_name if len(self.assignment.allowed_languages) == 1 else ''
c.pygmentize = Pygmentize(lexer_name=lexer_name)

return dict(page='assignments', event=self.event, assignment=self.assignment, values=values)

@expose()
Expand All @@ -121,8 +124,15 @@ def submit(self, *args, **kwargs):
flash('This assignment is not active, you may not create a submission', 'warning')
redirect(url(self.assignment.url))

submission = Submission(assignment=self.assignment, user=request.user,
created=datetime.now())
submission = Submission(
assignment=self.assignment,
filename=self.assignment.submission_filename or None,
source=self.assignment.submission_template or None,
language=self.assignment.allowed_languages[0],
user=request.user,
created=datetime.now(),
modified=datetime.now(),
)
DBSession.add(submission)
try:
DBSession.flush()
Expand Down
104 changes: 94 additions & 10 deletions sauce/controllers/crc/assignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
@see: :mod:`sauce.controllers.crc.base`
TODO: If possible, syntax highlighting for scaffold and template
TODO: tw2.dynforms to display scaffold field conditionally
@since: 12.11.2012
@author: moschlar
'''
Expand All @@ -24,19 +27,41 @@
## along with this program. If not, see <http://www.gnu.org/licenses/>.
#

from tg import config, url
from sauce.controllers.crc.base import FilterCrudRestController
from sauce.controllers.crc.test import run_tests
from sauce.model import Sheet, Assignment
import sauce.lib.helpers as h

from webhelpers.html.tags import link_to
from webhelpers.html.tags import link_to, literal

import tw2.bootstrap.forms as twb
try:
from tw2.ace import AceWidget as SourceEditor
# from tw2.codemirror import CodeMirrorWidget as SourceEditor
except ImportError: # pragma: no cover
from tw2.bootstrap.forms import TextArea as SourceEditor

import logging
log = logging.getLogger(__name__)

__all__ = ['SheetsCrudController', 'AssignmentsCrudController']


_lti = config.features.get('lti', False)


#--------------------------------------------------------------------------------


def _submissions(filler, obj):
'''Display submission link button for Assignments or Sheets'''
return (u'<a href="%s/submissions" style="white-space: pre;" class="btn btn-mini">'
'<i class="icon-inbox"></i>&nbsp;Submissions</a>' % (obj.url))

#--------------------------------------------------------------------------------


class SheetsCrudController(FilterCrudRestController):
'''CrudController for Sheets'''

Expand All @@ -50,14 +75,21 @@ class SheetsCrudController(FilterCrudRestController):
'__field_order__': [
'sheet_id', 'name', 'public',
'start_time', 'end_time', 'assignments',
'submissions',
],
'__search_fields__': ['id', 'sheet_id', 'name', ('assignments', 'assignment_id')],
'__xml_fields__': ['assignments'],
'__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__ = {
Expand Down Expand Up @@ -112,41 +144,78 @@ class AssignmentsCrudController(FilterCrudRestController):
'teacher_id', 'teacher',
#'allowed_languages',
'_teacher', 'description', 'tests',
'submissions', 'show_compiler_msg',
'show_compiler_msg',
'_start_time', '_end_time',
'lti',
'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__': ['sheet', 'allowed_languages'],
'__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),
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',
'_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',
},
Expand All @@ -159,8 +228,23 @@ class AssignmentsCrudController(FilterCrudRestController):
'show_compiler_msg': {
'help_text': u'Show error messages or warnings from the compiler run',
},
'public': {
'help_text': u'Make assignment visible for students',
'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'],
Expand Down
Loading

0 comments on commit f5bf09d

Please sign in to comment.