Skip to content

Commit

Permalink
feat: Revamp the UX of exercise page (#382)
Browse files Browse the repository at this point in the history
* feat: Revamp the UX of exercise page
  • Loading branch information
yammesicka authored Mar 29, 2024
1 parent 314ddfc commit 15bf52b
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 79 deletions.
45 changes: 31 additions & 14 deletions lms/lmsdb/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,7 @@ class SolutionAssessment(BaseModel):
name = CharField()
icon = CharField(null=True)
color = CharField()
active_color = CharField()
active_color = CharField(default="#fff")
order = IntegerField(default=0, index=True)
course = ForeignKeyField(Course, backref='assessments')

Expand Down Expand Up @@ -718,6 +718,19 @@ def ordered_versions(self) -> Iterable['Solution']:
def test_results(self) -> Iterable[dict]:
return SolutionExerciseTestExecution.by_solution(self)

@staticmethod
def _get_summary(solution: 'Solution') -> dict:
exercise = {}
exercise['solution_id'] = solution.id
exercise['is_checked'] = solution.is_checked
exercise['comments_num'] = len(solution.staff_comments)
if solution.is_checked and solution.checker:
exercise['checker'] = solution.checker.fullname
if solution.assessment:
exercise['assessment'] = solution.assessment.name
exercise['grade_color'] = solution.assessment.color
return exercise

@classmethod
def of_user(
cls, user_id: int, with_archived: bool = False,
Expand All @@ -737,15 +750,10 @@ def of_user(
.order_by(cls.submission_timestamp.desc())
)
for solution in solutions:
exercise = exercises[solution.exercise_id]
if exercise.get('solution_id') is None:
exercise['solution_id'] = solution.id
exercise['is_checked'] = solution.is_checked
exercise['comments_num'] = len(solution.staff_comments)
if solution.is_checked and solution.checker:
exercise['checker'] = solution.checker.fullname
if solution.assessment:
exercise['assessment'] = solution.assessment.name
id_ = solution.exercise_id
if exercises[id_].get('solution_id') is None:
exercises[id_].update(cls._get_summary(solution))

return tuple(exercises.values())

@property
Expand Down Expand Up @@ -1244,11 +1252,20 @@ def create_basic_roles() -> None:

def create_basic_assessments() -> None:
assessments_dict = {
'Excellent': {'color': 'green', 'icon': 'star', 'order': 1},
'Nice': {'color': 'blue', 'icon': 'check', 'order': 2},
'Try again': {'color': 'red', 'icon': 'exclamation', 'order': 3},
'Excellent': {
'color': 'green', 'icon': 'star', 'order': 1,
},
'Nice': {
'color': 'blue', 'icon': 'check', 'order': 2,
},
'Try again': {
'color': 'red', 'icon': 'exclamation', 'order': 3,
},
'Invalid': {
'color': 'red', 'icon': 'ban', 'order': 4,
},
'Plagiarism': {
'color': 'black', 'icon': 'exclamation-triangle', 'order': 4,
'color': 'black', 'icon': 'exclamation-triangle', 'order': 5,
},
}
courses = Course.select()
Expand Down
142 changes: 87 additions & 55 deletions lms/static/my.css
Original file line number Diff line number Diff line change
Expand Up @@ -124,16 +124,18 @@ a {

.exercise {
align-items: center;
background: #f8f8f8;
border-radius: 5px;
border: 1px solid;
background: #f8f9fa;
border-radius: 10px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: row;
justify-content: space-between;
margin-bottom: 0.5rem;
padding: 1rem;
text-align: center;
margin-bottom: 1rem;
overflow: auto;
padding: 0.75rem;
text-align: center;
border-right: #00000000 8px solid;
border-left: #00000000 8px solid;
}

.centered {
Expand All @@ -146,26 +148,34 @@ a {
align-items: stretch;
}

.right-side {
justify-content: start;
}

.left-side {
justify-content: end;
}

.exercise-number {
align-items: center;
border-radius: 100%;
border: 1px solid;
display: flex;
height: 3rem;
justify-content: center;
overflow: hidden;
text-align: center;
width: 3rem;
font-size: 1.2rem;
margin-right: 1rem;
color: #495057;
padding: 0.25rem 0.75rem;
border-radius: 15px;
min-width: 4rem;
justify-content: start;
}

.exercise-name {
align-items: center;
display: flex;
font-size: 1.5em;
font-size: 1.4rem;
line-height: 1.3;
height: 3rem;
justify-content: center;
text-align: center;
Expand Down Expand Up @@ -224,65 +234,62 @@ button#share-action:hover {
border-bottom-right-radius: 0;
}

.go-send {
background: #247ba0;
color: #ebf3f6;
}

.go-send:hover {
color: #ebf3f6;
}

.go-view {
background: #ffe066;
color: #18150a;
}

.go-view:hover {
color: #18150a;
#back-to-exercises {
width: 100%;
margin-top: 5vh;
}

.go-checked {
background: #70c1b3;
color: #0b1211;
.our-button {
align-items: center;
background-color: #ffffff;
border-radius: 20px;
border: 1px solid #dee2e6;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
color: #495057;
cursor: pointer;
display: flex;
font-size: 0.9rem;
font-weight: 500;
justify-content: space-between;
margin: 0.5em;
width: 8em;
padding: 0.55rem 1rem;
text-align: center;
transition: background-color 0.2s, transform 0.2s, box-shadow 0.2s;
}

.go-checked:hover {
color: #0b1211;
.our-button:hover {
background-color: #0d6efd;
color: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.15);
transform: translateY(-2px);
}

.go-grader {
background: #b2dbbf;
color: #1f2d3d;
.our-button:active {
transform: translateY(1px);
box-shadow: 0 1px 2px rgba(0,0,0,0.15);
}

.go-grader:hover {
color: #1f2d3d;
.our-button.active, .our-button:checked {
background-color: #28a745;
color: white;
}

#back-to-exercises {
width: 100%;
margin-top: 5vh;
.our-button.active:hover, .our-button:checked:hover {
background-color: #218838; /* Slightly darker green on hover */
}

.our-button {
border-radius: 8px;
display: flex;
margin-right: 0.5em;
outline: none;
padding: 0.55rem 1rem;
white-space: nowrap;
justify-content: space-between;
align-items: center;
height: 3em;
width: 8em;
@keyframes pop {
0% { transform: scale(0.9); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}

.our-button:hover {
text-decoration: none;
.our-button:active {
animation: pop 0.5s;
}

.our-button-narrow {
button.our-button-narrow, a.our-button-narrow {
width: 2em;
display: flex;
align-items: center;
Expand Down Expand Up @@ -887,19 +894,44 @@ code .grader-add .fa {
}

.which-notebook {
align-items: center;
align-self: center;
display: flex;
margin-inline-end: 1rem;
}

.fa-book {
color: #495057;
font-size: 1rem;
margin-inline-end: 0.25rem;
vertical-align: middle;
}

.comments-count {
display: flex;
align-items: center;
font-size: 0.9rem;
color: #495057;
display: flex;
align-self: center;
align-items: center;
font-size: 1.25rem;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
margin-inline-end: 1rem;
}

.comments-count .our-badge {
background-color: #e9ecef; /* Neutral background */
color: #495057; /* Dark text color for readability */
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
border-radius: 15px;
display: inline-flex;
align-items: center;
justify-content: center;
margin-inline-end: 0.5rem;
box-shadow: none; /* Remove the shadow if you prefer a flat design */
}

#courses-list {
overflow-y: auto;
max-height: 10em;
Expand Down
11 changes: 1 addition & 10 deletions lms/templates/exercises.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,12 @@ <h1 id="exercises-head">{{ _('Exercises') }}</h1>
</div>
<div id="exercises">
{%- for exercise in exercises %}
<div class="exercise">
<div class="exercise" {%- if exercise.grade_color %} style="border-right: 8px solid {{ exercise.grade_color }};" {%- endif -%}>
<div class="right-side {{ direction }}-language">
<div class="exercise-number me-3">{{ exercise['exercise_number'] }}</div>
<div class="exercise-name"><div class="ex-title">{{ exercise['exercise_name'] | e }}</div></div>
</div>
<div class="left-side">
<div class="comments-count">
<span class="badge bg-secondary">{{ exercise['comments_num'] }}</span>
<span class="visually-hidden">{{ _("Comments for the solution") }}</span>
</div>
{%- if exercise['notebook'] %}
<div class="which-notebook">
<i class="fa fa-book" aria-hidden="true"></i>
Expand All @@ -39,11 +35,6 @@ <h1 id="exercises-head">{{ _('Exercises') }}</h1>
<i class="fa fa-{{ details['icon'] }}" aria-hidden="true"></i>
</a>
{% endif -%}
{% if is_manager %}
<a class="our-button our-button-narrow go-grader" href="check/{{ exercise['exercise_id'] }}">
<i class="fa fa-graduation-cap" aria-hidden="true"></i>
</a>
{% endif %}
</div>
</div>
{% endfor -%}
Expand Down
12 changes: 12 additions & 0 deletions tests/test_solutions.py
Original file line number Diff line number Diff line change
Expand Up @@ -686,3 +686,15 @@ def test_solutions_of_user(
exercises = solution.of_user(student_user.id, from_all_courses=True)
assert exercises[0].get('assessment') is None
assert exercises[1].get('assessment') == 'Try again'

@staticmethod
def test_get_solution_summary(solution: Solution, staff_user: User):
summary = Solution._get_summary(solution)
assert summary.get('assessment') is None
assert summary['solution_id'] == solution.id
assert not summary['is_checked']

solution.mark_as_checked(staff_user)
solution = Solution.get_by_id(solution.id)
summary = Solution._get_summary(solution)
assert summary['is_checked']

0 comments on commit 15bf52b

Please sign in to comment.