Skip to content

Commit 4d4755d

Browse files
committed
fixed pre_save colors
1 parent f4f0b2d commit 4d4755d

File tree

13 files changed

+80
-60
lines changed

13 files changed

+80
-60
lines changed

lms/lmsdb/bootstrap.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,17 +253,17 @@ def _uuid_migration() -> bool:
253253
return True
254254

255255

256-
def _evaluation_migration() -> bool:
256+
def _assessment_migration() -> bool:
257257
Solution = models.Solution
258-
_add_not_null_column(Solution, Solution.evaluation)
258+
_add_not_null_column(Solution, Solution.assessment)
259259
return True
260260

261261

262262
def main():
263263
with models.database.connection_context():
264264
if models.database.table_exists(models.Solution.__name__.lower()):
265265
_last_status_view_migration()
266-
_evaluation_migration()
266+
_assessment_migration()
267267

268268
if models.database.table_exists(models.User.__name__.lower()):
269269
_api_keys_migration()
@@ -275,8 +275,8 @@ def main():
275275
models.create_basic_roles()
276276
if models.User.select().count() == 0:
277277
models.create_demo_users()
278-
if models.SolutionEvaluation.select().count() == 0:
279-
models.create_basic_evaluations()
278+
if models.SolutionAssessment.select().count() == 0:
279+
models.create_basic_assessments()
280280

281281
text_fixer.fix_texts()
282282
import_tests.load_tests_from_path('/app_dir/notebooks-tests')

lms/lmsdb/models.py

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
from lms.lmsdb import database_config
2525
from lms.models.errors import AlreadyExists
2626
from lms.utils import hashing
27-
from lms.utils.consts import COLORS
27+
from lms.utils.colors import get_hex_color
28+
from lms.utils.consts import DEFAULT_ACTIVE_COLOR, DEFAULT_COLOR
2829
from lms.utils.log import log
2930

3031

@@ -366,32 +367,34 @@ def to_choices(cls: enum.EnumMeta) -> Tuple[Tuple[str, str], ...]:
366367
return tuple((choice.name, choice.value) for choice in choices)
367368

368369

369-
class SolutionEvaluation(BaseModel):
370+
class SolutionAssessment(BaseModel):
370371
name = CharField()
371372
icon = CharField(null=True)
372373
color = CharField()
373374
active_color = CharField()
374375
order = IntegerField(default=0, index=True, unique=True)
375376

376377
@classmethod
377-
def get_evaluations(cls):
378+
def get_assessments(cls):
378379
return cls.select().order_by(cls.order)
379380

380381
def __str__(self):
381382
return self.name
382383

383384

384-
@pre_save(sender=SolutionEvaluation)
385-
def evaluation_on_save_handler(_model_class, instance, created):
385+
@pre_save(sender=SolutionAssessment)
386+
def assessment_on_save_handler(_model_class, instance, created):
386387
"""Change colors to hex."""
387388

388-
is_color_changed = not instance.color.startswith('#')
389-
if created or is_color_changed:
390-
instance.color = COLORS.get(instance.color, '#0d6efd')
389+
try:
390+
instance.color = get_hex_color(instance.color)
391+
except ValueError:
392+
instance.color = DEFAULT_COLOR
391393

392-
is_active_color_changed = not instance.active_color.startswith('#')
393-
if created or is_active_color_changed:
394-
instance.active_color = COLORS.get(instance.active_color, '#fff')
394+
try:
395+
instance.active_color = get_hex_color(instance.active_color)
396+
except ValueError:
397+
instance.active_color = DEFAULT_ACTIVE_COLOR
395398

396399

