Skip to content

Commit 9499e89

Browse files
authored
feature: Per course status page (#348)
* feature: Per course status page Also, lots of small improvements: - Hebrew strings fixes - Removing cluttered suggestions from views.py - Better migration setup - create tables that doesn't exist first - Tests for the status page
1 parent 8d8c41f commit 9499e89

File tree

9 files changed

+170
-94
lines changed

9 files changed

+170
-94
lines changed

Diff for: devops/bootstrap.sh

+17-9
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,22 @@ SCRIPT_FILE_PATH=$(readlink -f "${0}")
44
SCRIPT_FOLDER=$(dirname "${SCRIPT_FILE_PATH}")
55
MAIN_FOLDER="${SCRIPT_FOLDER}/../lms"
66

7-
docker exec -i lms_rabbitmq_1 rabbitmqctl add_vhost lmstests-public
8-
docker exec -i lms_rabbitmq_1 rabbitmqctl add_vhost lmstests-sandbox
9-
docker exec -i lms_rabbitmq_1 rabbitmqctl set_permissions -p lmstests-public rabbit-user ".*" ".*" ".*"
10-
docker exec -i lms_rabbitmq_1 rabbitmqctl set_permissions -p lmstests-sandbox rabbit-user ".*" ".*" ".*"
11-
docker exec -i lms_http_1 python lmsdb/bootstrap.py
7+
# Starting from docker-compose 2,
8+
# names are network-image-id instead of network_image_id
9+
if docker-compose --version | grep -q 'version 2'; then
10+
SEP="-"
11+
else
12+
SEP="_"
13+
fi
14+
15+
docker exec -i lms${SEP}rabbitmq${SEP}1 rabbitmqctl add_vhost lmstests-public
16+
docker exec -i lms${SEP}rabbitmq${SEP}1 rabbitmqctl add_vhost lmstests-sandbox
17+
docker exec -i lms${SEP}rabbitmq${SEP}1 rabbitmqctl set_permissions -p lmstests-public rabbit-user ".*" ".*" ".*"
18+
docker exec -i lms${SEP}rabbitmq${SEP}1 rabbitmqctl set_permissions -p lmstests-sandbox rabbit-user ".*" ".*" ".*"
19+
docker exec -i lms${SEP}http${SEP}1 python lmsdb/bootstrap.py
1220

1321
# build the image for docker inside a docker!
14-
docker exec lms_checks-docker-engine_1 mkdir -p /home/lms
15-
docker cp "${MAIN_FOLDER}"/lmstests/public/unittests/image/requirements.txt lms_checks-docker-engine_1:/home/lms/.
16-
docker cp "${MAIN_FOLDER}"/lmstests/public/unittests/image/Dockerfile lms_checks-docker-engine_1:/home/lms/.
17-
docker exec lms_checks-docker-engine_1 sh -c "cd /home/lms && docker build -t lms ."
22+
docker exec lms${SEP}checks-docker-engine${SEP}1 mkdir -p /home/lms
23+
docker cp "${MAIN_FOLDER}"/lmstests/public/unittests/image/requirements.txt lms${SEP}checks-docker-engine${SEP}1:/home/lms/.
24+
docker cp "${MAIN_FOLDER}"/lmstests/public/unittests/image/Dockerfile lms${SEP}checks-docker-engine${SEP}1:/home/lms/.
25+
docker exec lms${SEP}checks-docker-engine${SEP}1 sh -c "cd /home/lms && docker build -t lms ."

Diff for: lms/lmsdb/bootstrap.py

+24-13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Callable, Optional, Tuple, Type
1+
from typing import Any, Callable, Iterable, List, Optional, Tuple, Type, Union
22
from uuid import uuid4
33

44
from peewee import (
@@ -322,24 +322,35 @@ def _assessment_migration() -> bool:
322322
return True
323323

324324

325+
def is_tables_exists(tables: Union[Model, Iterable[Model]]) -> bool:
326+
if not isinstance(tables, (tuple, list)):
327+
tables = (tables,)
328+
329+
return all(
330+
models.database.table_exists(table.__name__.lower())
331+
for table in tables
332+
)
333+
334+
335+
def get_new_tables(tables: Iterable[Model]) -> List[Model]:
336+
return [table for table in tables if not is_tables_exists(table)]
337+
338+
325339
def main():
326340
with models.database.connection_context():
327-
if models.database.table_exists(models.Exercise.__name__.lower()):
328-
_add_exercise_course_id_and_number_columns_constraint()
341+
new_tables = get_new_tables(models.ALL_MODELS)
342+
models.database.create_tables(new_tables, safe=True)
329343

330-
if models.database.table_exists(models.Solution.__name__.lower()):
331-
_last_status_view_migration()
332-
_assessment_migration()
344+
_add_exercise_course_id_and_number_columns_constraint()
333345

334-
if models.database.table_exists(models.User.__name__.lower()):
335-
_api_keys_migration()
336-
_last_course_viewed_migration()
337-
_uuid_migration()
346+
_last_status_view_migration()
347+
_assessment_migration()
338348

339-
if models.database.table_exists(models.UserCourse.__name__.lower()):
340-
_add_user_course_constaint()
349+
_api_keys_migration()
350+
_last_course_viewed_migration()
351+
_uuid_migration()
341352

342-
models.database.create_tables(models.ALL_MODELS, safe=True)
353+
_add_user_course_constaint()
343354

344355
models.create_basic_roles()
345356
if models.User.select().count() == 0:

Diff for: lms/lmsdb/models.py

+10-7
Original file line numberDiff line numberDiff line change
@@ -798,26 +798,29 @@ def next_unchecked_of(cls, exercise_id) -> Optional['Solution']:
798798
return None
799799

800800
@classmethod
801-
def status(cls):
801+
def status(cls, course_id: Optional[int] = None):
802802
one_if_is_checked = Case(
803803
Solution.state, ((Solution.STATES.DONE.name, 1),), 0,
804804
)
805805
fields = (
806806
Exercise.id,
807+
Exercise.course,
807808
Exercise.subject.alias('name'),
808809
Exercise.is_archived.alias('is_archived'),
809810
fn.Count(Solution.id).alias('submitted'),
810811
fn.Sum(one_if_is_checked).alias('checked'),
811812
)
812-
join_by_exercise = (Solution.exercise == Exercise.id)
813-
active_solutions = Solution.state.in_(
814-
Solution.STATES.active_solutions(),
815-
)
813+
active_solution_states = Solution.STATES.active_solutions()
814+
active_solutions = Solution.state.in_(active_solution_states)
815+
right_course = (course_id is None) or course_id == Course.id
816+
816817
return (
817818
Exercise
818819
.select(*fields)
819-
.join(Solution, JOIN.LEFT_OUTER, on=join_by_exercise)
820-
.where(active_solutions)
820+
.join(Course, on=(Course.id == Exercise.course))
821+
.switch()
822+
.join(Solution, on=(Solution.exercise == Exercise.id))
823+
.where(active_solutions & right_course)
821824
.group_by(Exercise.subject, Exercise.id)
822825
.order_by(Exercise.id)
823826
)

Diff for: lms/lmsweb/translations/he/LC_MESSAGES/messages.po

+31-31
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,20 @@
55
#
66
msgid ""
77
msgstr ""
8-
"Project-Id-Version: 1.0\n"
8+
"Project-Id-Version: 1.0\n"
99
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
1010
"POT-Creation-Date: 2021-10-15 14:37+0300\n"
11-
"PO-Revision-Date: 2021-09-29 11:30+0300\n"
11+
"PO-Revision-Date: 2021-10-17 01:59+0300\n"
1212
"Last-Translator: Or Ronai\n"
1313
"Language: he\n"
1414
"Language-Team: he <[email protected]>\n"
15-
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
15+
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
1616
"MIME-Version: 1.0\n"
1717
"Content-Type: text/plain; charset=utf-8\n"
1818
"Content-Transfer-Encoding: 8bit\n"
1919
"Generated-By: Babel 2.9.1\n"
20+
"X-Generator: Poedit 3.0\n"
21+
"X-Poedit-Bookmarks: -1,-1,-1,-1,0,-1,-1,-1,-1,-1\n"
2022

2123
#: lmsdb/models.py:921
2224
msgid "Fatal error"
@@ -39,8 +41,8 @@ msgstr "הבודק האוטומטי לא הצליח להריץ את הקוד ש
3941
#: lmstests/public/unittests/services.py:120
4042
#, python-format
4143
msgid ""
42-
"The automatic checker failed in %(number)d examples in your "
43-
"\"%(subject)s\" solution."
44+
"The automatic checker failed in %(number)d examples in your \"%(subject)s\" "
45+
"solution."
4446
msgstr "הבודק האוטומטי נכשל ב־ %(number)d דוגמאות בתרגיל \"%(subject)s\"."
4547

4648
#: lmstests/public/unittests/services.py:142
@@ -73,12 +75,12 @@ msgstr "קישור לאיפוס הסיסמה נשלח בהצלחה"
7375

7476
#: lmsweb/views.py:246
7577
msgid "Reset password link is expired"
76-
msgstr "קישור איפוס הסיסמה פג תוקף"
78+
msgstr "הקישור לאיפוס הסיסמה פג תוקף"
7779

7880
#: lmsweb/forms/change_password.py:20 lmsweb/forms/register.py:32
7981
#: lmsweb/forms/reset_password.py:25
8082
msgid "The passwords are not identical"
81-
msgstr "הסיסמאות שהוקלדו אינן זהות"
83+
msgstr "הסיסמאות שהוזנו אינן זהות"
8284

8385
#: lmsweb/forms/change_password.py:31
8486
msgid "Invalid old password has been inserted too many times"
@@ -91,15 +93,15 @@ msgstr "הסיסמה הנוכחית שהוזנה שגויה"
9193
#: lmsweb/forms/register.py:14 lmsweb/forms/reset_password.py:12
9294
#: lmsweb/tools/validators.py:29
9395
msgid "Invalid email"
94-
msgstr "אימייל לא תקין"
96+
msgstr "כתובת הדואר האלקטרוני שהוזנה אינה תקינה"
9597

9698
#: lmsweb/tools/validators.py:13
9799
msgid "The username is already in use"
98-
msgstr "שם המשתמש כבר נמצא בשימוש"
100+
msgstr "שם המשתמש הזה כבר נמצא בשימוש"
99101

100102
#: lmsweb/tools/validators.py:21
101103
msgid "The email is already in use"
102-
msgstr "האימייל כבר נמצא בשימוש"
104+
msgstr "כתובת הדואר האלקטרוני הזו כבר נמצאת בשימוש"
103105

104106
#: models/solutions.py:52
105107
#, python-format
@@ -267,7 +269,7 @@ msgstr "הזינו סיסמה לצורך שינוי הסיסמה:"
267269

268270
#: templates/reset-password.html:11
269271
msgid "Insert your email for getting link to reset it:"
270-
msgstr "הזינו אימייל לצורך שליחת קישור לאיפוס הסיסמה:"
272+
msgstr "הזינו מייל לצורך שליחת קישור לאיפוס הסיסמה:"
271273

272274
#: templates/reset-password.html:14 templates/signup.html:15
273275
#: templates/user.html:12
@@ -288,7 +290,7 @@ msgstr "הרשמה"
288290

289291
#: templates/signup.html:12
290292
msgid "Insert your email and password for registration:"
291-
msgstr "הזינו אימייל וסיסמה לצורך רישום למערכת:"
293+
msgstr "הזינו מייל וסיסמה לצורך רישום למערכת:"
292294

293295
#: templates/signup.html:17
294296
msgid "Full Name"
@@ -304,11 +306,11 @@ msgstr "שם"
304306

305307
#: templates/status.html:13 templates/user.html:47
306308
msgid "Checked"
307-
msgstr "נבדק/ו"
309+
msgstr "נבדקו"
308310

309311
#: templates/status.html:14
310312
msgid "Solved"
311-
msgstr "נפתר/ו"
313+
msgstr "נפתרו"
312314

313315
#: templates/status.html:15
314316
msgid "Percentage"
@@ -328,7 +330,7 @@ msgstr "העלאת מחברות"
328330

329331
#: templates/upload.html:11
330332
msgid "Drag here the notebook file or click and choose it from your computer."
331-
msgstr "גררו לכאן את קובץ המחברת, או לחצו ובחרו אותה מהמחשב שלכם."
333+
msgstr "גררו לכאן את קובץ המחברת, או לחצו ובחרו אותה מהמחשב שלכם."
332334

333335
#: templates/upload.html:14
334336
msgid "Back to Exercises List"
@@ -380,7 +382,7 @@ msgstr "בודק"
380382

381383
#: templates/user.html:37 templates/view.html:25 templates/view.html:112
382384
msgid "Assessment"
383-
msgstr "הערה מילולית"
385+
msgstr "הערכה מילולית"
384386

385387
#: templates/user.html:47
386388
msgid "Submitted"
@@ -400,7 +402,7 @@ msgstr "פתקית חדשה"
400402

401403
#: templates/user.html:70
402404
msgid "Related Exercise"
403-
msgstr "תרגיל משויך"
405+
msgstr "עבור תרגיל"
404406

405407
#: templates/user.html:79
406408
msgid "Privacy Level"
@@ -412,7 +414,7 @@ msgstr "הוסף פתקית"
412414

413415
#: templates/view.html:6
414416
msgid "Exercise view"
415-
msgstr "שם תרגיל"
417+
msgstr "תצוגת תרגיל"
416418

417419
#: templates/view.html:9
418420
msgid "Your solution had checked!"
@@ -428,7 +430,7 @@ msgstr "התרגיל שלך נבדק ברגעים אלו!"
428430

429431
#: templates/view.html:13
430432
msgid "This solution is not up to date!"
431-
msgstr "פתרון זה אינו פתרון עדכני!"
433+
msgstr "פתרון זה אינו עדכני!"
432434

433435
#: templates/view.html:15
434436
msgid "Your solution hasn't been checked."
@@ -460,11 +462,11 @@ msgstr "בדיקות אוטומטיות"
460462

461463
#: templates/view.html:93
462464
msgid "Error"
463-
msgstr "כישלון חמור"
465+
msgstr "כישלון"
464466

465467
#: templates/view.html:98
466468
msgid "Staff Error"
467-
msgstr "כישלון חמור"
469+
msgstr "כישלון סגל"
468470

469471
#: templates/view.html:134
470472
msgid "General comments"
@@ -476,12 +478,12 @@ msgstr "הערות בודק"
476478

477479
#: templates/view.html:152
478480
msgid "Done Checking"
479-
msgstr "סיום בדיקה"
481+
msgstr "סיים לבדוק"
480482

481483
#: utils/mail.py:25
482484
#, python-format
483485
msgid "Confirmation mail - %(site_name)s"
484-
msgstr "מייל אימות - %(site_name)s"
486+
msgstr "הודעת אימות - %(site_name)s"
485487

486488
#: utils/mail.py:32
487489
#, python-format
@@ -490,12 +492,12 @@ msgid ""
490492
"Your confirmation link is: %(link)s"
491493
msgstr ""
492494
"שלום %(fullname)s,\n"
493-
"לינק האימות שלך למערכת הוא: %(link)s"
495+
"קישור האימות שלך למערכת הוא: %(link)s"
494496

495497
#: utils/mail.py:42
496498
#, python-format
497499
msgid "Reset password mail - %(site_name)s"
498-
msgstr "מייל איפוס סיסמה - %(site_name)s"
500+
msgstr "הודעה על איפוס סיסמה - %(site_name)s"
499501

500502
#: utils/mail.py:49
501503
#, python-format
@@ -504,7 +506,7 @@ msgid ""
504506
"Your reset password link is: %(link)s"
505507
msgstr ""
506508
"שלום %(fullname)s,\n"
507-
"לינק לצורך איפוס הסיסמה שלך הוא: %(link)s"
509+
"הקישור לצורך איפוס הסיסמה שלך הוא: %(link)s"
508510

509511
#: utils/mail.py:58
510512
#, python-format
@@ -514,12 +516,10 @@ msgstr "שינוי סיסמה - %(site_name)s"
514516
#: utils/mail.py:62
515517
#, python-format
516518
msgid ""
517-
"Hello %(fullname)s. Your password in %(site_name)s site has been changed."
518-
"\n"
519+
"Hello %(fullname)s. Your password in %(site_name)s site has been changed.\n"
519520
"If you didn't do it please contact with the site management.\n"
520521
"Mail address: %(site_mail)s"
521522
msgstr ""
522523
"שלום %(fullname)s. הסיסמה שלך באתר %(site_name)s שונתה.\n"
523-
"אם אתה לא עשית את זה צור קשר עם הנהלת האתר.\n"
524-
"כתובת המייל: %(site_mail)s"
525-
524+
"אם לא אתה שינית את הסיסמה, צור קשר עם הנהלת האתר.\n"
525+
"כתובת המייל שלך לפי רישומינו: %(site_mail)s"

Diff for: lms/lmsweb/views.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -319,13 +319,21 @@ def main():
319319
return redirect(url_for('exercises_page'))
320320

321321

322-
@webapp.route(routes.STATUS)
322+
@webapp.route(f'{routes.STATUS}/')
323+
def overview_status():
324+
return render_template(
325+
'status.html',
326+
exercises=Solution.status(),
327+
)
328+
329+
330+
@webapp.route(f'/course/<int:course_id>/{routes.STATUS.strip("/")}/')
323331
@managers_only
324332
@login_required
325-
def status():
333+
def status(course_id: int):
326334
return render_template(
327335
'status.html',
328-
exercises=Solution.status(),
336+
exercises=Solution.status(course_id),
329337
)
330338

331339

Diff for: lms/models/solutions.py

-4
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,6 @@ def get_view_parameters(
140140
**view_params,
141141
'exercise_common_comments':
142142
comments._common_comments(exercise_id=solution.exercise),
143-
'all_common_comments':
144-
comments._common_comments(),
145-
'user_comments':
146-
comments._common_comments(user_id=current_user.id),
147143
'left': Solution.left_in_exercise(solution.exercise),
148144
'assessments':
149145
SolutionAssessment.get_assessments(solution.exercise.course),

Diff for: lms/templates/navbar.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@
8484
{% endif -%}
8585
</ul>
8686
<ul class="navbar-nav ms-auto">
87-
{%- if current_user.role.is_manager %}
87+
{%- if current_user.role.is_manager and current_user.last_course_viewed %}
8888
<li class="nav-item">
89-
<a href="/status" class="nav-link">
89+
<a href="{{ url_for('status', course_id=current_user.last_course_viewed.id) }}" class="nav-link">
9090
<i class="fa fa-check-square-o" aria-hidden="true"></i>
9191
{{ _('Check Exercises') }}
9292
</a>

0 commit comments

Comments
 (0)