Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Assignment Categories #1196

Open
wants to merge 6 commits into
base: epai/categories
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions server/controllers/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ def edit_category(cid, category_id):
if request.method == 'POST' and form.validate(editing=True):
if form.delete.data == True:
category.archive()
db.session.delete(category)
db.session.commit()
msg = "Category \"{}\" was deleted.".format(category.name)
flash(msg, "error")
Expand All @@ -438,15 +439,10 @@ def edit_category(cid, category_id):
@is_staff(course_arg='cid')
def course_assignments(cid):
courses, current_course = get_courses(cid)
categories = current_course.categories
uncategorized = Assignment.query.filter_by(
course=current_course,
category=None
).all()
return render_template('staff/course/assignment/assignments.html',
current_course=current_course,
categories=categories,
uncategorized=uncategorized)
categories=current_course.categories,
uncategorized=current_course.uncategorized)

@admin.route("/course/<int:cid>/assignments/new", methods=["GET", "POST"])
@is_staff(course_arg='cid')
Expand Down Expand Up @@ -490,6 +486,8 @@ def assignment(cid, aid):
cache.delete_memoized(Assignment.name_to_assign_info)
db.session.commit()
flash("Assignment edited successfully.", "success")
else:
form.set_defaults()

return render_template('staff/course/assignment/assignment.html', assignment=assign,
form=form, courses=courses,
Expand Down
8 changes: 7 additions & 1 deletion server/controllers/student.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,14 @@ def course(offering):
'inactive': [a.user_status(current_user) for a in course.assignments
if not a.active and a.visible]
}

user_assigns = lambda assigns: [a.user_status(current_user) for a in assigns if a.visible]
categories = [(c, user_assigns(c.assignments)) for c in course.categories]
uncategorized = user_assigns(course.uncategorized)
print(uncategorized)
return render_template('student/course/index.html', course=course,
**assignments)
categories=categories,
uncategorized=uncategorized)


@student.route('/<assignment_name:name>/')
Expand Down
47 changes: 25 additions & 22 deletions server/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,28 +158,10 @@ def validate(self, editing=False):

class AssignmentForm(BaseForm):

def __init__(self, course, obj=None, **kwargs):
self.course = course
self.obj = obj
super(AssignmentForm, self).__init__(obj=obj, **kwargs)
if obj:
if obj.due_date == self.due_date.data:
self.due_date.data = utils.local_time_obj(obj.due_date, course)
if obj.lock_date == self.lock_date.data:
self.lock_date.data = utils.local_time_obj(
obj.lock_date, course)

# dynamically set values
categories = Category.query.filter_by(
course=course
).order_by(Category.name).all()
self.category.choices = [(str(c.id), c.name) for c in categories]
self.category.choices.insert(0, ('0', 'Uncategorized'))
self.category.default = 'Uncategorized'