397400
class Solution(BaseModel):
@@ -418,8 +421,8 @@ class Solution(BaseModel):
418421
index=True,
419422
)
420423
last_time_view = DateTimeField(default=datetime.now, null=True, index=True)
421-
evaluation = ForeignKeyField(
422-
SolutionEvaluation, backref='solutions', null=True,
424+
assessment = ForeignKeyField(
425+
SolutionAssessment, backref='solutions', null=True,
423426
)
424427

425428
@property
@@ -481,14 +484,14 @@ def start_checking(self) -> bool:
481484

482485
def set_state(
483486
self, new_state: SolutionState,
484-
evaluation: Optional[SolutionEvaluation] = None, **kwargs,
487+
assessment: Optional[SolutionAssessment] = None, **kwargs,
485488
) -> bool:
486489
# Optional: filter the old state of the object
487490
# to make sure that no two processes set the state together
488491
requested_solution = (Solution.id == self.id)
489492
updates_dict = {Solution.state.name: new_state.name}
490-
if evaluation is not None:
491-
updates_dict[Solution.evaluation.name] = evaluation
493+
if assessment is not None:
494+
updates_dict[Solution.assessment.name] = assessment
492495
changes = Solution.update(
493496
**updates_dict, **kwargs,
494497
).where(requested_solution)
@@ -513,7 +516,7 @@ def of_user(
513516
solutions = (
514517
cls
515518
.select(
516-
cls.exercise, cls.id, cls.state, cls.checker, cls.evaluation,
519+
cls.exercise, cls.id, cls.state, cls.checker, cls.assessment,
517520
)
518521
.where(cls.exercise.in_(db_exercises), cls.solver == user_id)
519522
.order_by(cls.submission_timestamp.desc())
@@ -526,8 +529,8 @@ def of_user(
526529
exercise['comments_num'] = len(solution.staff_comments)
527530
if solution.is_checked and solution.checker:
528531
exercise['checker'] = solution.checker.fullname
529-
if solution.evaluation:
530-
exercise['evaluation'] = solution.evaluation.name
532+
if solution.assessment:
533+
exercise['assessment'] = solution.assessment.name
531534
return tuple(exercises.values())
532535

533536
@property
@@ -630,13 +633,13 @@ def _base_next_unchecked(cls):
630633

631634
def mark_as_checked(
632635
self,
633-
evaluation_id: Optional[int] = None,
636+
assessment_id: Optional[int] = None,
634637
by: Optional[Union[User, int]] = None,
635638
) -> bool:
636639
return self.set_state(
637640
Solution.STATES.DONE,
638-
SolutionEvaluation.get_or_none(
639-
SolutionEvaluation.id == evaluation_id,
641+
SolutionAssessment.get_or_none(
642+
SolutionAssessment.id == assessment_id,
640643
),
641644
checker=by,
642645
)
@@ -1014,17 +1017,17 @@ def create_basic_roles():
10141017
Role.create(name=role.value)
10151018

10161019

1017-
def create_basic_evaluations():
1018-
evaluations_dict = {
1020+
def create_basic_assessments():
1021+
assessments_dict = {
10191022
'Excellent': {'color': 'green', 'icon': 'star', 'order': 1},
10201023
'Nice': {'color': 'blue', 'icon': 'check', 'order': 2},
10211024
'Try again': {'color': 'red', 'icon': 'exclamation', 'order': 3},
10221025
'Plagiarism': {
10231026
'color': 'black', 'icon': 'exclamation-triangle', 'order': 4,
10241027
},
10251028
}
1026-
for name, values in evaluations_dict.items():
1027-
SolutionEvaluation.create(
1029+
for name, values in assessments_dict.items():
1030+
SolutionAssessment.create(
10281031
name=name, icon=values.get('icon'), color=values.get('color'),
10291032
active_color='white', order=values.get('order'),
10301033
)

lms/lmsweb/translations/he/LC_MESSAGES/messages.po

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ msgid ""
77
msgstr ""
88
"Project-Id-Version: 1.0\n"
99
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
10-
"POT-Creation-Date: 2021-10-02 12:46+0300\n"
10+
"POT-Creation-Date: 2021-10-03 09:13+0300\n"
1111
"PO-Revision-Date: 2021-09-29 11:30+0300\n"
1212
"Last-Translator: Or Ronai\n"
1313
"Language: he\n"
@@ -18,7 +18,7 @@ msgstr ""
1818
"Content-Transfer-Encoding: 8bit\n"
1919
"Generated-By: Babel 2.9.1\n"
2020

21-
#: lmsdb/models.py:764
21+
#: lmsdb/models.py:781
2222
msgid "Fatal error"
2323
msgstr "כישלון חמור"
2424

@@ -418,7 +418,7 @@ msgid "It's important for us that all exercises will be checked by human eye."
418418
msgstr "חשוב לנו שכל תרגיל יעבור בדיקה של עין אנושית."
419419

420420
#: templates/view.html:18
421-
msgid "Presenter"
421+
msgid "Solver"
422422
msgstr "מגיש"
423423

424424
#: templates/view.html:24

lms/lmsweb/views.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -614,11 +614,11 @@ def shared_solution(shared_url: str, file_id: Optional[int] = None):
614614
@managers_only
615615
def done_checking(exercise_id, solution_id):
616616
if request.method == 'POST':
617-
evaluation_id = request.json.get('evaluation')
617+
assessment_id = request.json.get('assessment')
618618
else: # it's a GET
619-
evaluation_id = request.args.get('evaluation')
619+
assessment_id = request.args.get('assessment')
620620
is_updated = solutions.mark_as_checked(
621-
solution_id, current_user.id, evaluation_id,
621+
solution_id, current_user.id, assessment_id,
622622
)
623623
next_solution = solutions.get_next_unchecked(exercise_id)
624624
next_solution_id = getattr(next_solution, 'id', None)

lms/models/solutions.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from lms.extractors.base import File
1111
from lms.lmsdb.models import (
12-
SharedSolution, Solution, SolutionEvaluation, SolutionFile, User,
12+
SharedSolution, Solution, SolutionAssessment, SolutionFile, User,
1313
)
1414
from lms.lmstests.public.general import tasks as general_tasks
1515
from lms.lmstests.public.identical_tests import tasks as identical_tests_tasks
@@ -66,11 +66,11 @@ def get_message_and_addressee(
6666

6767

6868
def mark_as_checked(
69-
solution_id: int, checker_id: int, evaluation_id: Optional[int] = None,
69+
solution_id: int, checker_id: int, assessment_id: Optional[int] = None,
7070
) -> bool:
7171
checked_solution: Solution = Solution.get_by_id(solution_id)
7272
is_updated = checked_solution.mark_as_checked(
73-
evaluation_id=evaluation_id, by=checker_id,
73+
assessment_id=assessment_id, by=checker_id,
7474
)
7575
msg = _(
7676
'Your solution for the "%(subject)s" exercise has been checked.',
@@ -142,7 +142,7 @@ def get_view_parameters(
142142
'user_comments':
143143
comments._common_comments(user_id=current_user.id),
144144
'left': Solution.left_in_exercise(solution.exercise),
145-
'evaluations': SolutionEvaluation.get_evaluations(),
145+
'assessments': SolutionAssessment.get_assessments(),
146146
}
147147

148148
if viewer_is_solver:

lms/static/checker.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
function trackFinished(exerciseId, solutionId, element) {
22
element.addEventListener('click', () => {
3-
const evaluation = document.querySelector('input[name="evaluation"]:checked');
4-
const evaluationValue = evaluation.value;
3+
const assessment = document.querySelector('input[name="assessment"]:checked');
4+
const assessmentValue = assessment.value;
55
const xhr = new XMLHttpRequest();
66
xhr.open('POST', `/checked/${exerciseId}/${solutionId}`, true);
77
xhr.setRequestHeader('Content-Type', 'application/json');
@@ -21,7 +21,7 @@ function trackFinished(exerciseId, solutionId, element) {
2121
};
2222

2323
xhr.send(JSON.stringify({
24-
evaluation: evaluationValue,
24+
assessment: assessmentValue,
2525
}));
2626
});
2727
}

lms/static/my.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ code {
616616
display: block;
617617
}
618618

619-
#solution-evaluation {
619+
#solution-assessment {
620620
display: flex;
621621
}
622622

lms/templates/user.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ <h2>{{ _('Exercises Submitted') }}:</h2>
4343
</td>
4444
<td><a href="/view/{{ solution.solution_id }}">{{ solution.solution_id }}</a></td>
4545
<td>{{ solution.get('checker', '') | e }}</a></td>
46-
<td>{{ solution.get('evaluation', '') | e }}</td>
46+
<td>{{ solution.get('assessment', '') | e }}</td>
4747
</tr>
4848
{% endfor -%}
4949
</tbody>

lms/templates/view.html

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ <h1>{{ _('Exercise view') }} {{ solution['exercise']['id'] }}: {{ solution['exer
1515
<p class="unchecked-msg {{ direction }}"><strong>{{ _("Your solution hasn't been checked.") }}</strong> {{ _("It's important for us that all exercises will be checked by human eye.") }}</p>
1616
{% endif %}
1717
{% else %}
18-
<p id="solver"><strong>{{ _('Presenter') }}:</strong> <a href="/user/{{ solution['solver']['id'] }}">{{ solution['solver']['fullname'] | e }}</a></p>
18+
<p id="solver"><strong>{{ _('Solver') }}:</strong> <a href="/user/{{ solution['solver']['id'] }}">{{ solution['solver']['fullname'] | e }}</a></p>
1919
{% endif %}
20-
{% if solution.evaluation and not shared_url %}
21-
<p id="evaluation"><strong>{{ _('Verbal note') }}:</strong>{% if solution.evaluation.icon %} <i class="fa fa-{{ solution.evaluation.icon | e }}"></i>{% endif %} {{ solution.evaluation.name | e }}</p>
20+
{% if solution.assessment and not shared_url %}
21+
<p id="assessment"><strong>{{ _('Verbal note') }}:</strong>{% if solution.assessment.icon %} <i class="fa fa-{{ solution.assessment.icon | e }}"></i>{% endif %} {{ solution.assessment.name | e }}</p>
2222
{% endif %}
2323
{% if not shared_url %}
2424
<nav id="versions" aria-label="{{ _('Navigate in solution versions') }}">
@@ -100,12 +100,12 @@ <h5 class="test-name">
100100
</div>
101101
{% if is_manager and not shared_url %}
102102
<div id="popular-comments">
103-
<div id="exercise-evaluation" class="{{ direction }}">
103+
<div id="exercise-assessment" class="{{ direction }}">
104104
<h2>{{ _('Verbal note') }}</h2>
105-
<div class="btn-group-vertical" id="solution-evaluation" role="group" aria-label="evaluations radio toggle button group">
106-
{% for evaluation in evaluations %}
107-
<input type="radio" class="btn-check" name="evaluation" id="btnradio{{ loop.index }}" autocomplete="off" value="{{ evaluation.id }}" {%- if solution.evaluation.name == evaluation.name or loop.first %}checked{% endif -%}>
108-
<label class="btn btn-outline" style="--color: {{ evaluation.color }}; --active-color: {{ evaluation.active_color }};" for="btnradio{{ loop.index }}">{% if evaluation.icon %}<i class="fa fa-{{ evaluation.icon }}"></i> {% endif %}{{ evaluation.name }}</label>
105+
<div class="btn-group-vertical" id="solution-assessment" role="group" aria-label="assessments radio toggle button group">
106+
{% for assessment in assessments %}
107+
<input type="radio" class="btn-check" name="assessment" id="btnradio{{ loop.index }}" autocomplete="off" value="{{ assessment.id }}" {%- if solution.assessment.name == assessment.name or loop.first %}checked{% endif -%}>
108+
<label class="btn btn-outline" style="--color: {{ assessment.color }}; --active-color: {{ assessment.active_color }};" for="btnradio{{ loop.index }}">{% if assessment.icon %}<i class="fa fa-{{ assessment.icon }}"></i> {% endif %}{{ assessment.name }}</label>
109109
{% endfor %}
110110
</div>
111111
</div>

lms/utils/colors.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import re
2+
from typing import Optional
3+
4+
from lms.utils.consts import COLORS
5+
6+
7+
HEX_COLOR = re.compile(r'(?P<hex>#[a-f0-9]{6}|#[a-f0-9]{3})')
8+
9+
10+
def get_hex_color(number: str) -> Optional[str]:
11+
if color := HEX_COLOR.match(number):
12+
return color.groupdict()['hex']
13+
elif color := COLORS.get(number):
14+
return color
15+
raise ValueError('This is not a valid hex color')

lms/utils/consts.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@
1313
'teal': '#20c997', 'orange': '#fd7e14', 'pink': '#d63384',
1414
'purple': '#6f42c1', 'indigo': '#6610f2', 'light': '#f8f9fa',
1515
}
16+
DEFAULT_COLOR = '#0d6efd' # primary
17+
DEFAULT_ACTIVE_COLOR = '#fff' # white

tests/conftest.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
from lms.lmsdb.models import (
1818
ALL_MODELS, Comment, CommentText, Exercise, Note, Notification, Role,
19-
RoleOptions, SharedSolution, Solution, SolutionEvaluation, User,
19+
RoleOptions, SharedSolution, Solution, SolutionAssessment, User,
2020
)
2121
from lms.extractors.base import File
2222
from lms.lmstests.public import celery_app as public_app
@@ -46,17 +46,17 @@ def populate_roles():
4646

4747

4848
@pytest.fixture(autouse=True, scope='session')
49-
def populate_evaluations():
50-
evaluations_dict = {
49+
def populate_assessments():
50+
assessments_dict = {
5151
'Excellent': {'color': 'green', 'icon': 'star', 'order': 1},
5252
'Nice': {'color': 'blue', 'icon': 'check', 'order': 2},
5353
'Try again': {'color': 'red', 'icon': 'exclamation', 'order': 3},
5454
'Plagiarism': {
5555
'color': 'black', 'icon': 'exclamation-triangle', 'order': 4,
5656
},
5757
}
58-
for name, values in evaluations_dict.items():
59-
SolutionEvaluation.create(
58+
for name, values in assessments_dict.items():
59+
SolutionAssessment.create(
6060
name=name, icon=values.get('icon'), color=values.get('color'),
6161
active_color='white', order=values.get('order'),
6262
)

tests/test_solutions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ def test_done_checking(
561561
client = conftest.get_logged_user(staff_user.username)
562562
response = client.post(
563563
f'/checked/{solution.exercise.id}/{solution.id}',
564-
data=json.dumps({'evaluation': 1}),
564+
data=json.dumps({'assessment': 1}),
565565
content_type='application/json',
566566
)
567567
assert response.status_code == 200

0 commit comments

Comments
 (0)