Add order and score for course problems (#124)

* Add order and grade for course problems

* Fix delete problems bug
This commit is contained in:
Phuoc Anh Kha Le 2024-09-03 21:26:20 +07:00 committed by GitHub
parent 67888bcd27
commit c833dc06d9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 483 additions and 102 deletions

View file

@ -15,7 +15,11 @@
<div class="lesson-title">
{{ lesson.title }}
<div class="lesson-points">
{{progress['achieved_points'] | floatformat(1)}} / {{lesson.points}}
{% if progress['total_points'] %}
{{(progress['achieved_points'] / progress['total_points'] * lesson.points) | floatformat(1)}} / {{lesson.points}}
{% else %}
0 / {{lesson.points}}
{% endif %}
</div>
</div>
<div class="progress-container">
@ -30,7 +34,6 @@
{% set achieved_points = total_progress['achieved_points'] %}
{% set total_points = total_progress['total_points'] %}
{% set percentage = total_progress['percentage'] %}
{{_("Total achieved points")}}:
<span style="float: right; font-weight: normal;">
{{ achieved_points | floatformat(2) }} / {{ total_points }} ({{percentage|floatformat(1)}}%)

View file

@ -1,67 +1,109 @@
{% extends "course/base.html" %}
{% block two_col_media %}
{{ form.media.css }}
<style type="text/css">
.field-order, .field-title, .field-points {
display: inline-flex;
}
.form-header {
margin-bottom: 0.5em;
}
</style>
{% endblock %}
{% block js_media %}
{{ form.media.js }}
<script>
$(function() {
setTimeout(function() {
if ('DjangoPagedown' in window) {
DjangoPagedown.init();
}
}, 3000);
});
</script>
{% endblock %}
{% block middle_content %}
<form method="post">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
<h3 class="toggle {{'open' if form.errors else 'closed'}} form-header"><i class="fa fa-chevron-right fa-fw"></i>
{% if form.title.value() %}
{{form.order.value()}}. {{form.title.value()}}
{% else %}
+ {{_("Add new")}}
{% endif %}
</h3>
<div class="toggled" style="{{'display: none;' if not form.errors}} margin-bottom: 1em">
{{form.id}}
{% if form.errors %}
<div class="alert alert-danger alert-dismissable">
<a href="#" class="close">x</a>
{{_("Please fix below errors")}}
</div>
{% endif %}
{% for field in form %}
{% if not field.is_hidden %}
<div style="margin-bottom: 1em;">
{{ field.errors }}
<label for="{{field.id_for_label }}"><b>{{ field.label }}{% if field.field.required %}<span class="red"> * </span>{% endif %}:</b> </label>
<div class="org-field-wrapper field-{{field.name}}" id="org-field-wrapper-{{field.html_name}}">
{{ field }}
</div>
{% if field.help_text %}
<small class="org-help-text"><i class="fa fa-exclamation-circle"></i> {{ field.help_text|safe }}</small>
{% endif %}
</div>
{% endif %}
{% endfor %}
<hr/>
</div>
{% endfor %}
<input type="submit" value="{{_('Save')}}" style="float: right">
</form>
{% extends "course/base.html" %}
{% block two_col_media %}
{{ form.media.css }}
<style type="text/css">
.field-order, .field-title, .field-points {
display: inline-flex;
}
.form-header {
margin-bottom: 0.5em;
}
</style>
{% endblock %}
{% block js_media %}
{{ form.media.js }}
<script>
$(function() {
setTimeout(function() {
if ('DjangoPagedown' in window) {
DjangoPagedown.init();
}
}, 3000);
});
</script>
{% endblock %}
{% block middle_content %}
<form method="post">
{% csrf_token %}
{{ formset.management_form }}
{% for lesson_form in formset %}
{% set ns = namespace(problem_formset_has_error=false) %}
{% if lesson_form.instance.id %}
{% set problem_formset = problem_formsets[lesson_form.instance.id] %}
{% for form in problem_formset %}
{% if form.errors %}
{% set ns.problem_formset_has_error = true %}
{% break %}
{% endif %}
{% endfor %}
{% endif %}
<h3 class="toggle {{'open' if lesson_form.errors or ns.problem_formset_has_error else 'closed'}} form-header">
<i class="fa fa-chevron-right fa-fw"></i>
{% if lesson_form.title.value() %}
{{lesson_form.order.value()}}. {{lesson_form.title.value()}}
{% else %}
+ {{_("Add new")}}
{% endif %}
</h3>
<div class="toggled" style="{{'display: none;' if not lesson_form.errors and not ns.problem_formset_has_error}} margin-bottom: 1em">
{{lesson_form.id}}
{% if lesson_form.errors %}
<div class="alert alert-danger alert-dismissable">
<a href="#" class="close">x</a>
{{_("Please fix below errors")}}
</div>
{% endif %}
{% for field in lesson_form %}
{% if not field.is_hidden %}
<div style="margin-bottom: 1em;">
{{ field.errors }}
<label for="{{field.id_for_label }}"><b>{{ field.label }}{% if field.field.required %}<span class="red"> * </span>{% endif %}:</b> </label>
<div class="org-field-wrapper field-{{field.name}}" id="org-field-wrapper-{{field.html_name}}">
{{ field }}
</div>
{% if field.help_text %}
<small class="org-help-text"><i class="fa fa-exclamation-circle"></i> {{ field.help_text|safe }}</small>
{% endif %}
</div>
{% endif %}
{% endfor %}
<!-- Problems Table -->
{% if problem_formset %}
{{ problem_formset.management_form }}
<table class="table">
<thead>
<tr>
{% for field in problem_formset.forms.0 %}
{% if not field.is_hidden %}
<th class="problems-{{field.name}}">
{{field.label}}
</th>
{% endif %}
{% endfor %}
</tr>
</thead>
<tbody>
{% for form in problem_formset %}
<tr>
{% for field in form %}
<td class="problems-{{field.name}}" title="{{ field.help_text|safe if field.help_text }}" style="{{ 'display:none' if field.is_hidden }}">
{{field}}<div class="red">{{field.errors}}</div>
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
<hr/>
</div>
{% endfor %}
<input type="submit" value="{{_('Save')}}" style="float: right">
</form>
{% endblock %}

View file

@ -74,8 +74,8 @@
{% endblock %}
{% block middle_content %}
<center><h2>{{title}}</h2></center>
{% set lessons = course.lessons.all() %}
<center><h2>{{content_title}}</h2></center>
{% set lessons = course.lessons.order_by('order') %}
{{_("Sort by")}}:
<select id="sortSelect">
<option value="username">{{_("Username")}}</option>
@ -89,7 +89,7 @@
{% if grades|length > 0 %}
{% for lesson in lessons %}
<th class="points">
<a href="{{url('course_lesson_detail', course.slug, lesson.id)}}">
<a href="{{url('course_grades_lesson', course.slug, lesson.id)}}">
{{ lesson.title }}
<div class="point-denominator">{{lesson.points}}</div>
</a>
@ -111,9 +111,14 @@
</div>
</td>
{% for lesson in lessons %}
{% set val = grade.get(lesson.id) %}
<td class="partial-score">
<a href="{{url('course_lesson_detail', course.slug, lesson.id)}}?user={{student.username}}">
{{ grade[lesson.id]['percentage'] | floatformat(0) }}%
{% if val and val['total_points'] %}
{{ (val['achieved_points'] / val['total_points'] * lesson.points) | floatformat(0) }}
{% else %}
0
{% endif %}
</a>
</td>
{% endfor %}

View file

@ -0,0 +1,132 @@
{% extends "course/base.html" %}
{% block two_col_media %}
<style type="text/css">
table {
font-size: 15px;
}
td {
height: 2.5em;
}
.user-name {
padding-left: 1em !important;
}
#search-input {
float: right;
margin-bottom: 1em;
}
</style>
{% endblock %}
{% block js_media %}
<script>
$(document).ready(function(){
var $searchInput = $("#search-input");
var $usersTable = $("#users-table");
var tableBorderColor = $('#users-table td').css('border-bottom-color');
$searchInput.on("keyup", function() {
var value = $(this).val().toLowerCase();
$("#users-table tbody tr").filter(function() {
$(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
});
if(value) {
$('#users-table').css('border-bottom', '1px solid ' + tableBorderColor);
} else {
$('#users-table').css('border-bottom', '');
}
});
$('#sortSelect').select2({
minimumResultsForSearch: -1,
width: "10em",
});
$('#sortSelect').on('change', function() {
var rows = $('#users-table tbody tr').get();
var sortBy = $(this).val();
rows.sort(function(a, b) {
var keyA = $(a).find(sortBy === 'username' ? '.user-name' : 'td:last-child').text().trim();
var keyB = $(b).find(sortBy === 'username' ? '.user-name' : 'td:last-child').text().trim();
if(sortBy === 'total') {
// Convert percentage string to number for comparison
keyA = -parseFloat(keyA.replace('%', ''));
keyB = -parseFloat(keyB.replace('%', ''));
}
else {
keyA = keyA.toLowerCase();
keyB = keyB.toLowerCase();
}
if(keyA < keyB) return -1;
if(keyA > keyB) return 1;
return 0;
});
$.each(rows, function(index, row) {
$('#users-table tbody').append(row);
});
});
});
</script>
{% endblock %}
{% block middle_content %}
<center><h2>{{content_title}}</h2></center>
{% set lesson_problems = lesson.lesson_problems.order_by('order') %}
{{_("Sort by")}}:
<select id="sortSelect">
<option value="username">{{_("Username")}}</option>
<option value="total">{{_("Score")}}</option>
</select>
<input type="text" id="search-input" placeholder="{{_('Search')}}" autofocus>
<table class="table striped" id="users-table">
<thead>
<tr>
<th>{{_('Student')}}</th>
{% if grades|length > 0 %}
{% for lesson_problem in lesson_problems %}
<th class="points">
<a href="{{url('problem_detail', lesson_problem.problem.code)}}">
{{ lesson_problem.problem.name }}
<div class="point-denominator">{{lesson_problem.score}}</div>
</a>
</th>
{% endfor %}
{% endif %}
<th>{{_('Total')}}</th>
</tr>
</thead>
<tbody>
{% for student, grade in grades.items() %}
<tr>
<td class="user-name">
<div>
{{link_user(student)}}
</div>
<div>
{{student.first_name}}
</div>
</td>
{% for lesson_problem in lesson_problems %}
{% set val = grade.get(lesson_problem.problem.id) %}
<td class="partial-score">
<a href="{{url('user_submissions', lesson_problem.problem.code, student.username)}}">
{% if val and val['case_total'] %}
{{ (val['case_points'] / val['case_total'] * lesson_problem.score) | floatformat(0) }}
{% else %}
0
{% endif %}
</a>
</td>
{% endfor %}
<td style="font-weight: bold">
{{ grade['total']['percentage'] | floatformat(0) }}%
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View file

@ -6,14 +6,15 @@
{% endblock %}
{% block middle_content %}
<center><h2>{{title}}</h2></center>
<center><h2>{{title}} - {{profile.username}}</h2></center>
<h3 class="course-content-title">{{_("Content")}}</h3>
<div>
{{ lesson.content|markdown|reference|str|safe }}
</div>
<h3 class="course-content-title">{{_("Problems")}}</h3>
<ul class="course-problem-list">
{% for problem in lesson.problems.all() %}
{% for lesson_problem in lesson.lesson_problems.order_by('order') %}
{% set problem = lesson_problem.problem %}
<a href="{{url('problem_detail', problem.code)}}">
<li>
{% if problem.id in completed_problem_ids %}
@ -26,10 +27,10 @@
<span class="problem-name">{{problem.name}}</span>
{% set pp = problem_points[problem.id] %}
<span class="score">
{% if pp %}
{{pp.case_points|floatformat(1)}} / {{pp.case_total|floatformat(0)}}
{% if pp and pp.case_total %}
{{(pp.case_points / pp.case_total * lesson_problem.score) |floatformat(1)}} / {{lesson_problem.score|floatformat(0)}}
{% else %}
0
0 / {{lesson_problem.score|floatformat(0)}}
{% endif %}
</span>
</li>