display_name = StringField('Name',
validators=[validators.required()])
category = SelectField('Grading Category')
points = IntegerField('Points')
name = StringField(description='Endpoint',
validators=[validators.required()])
due_date = DateTimeField('Due Date (Course Time)',
Expand All @@ -204,19 +186,40 @@ def __init__(self, course, obj=None, **kwargs):
visible = BooleanField('Visible On Student Dashboard', default=True)
autograding_key = StringField('Autograder Key',
validators=[validators.optional()])

def __init__(self, course, obj=None, **kwargs):
self.course = course
super(AssignmentForm, self).__init__(obj=obj, **kwargs)

# dynamically set values
self.category.choices = [(str(c.id), c.name) for c in course.categories]
self.category.choices.insert(0, ('0', 'Uncategorized'))

self.obj = obj
if obj:
self.due_date.data = utils.local_time_obj(obj.due_date, self.course)
self.lock_date.data = utils.local_time_obj(obj.lock_date, self.course)

def set_defaults(self):
if self.obj:
category = self.obj.category
self.category.data = str(category and category.id or 0)


@property
def endpoint(self):
return '{}/{}'.format(self.course.offering, self.display_name.data)

def populate_obj(self, obj):
""" Updates obj attributes based on form contents. """
category_id = int(self.category.data)
if self.category.data:
self.category.data = Category.query.get(category_id)
super(AssignmentForm, self).populate_obj(obj)
self.category.data = Category.query.get(category_id)
super().populate_obj(obj)
obj.due_date = utils.server_time_obj(self.due_date.data, self.course)
obj.lock_date = utils.server_time_obj(self.lock_date.data, self.course)
obj.name = self.endpoint
self.obj = obj
self.category.data = str(category_id)

def validate(self):
if not super(AssignmentForm, self).validate():
Expand Down
38 changes: 24 additions & 14 deletions server/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,20 @@ def __repr__(self):
def by_name(name):
return Course.query.filter_by(offering=name).one_or_none()

@property
def categories(self):
return Category.query.filter_by(
course=self
).order_by(Category.name).all()

@property
def uncategorized(self):
return Assignment.query.filter_by(
course=self,
category=None # uncategorized
).all()


@property
def display_name_with_semester(self):
year = self.offering[-2:]
Expand Down Expand Up @@ -358,9 +372,9 @@ class Category(Model):
e.g. "Drop lowest X", "Cap at X points", "Never drop X"
"""
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), index=True, default="Uncategorized")
name = db.Column(db.String(255), index=True)
course_id = db.Column(db.ForeignKey("course.id"), index=True, nullable=False)
points = db.Column(db.Integer, default=0.0)
points = db.Column(db.Integer, default=0)
visible = db.Column(db.Boolean, default=True)
ceil = db.Column(db.Boolean, default=True)
# assignments (from Assignment backref)
Expand All @@ -382,12 +396,8 @@ def can(cls, obj, user, action):
return is_staff

def archive(self):
uncategorized = Category.query.filter_by(
course=self.course,
name='Uncategorized'
).first()
for assignment in self.assignments:
assignment.category = uncategorized
for assignment in self.assignments[:]:
assignment.category = None


class Assignment(Model):
Expand Down Expand Up @@ -492,19 +502,19 @@ def assignment_stats(assign_id, detailed=True):
})
return stats

@staticmethod
@classmethod
@cache.memoize(1000)
def name_to_assign_info(name):
assign = Assignment.query.filter_by(name=name).one_or_none()
def name_to_assign_info(cls, name):
assign = cls.query.filter_by(name=name).one_or_none()
if assign:
info = assign.as_dict()
info['active'] = assign.active
return info

@staticmethod
def by_name(name):
@classmethod
def by_name(cls, name):
""" Return assignment object when given a name."""
return Assignment.query.filter_by(name=name).one_or_none()
return cls.query.filter_by(name=name).one_or_none()

def user_timeline(self, user_id, current_backup_id=None):
""" Timeline of user submissions. Returns a dictionary
Expand Down
35 changes: 23 additions & 12 deletions server/static/css/student.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ i, br {
font-weight:300;
letter-spacing:normal;
}
.subcontent {
margin-bottom: 30px;
}
.content h2 {
font-size:2em;
font-weight:300;
Expand Down Expand Up @@ -201,6 +204,9 @@ section {
table {
width:100%;
}
.subcontent .wrap table {
background-color: #f6f6f6;
}
tr.header {
color:#767676;
}
Expand Down Expand Up @@ -433,40 +439,45 @@ header .logo {
}

.invite-input {
width: 95%;
margin:1.5em 0 0 1em;

width: 300px;
margin:1em 0 1.5em 14px;
color:#666;
outline: none;
border-top: none;
border-left: none;
border-right: none;
border-bottom-width: 1px;
border-bottom-style: dashed;
border-bottom-color: rgb(153, 153, 153);
padding: 14px 0;
border-bottom: 1px solid #ccc;
}
.invite-input:focus {
border-bottom: 1px solid black;
color: black;
}
.btn-invite {
margin:1em 0 0 0;
margin-top: 2em;
}
.btn-accept {
margin-bottom: 1em;
outline: none;
}
.subcontent.list .wrap {
margin-bottom: 2em;
}
.content h1 {
padding:0 14px;
}
.content h2 {
padding:0 14px 10px 14px;
padding:0 14px;
}
.cell {
padding:2em;
padding: 1em 2em;
background-color:#f6f6f6;
font-size: 0.9em;
}
.upload-cell {
background-color: #f6f6f6;
}

.cell:nth-child(even) {
background-color:#f6f6f6;
}
.cell-title {
font-size:1.5em;
}
Expand Down
4 changes: 2 additions & 2 deletions server/templates/staff/course/assignment/assignment.html
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ <h3 class="box-title">Edit {{ assignment.display_name }} Assignment</h3>
{% call forms.render_form(form, action_url="", action_text='Update Assignment', class_='form') %}
{{ forms.render_field(form.display_name, label_visible=true, placeholder='Hog', type='text') }}
{{ forms.render_field(form.name, label_visible=false, value=current_course.offering + '/hog', type='text', id="assignment-name") }}
{{ forms.render_field(form.category, label_visible=true,
required="required", type='text', id="category") }}
{{ forms.render_field(form.category, label_visible=true, required="required", type='text', id="category") }}
{{ forms.render_field(form.points, label_visible=true, required="required", type='text', id="points") }}
{{ forms.render_field(form.max_group_size, label_visible=true, type='number', min='1') }}
{{ forms.render_field(form.due_date, label_visible=true, placeholder=utils.new_due_date(current_course), type='text', class='form-control datepicker') }}
{{ forms.render_field(form.lock_date, label_visible=true, placeholder=utils.new_lock_date(current_course), type='text', class='form-control datepicker') }}
Expand Down
Loading