diff --git a/devops/bootstrap.sh b/devops/bootstrap.sh index 971e32b1..28b5020f 100755 --- a/devops/bootstrap.sh +++ b/devops/bootstrap.sh @@ -4,14 +4,22 @@ SCRIPT_FILE_PATH=$(readlink -f "${0}") SCRIPT_FOLDER=$(dirname "${SCRIPT_FILE_PATH}") MAIN_FOLDER="${SCRIPT_FOLDER}/../lms" -docker exec -i lms_rabbitmq_1 rabbitmqctl add_vhost lmstests-public -docker exec -i lms_rabbitmq_1 rabbitmqctl add_vhost lmstests-sandbox -docker exec -i lms_rabbitmq_1 rabbitmqctl set_permissions -p lmstests-public rabbit-user ".*" ".*" ".*" -docker exec -i lms_rabbitmq_1 rabbitmqctl set_permissions -p lmstests-sandbox rabbit-user ".*" ".*" ".*" -docker exec -i lms_http_1 python lmsdb/bootstrap.py +# Starting from docker-compose 2, +# names are network-image-id instead of network_image_id +if docker-compose --version | grep -q 'version 2'; then + SEP="-" +else + SEP="_" +fi + +docker exec -i lms${SEP}rabbitmq${SEP}1 rabbitmqctl add_vhost lmstests-public +docker exec -i lms${SEP}rabbitmq${SEP}1 rabbitmqctl add_vhost lmstests-sandbox +docker exec -i lms${SEP}rabbitmq${SEP}1 rabbitmqctl set_permissions -p lmstests-public rabbit-user ".*" ".*" ".*" +docker exec -i lms${SEP}rabbitmq${SEP}1 rabbitmqctl set_permissions -p lmstests-sandbox rabbit-user ".*" ".*" ".*" +docker exec -i lms${SEP}http${SEP}1 python lmsdb/bootstrap.py # build the image for docker inside a docker! -docker exec lms_checks-docker-engine_1 mkdir -p /home/lms -docker cp "${MAIN_FOLDER}"/lmstests/public/unittests/image/requirements.txt lms_checks-docker-engine_1:/home/lms/. -docker cp "${MAIN_FOLDER}"/lmstests/public/unittests/image/Dockerfile lms_checks-docker-engine_1:/home/lms/. -docker exec lms_checks-docker-engine_1 sh -c "cd /home/lms && docker build -t lms ." +docker exec lms${SEP}checks-docker-engine${SEP}1 mkdir -p /home/lms +docker cp "${MAIN_FOLDER}"/lmstests/public/unittests/image/requirements.txt lms${SEP}checks-docker-engine${SEP}1:/home/lms/. +docker cp "${MAIN_FOLDER}"/lmstests/public/unittests/image/Dockerfile lms${SEP}checks-docker-engine${SEP}1:/home/lms/. +docker exec lms${SEP}checks-docker-engine${SEP}1 sh -c "cd /home/lms && docker build -t lms ." diff --git a/lms/lmsdb/bootstrap.py b/lms/lmsdb/bootstrap.py index 64a9f401..e6dff390 100644 --- a/lms/lmsdb/bootstrap.py +++ b/lms/lmsdb/bootstrap.py @@ -1,4 +1,4 @@ -from typing import Any, Callable, Optional, Tuple, Type +from typing import Any, Callable, Iterable, List, Optional, Tuple, Type, Union from uuid import uuid4 from peewee import ( @@ -322,24 +322,35 @@ def _assessment_migration() -> bool: return True +def is_tables_exists(tables: Union[Model, Iterable[Model]]) -> bool: + if not isinstance(tables, (tuple, list)): + tables = (tables,) + + return all( + models.database.table_exists(table.__name__.lower()) + for table in tables + ) + + +def get_new_tables(tables: Iterable[Model]) -> List[Model]: + return [table for table in tables if not is_tables_exists(table)] + + def main(): with models.database.connection_context(): - if models.database.table_exists(models.Exercise.__name__.lower()): - _add_exercise_course_id_and_number_columns_constraint() + new_tables = get_new_tables(models.ALL_MODELS) + models.database.create_tables(new_tables, safe=True) - if models.database.table_exists(models.Solution.__name__.lower()): - _last_status_view_migration() - _assessment_migration() + _add_exercise_course_id_and_number_columns_constraint() - if models.database.table_exists(models.User.__name__.lower()): - _api_keys_migration() - _last_course_viewed_migration() - _uuid_migration() + _last_status_view_migration() + _assessment_migration() - if models.database.table_exists(models.UserCourse.__name__.lower()): - _add_user_course_constaint() + _api_keys_migration() + _last_course_viewed_migration() + _uuid_migration() - models.database.create_tables(models.ALL_MODELS, safe=True) + _add_user_course_constaint() models.create_basic_roles() if models.User.select().count() == 0: diff --git a/lms/lmsdb/models.py b/lms/lmsdb/models.py index b0ac75f7..f28433e3 100644 --- a/lms/lmsdb/models.py +++ b/lms/lmsdb/models.py @@ -798,26 +798,29 @@ def next_unchecked_of(cls, exercise_id) -> Optional['Solution']: return None @classmethod - def status(cls): + def status(cls, course_id: Optional[int] = None): one_if_is_checked = Case( Solution.state, ((Solution.STATES.DONE.name, 1),), 0, ) fields = ( Exercise.id, + Exercise.course, Exercise.subject.alias('name'), Exercise.is_archived.alias('is_archived'), fn.Count(Solution.id).alias('submitted'), fn.Sum(one_if_is_checked).alias('checked'), ) - join_by_exercise = (Solution.exercise == Exercise.id) - active_solutions = Solution.state.in_( - Solution.STATES.active_solutions(), - ) + active_solution_states = Solution.STATES.active_solutions() + active_solutions = Solution.state.in_(active_solution_states) + right_course = (course_id is None) or course_id == Course.id + return ( Exercise .select(*fields) - .join(Solution, JOIN.LEFT_OUTER, on=join_by_exercise) - .where(active_solutions) + .join(Course, on=(Course.id == Exercise.course)) + .switch() + .join(Solution, on=(Solution.exercise == Exercise.id)) + .where(active_solutions & right_course) .group_by(Exercise.subject, Exercise.id) .order_by(Exercise.id) ) diff --git a/lms/lmsweb/translations/he/LC_MESSAGES/messages.po b/lms/lmsweb/translations/he/LC_MESSAGES/messages.po index 3b9f9aa0..20cd5355 100644 --- a/lms/lmsweb/translations/he/LC_MESSAGES/messages.po +++ b/lms/lmsweb/translations/he/LC_MESSAGES/messages.po @@ -5,18 +5,20 @@ # msgid "" msgstr "" -"Project-Id-Version: 1.0\n" +"Project-Id-Version: 1.0\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2021-10-15 14:37+0300\n" -"PO-Revision-Date: 2021-09-29 11:30+0300\n" +"PO-Revision-Date: 2021-10-17 01:59+0300\n" "Last-Translator: Or Ronai\n" "Language: he\n" "Language-Team: he \n" -"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.9.1\n" +"X-Generator: Poedit 3.0\n" +"X-Poedit-Bookmarks: -1,-1,-1,-1,0,-1,-1,-1,-1,-1\n" #: lmsdb/models.py:921 msgid "Fatal error" @@ -39,8 +41,8 @@ msgstr "הבודק האוטומטי לא הצליח להריץ את הקוד ש #: lmstests/public/unittests/services.py:120 #, python-format msgid "" -"The automatic checker failed in %(number)d examples in your " -"\"%(subject)s\" solution." +"The automatic checker failed in %(number)d examples in your \"%(subject)s\" " +"solution." msgstr "הבודק האוטומטי נכשל ב־ %(number)d דוגמאות בתרגיל \"%(subject)s\"." #: lmstests/public/unittests/services.py:142 @@ -73,12 +75,12 @@ msgstr "קישור לאיפוס הסיסמה נשלח בהצלחה" #: lmsweb/views.py:246 msgid "Reset password link is expired" -msgstr "קישור איפוס הסיסמה פג תוקף" +msgstr "הקישור לאיפוס הסיסמה פג תוקף" #: lmsweb/forms/change_password.py:20 lmsweb/forms/register.py:32 #: lmsweb/forms/reset_password.py:25 msgid "The passwords are not identical" -msgstr "הסיסמאות שהוקלדו אינן זהות" +msgstr "הסיסמאות שהוזנו אינן זהות" #: lmsweb/forms/change_password.py:31 msgid "Invalid old password has been inserted too many times" @@ -91,15 +93,15 @@ msgstr "הסיסמה הנוכחית שהוזנה שגויה" #: lmsweb/forms/register.py:14 lmsweb/forms/reset_password.py:12 #: lmsweb/tools/validators.py:29 msgid "Invalid email" -msgstr "אימייל לא תקין" +msgstr "כתובת הדואר האלקטרוני שהוזנה אינה תקינה" #: lmsweb/tools/validators.py:13 msgid "The username is already in use" -msgstr "שם המשתמש כבר נמצא בשימוש" +msgstr "שם המשתמש הזה כבר נמצא בשימוש" #: lmsweb/tools/validators.py:21 msgid "The email is already in use" -msgstr "האימייל כבר נמצא בשימוש" +msgstr "כתובת הדואר האלקטרוני הזו כבר נמצאת בשימוש" #: models/solutions.py:52 #, python-format @@ -267,7 +269,7 @@ msgstr "הזינו סיסמה לצורך שינוי הסיסמה:" #: templates/reset-password.html:11 msgid "Insert your email for getting link to reset it:" -msgstr "הזינו אימייל לצורך שליחת קישור לאיפוס הסיסמה:" +msgstr "הזינו מייל לצורך שליחת קישור לאיפוס הסיסמה:" #: templates/reset-password.html:14 templates/signup.html:15 #: templates/user.html:12 @@ -288,7 +290,7 @@ msgstr "הרשמה" #: templates/signup.html:12 msgid "Insert your email and password for registration:" -msgstr "הזינו אימייל וסיסמה לצורך רישום למערכת:" +msgstr "הזינו מייל וסיסמה לצורך רישום למערכת:" #: templates/signup.html:17 msgid "Full Name" @@ -304,11 +306,11 @@ msgstr "שם" #: templates/status.html:13 templates/user.html:47 msgid "Checked" -msgstr "נבדק/ו" +msgstr "נבדקו" #: templates/status.html:14 msgid "Solved" -msgstr "נפתר/ו" +msgstr "נפתרו" #: templates/status.html:15 msgid "Percentage" @@ -328,7 +330,7 @@ msgstr "העלאת מחברות" #: templates/upload.html:11 msgid "Drag here the notebook file or click and choose it from your computer." -msgstr "גררו לכאן את קובץ המחברת, או לחצו ובחרו אותה מהמחשב שלכם." +msgstr "גררו לכאן את קובץ המחברת, או לחצו ובחרו אותה מהמחשב שלכם." #: templates/upload.html:14 msgid "Back to Exercises List" @@ -380,7 +382,7 @@ msgstr "בודק" #: templates/user.html:37 templates/view.html:25 templates/view.html:112 msgid "Assessment" -msgstr "הערה מילולית" +msgstr "הערכה מילולית" #: templates/user.html:47 msgid "Submitted" @@ -400,7 +402,7 @@ msgstr "פתקית חדשה" #: templates/user.html:70 msgid "Related Exercise" -msgstr "תרגיל משויך" +msgstr "עבור תרגיל" #: templates/user.html:79 msgid "Privacy Level" @@ -412,7 +414,7 @@ msgstr "הוסף פתקית" #: templates/view.html:6 msgid "Exercise view" -msgstr "שם תרגיל" +msgstr "תצוגת תרגיל" #: templates/view.html:9 msgid "Your solution had checked!" @@ -428,7 +430,7 @@ msgstr "התרגיל שלך נבדק ברגעים אלו!" #: templates/view.html:13 msgid "This solution is not up to date!" -msgstr "פתרון זה אינו פתרון עדכני!" +msgstr "פתרון זה אינו עדכני!" #: templates/view.html:15 msgid "Your solution hasn't been checked." @@ -460,11 +462,11 @@ msgstr "בדיקות אוטומטיות" #: templates/view.html:93 msgid "Error" -msgstr "כישלון חמור" +msgstr "כישלון" #: templates/view.html:98 msgid "Staff Error" -msgstr "כישלון חמור" +msgstr "כישלון סגל" #: templates/view.html:134 msgid "General comments" @@ -476,12 +478,12 @@ msgstr "הערות בודק" #: templates/view.html:152 msgid "Done Checking" -msgstr "סיום בדיקה" +msgstr "סיים לבדוק" #: utils/mail.py:25 #, python-format msgid "Confirmation mail - %(site_name)s" -msgstr "מייל אימות - %(site_name)s" +msgstr "הודעת אימות - %(site_name)s" #: utils/mail.py:32 #, python-format @@ -490,12 +492,12 @@ msgid "" "Your confirmation link is: %(link)s" msgstr "" "שלום %(fullname)s,\n" -"לינק האימות שלך למערכת הוא: %(link)s" +"קישור האימות שלך למערכת הוא: %(link)s" #: utils/mail.py:42 #, python-format msgid "Reset password mail - %(site_name)s" -msgstr "מייל איפוס סיסמה - %(site_name)s" +msgstr "הודעה על איפוס סיסמה - %(site_name)s" #: utils/mail.py:49 #, python-format @@ -504,7 +506,7 @@ msgid "" "Your reset password link is: %(link)s" msgstr "" "שלום %(fullname)s,\n" -"לינק לצורך איפוס הסיסמה שלך הוא: %(link)s" +"הקישור לצורך איפוס הסיסמה שלך הוא: %(link)s" #: utils/mail.py:58 #, python-format @@ -514,12 +516,10 @@ msgstr "שינוי סיסמה - %(site_name)s" #: utils/mail.py:62 #, python-format msgid "" -"Hello %(fullname)s. Your password in %(site_name)s site has been changed." -"\n" +"Hello %(fullname)s. Your password in %(site_name)s site has been changed.\n" "If you didn't do it please contact with the site management.\n" "Mail address: %(site_mail)s" msgstr "" "שלום %(fullname)s. הסיסמה שלך באתר %(site_name)s שונתה.\n" -"אם אתה לא עשית את זה צור קשר עם הנהלת האתר.\n" -"כתובת המייל: %(site_mail)s" - +"אם לא אתה שינית את הסיסמה, צור קשר עם הנהלת האתר.\n" +"כתובת המייל שלך לפי רישומינו: %(site_mail)s" diff --git a/lms/lmsweb/views.py b/lms/lmsweb/views.py index 9eeb6709..a21fd7ac 100644 --- a/lms/lmsweb/views.py +++ b/lms/lmsweb/views.py @@ -319,13 +319,21 @@ def main(): return redirect(url_for('exercises_page')) -@webapp.route(routes.STATUS) +@webapp.route(f'{routes.STATUS}/') +def overview_status(): + return render_template( + 'status.html', + exercises=Solution.status(), + ) + + +@webapp.route(f'/course//{routes.STATUS.strip("/")}/') @managers_only @login_required -def status(): +def status(course_id: int): return render_template( 'status.html', - exercises=Solution.status(), + exercises=Solution.status(course_id), ) diff --git a/lms/models/solutions.py b/lms/models/solutions.py index 541a6c39..6324a637 100644 --- a/lms/models/solutions.py +++ b/lms/models/solutions.py @@ -140,10 +140,6 @@ def get_view_parameters( **view_params, 'exercise_common_comments': comments._common_comments(exercise_id=solution.exercise), - 'all_common_comments': - comments._common_comments(), - 'user_comments': - comments._common_comments(user_id=current_user.id), 'left': Solution.left_in_exercise(solution.exercise), 'assessments': SolutionAssessment.get_assessments(solution.exercise.course), diff --git a/lms/templates/navbar.html b/lms/templates/navbar.html index d2e49864..603b2947 100644 --- a/lms/templates/navbar.html +++ b/lms/templates/navbar.html @@ -84,9 +84,9 @@ {% endif -%}