Skip to content

feature: Per course status page #348

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 17 additions & 9 deletions devops/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 ."
37 changes: 24 additions & 13 deletions lms/lmsdb/bootstrap.py
Original file line number Diff line number Diff line change
@@ -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 (
Expand Down Expand Up @@ -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:
Expand Down
17 changes: 10 additions & 7 deletions lms/lmsdb/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)
Expand Down
62 changes: 31 additions & 31 deletions lms/lmsweb/translations/he/LC_MESSAGES/messages.po
Original file line number Diff line number Diff line change
Expand Up @@ -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 <[email protected]>\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"
Expand All @@ -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
Expand Down Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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"
Expand All @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -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"
Expand All @@ -400,7 +402,7 @@ msgstr "פתקית חדשה"

#: templates/user.html:70
msgid "Related Exercise"
msgstr "תרגיל משויך"
msgstr "עבור תרגיל"

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

#: templates/view.html:6
msgid "Exercise view"
msgstr "שם תרגיל"
msgstr "תצוגת תרגיל"

#: templates/view.html:9
msgid "Your solution had checked!"
Expand All @@ -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."
Expand Down Expand Up @@ -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"
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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"
14 changes: 11 additions & 3 deletions lms/lmsweb/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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/<int:course_id>/{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),
)


Expand Down
4 changes: 0 additions & 4 deletions lms/models/solutions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
4 changes: 2 additions & 2 deletions lms/templates/navbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@
{% endif -%}
</ul>
<ul class="navbar-nav ms-auto">
{%- if current_user.role.is_manager %}
{%- if current_user.role.is_manager and current_user.last_course_viewed %}
<li class="nav-item">
<a href="/status" class="nav-link">
<a href="{{ url_for('status', course_id=current_user.last_course_viewed.id) }}" class="nav-link">
<i class="fa fa-check-square-o" aria-hidden="true"></i>
{{ _('Check Exercises') }}
</a>
Expand Down
Loading