From 51573c7afc0b2a635152b3699c1f4924659822ee Mon Sep 17 00:00:00 2001 From: Michael Kaiser Date: Thu, 8 Feb 2024 18:39:21 +0100 Subject: [PATCH 1/8] adding first version of the live gamification feedback in the user frontend --- codeGrader/frontend/user/app.py | 26 ++++++--- .../frontend/user/handlers/Submission.py | 54 +++++++++++++++++-- codeGrader/frontend/user/handlers/Task.py | 15 ++++-- codeGrader/frontend/user/handlers/__init__.py | 5 +- codeGrader/frontend/user/templates/task.html | 37 +++++++++++++ 5 files changed, 121 insertions(+), 16 deletions(-) diff --git a/codeGrader/frontend/user/app.py b/codeGrader/frontend/user/app.py index 9a13447..98e3ff2 100644 --- a/codeGrader/frontend/user/app.py +++ b/codeGrader/frontend/user/app.py @@ -29,7 +29,7 @@ from codeGrader.frontend.user import templates from codeGrader.frontend.user.handlers import UserSessionHandler, SessionUser, UserLoginHandler, HomeHandler, \ ExerciseListHandler, ExerciseHandler, TaskHandler, TaskListHandler, TaskAttachmentHandler, TaskInstructionHandler, \ - AddSubmissionHandler, SettingsHandler, PasswordResetHandler, SubjectHandler, SubjectListHandler + AddSubmissionHandler, SettingsHandler, PasswordResetHandler, SubjectHandler, SubjectListHandler, SubmissionHandler from gevent.pywsgi import WSGIServer from typing import Union import datetime @@ -198,18 +198,18 @@ def tasks() -> str: @app.route("/task/", methods=['GET']) @login_required -def task(id_: int, from_submission: bool = False) -> str: +def task(id_: int) -> str: """ The TaskHandler to render or redirect the templates @param id_: The identifier of the task @type id_: int - @param from_submission: Flag if the source is coming from a redirect after posting a submission - @type from_submission: bool + @param submission_id: The id of a submission if a submission has been made (used for rendering after submission) + @type submission_id: int @return: The rendered Task site @rtype: str """ if request.method == 'GET': - return TaskHandler(request).get(id_, from_submission=from_submission) + return TaskHandler(request).get(id_, request.args) @app.route("/task//attachment/", methods=['GET']) @@ -244,13 +244,27 @@ def TaskInstruction(task_id_: int, instruction_id_: int) -> Union[Response, str] return TaskInstructionHandler(request).get(task_id_, instruction_id_) -@app.route("/task//submission/add", methods = ['POST']) +@app.route("/task//submission/add", methods=['POST']) @login_required def addSubmission(task_id_: int) -> Union[Response, str]: if request.method == 'POST': return AddSubmissionHandler(request).post(task_id_) +@app.route("/gamification/submission/", methods=['GET']) +@login_required +def submission_gamification(id_: int) -> Union[Response, str]: + """ + Route for the function that returns HTML code to be inserted by javascript into the code + @param id_: The identifier of the submission that has been made + @type id_: int + @return: HTML Code + @rtype: str + """ + if request.method == 'GET': + return SubmissionHandler(request).get_gamification(id_) + + @app.route("/settings", methods=['GET']) @login_required def settings(): diff --git a/codeGrader/frontend/user/handlers/Submission.py b/codeGrader/frontend/user/handlers/Submission.py index d0c3cd1..a9ff523 100644 --- a/codeGrader/frontend/user/handlers/Submission.py +++ b/codeGrader/frontend/user/handlers/Submission.py @@ -26,6 +26,49 @@ from typing import Union +class SubmissionHandler(BaseHandler): + """ + Class to handle the Requests for a submission + """ + + def __init__(self, request: flask.Request) -> None: + """ + Constructor of the Handler + """ + super().__init__(request) + + def get_gamification(self, id_: int) -> str: + """ + Render for the submission result + @param id_: The identifier of the submission + @type id_: int + @return: HTML Code that will be inserted into the rendered html via javascript / + This html code is only a snippet and not complete code + """ + if id_ == 0: + return "" # we do not return anything + + else: + # we need to prepare the data now + submission = self.api.get(f"/submission/{id_}") + state = submission["state"] + score = float(submission["max_score"]) + + if state != "finished": + return 'Hi GIF - Hi GIFs' + + else: + # submission has finished in the backend, need to calculate the result base on the score. + if score == 100.0: + return 'One Hundred Percent Sophie GIF - One Hundred Percent Sophie How I Met Your Father GIFs' + + elif score == 0.0: + return 'Thisisfine Disaster GIF - Thisisfine Disaster Denial GIFs' + + else: + return 'Not Good Not Bad Good And Bad GIF - Not Good Not Bad Not Good Not Bad GIFs' + + class AddSubmissionHandler(BaseHandler): """ Class to handle the upload of an submission @@ -51,6 +94,8 @@ def post(self, id_: int) -> Response: user_id = self.user.id task_id = id_ + submission_id = None + for file_key in self.request.files.keys(): file = self.request.files[file_key] @@ -64,8 +109,11 @@ def post(self, id_: int) -> Response: body = ({"task_id": task_id, "file_id": file_id, "user_id": user_id}) url = f"/submission/add" - self.api.post(url, body=body) # adding the submission via file_id + response = self.api.post(url, body=body) # adding the submission via file_id + print(response) + submission_id = response["response"]["id"] + # either way redirect to the task - self.flash("Submission received") - return redirect(url_for("task", id_=id_)) + self.flash(f"Submission received with id_ {submission_id}") + return redirect(url_for("task", id_=id_, submission_id=submission_id)) diff --git a/codeGrader/frontend/user/handlers/Task.py b/codeGrader/frontend/user/handlers/Task.py index cc1a9f0..2ccc70f 100644 --- a/codeGrader/frontend/user/handlers/Task.py +++ b/codeGrader/frontend/user/handlers/Task.py @@ -21,7 +21,7 @@ """ import flask -from flask import request, render_template, redirect, url_for, flash, Response +from flask import request, render_template, redirect, url_for, flash, Response, Request from .Base import BaseHandler from typing import Union @@ -71,19 +71,24 @@ def __init__(self, request: flask.Request) -> None: """ super().__init__(request) - def get(self, id_: int, from_submission: bool = False) -> Union[str, Response]: + def get(self, id_: int, arguments: Request.args) -> Union[str, Response]: """ Get Method to render or redirect for a specific Task @param id_: The identifier of the object @type id_: int - @param from_submission: Flag if the source is coming from a redirect after posting a submission. / + @param arguments: The arguments passed to the function in the url / This is used when we want to live render the execution of the submission - @rtype: bool + @rtype: int @return: """ task = self.api.get(f"/task/{id_}") - task["from_submission"] = from_submission + + if "submission_id" in arguments.keys(): + task["submission_id"] = arguments["submission_id"] + + else: + task["submission_id"] = 0 if self.user.check_permission(subject_id=task["subject_id"]): # when user is allowed to view this task submissions = self.api.get(f"/submissions", task_id=id_, user_id=self.user.id) diff --git a/codeGrader/frontend/user/handlers/__init__.py b/codeGrader/frontend/user/handlers/__init__.py index 613b8da..f5913f4 100644 --- a/codeGrader/frontend/user/handlers/__init__.py +++ b/codeGrader/frontend/user/handlers/__init__.py @@ -28,11 +28,12 @@ from .Task import TaskHandler, TaskListHandler from .TaskAttachment import TaskAttachmentHandler from .TaskInstruction import TaskInstructionHandler -from .Submission import AddSubmissionHandler +from .Submission import AddSubmissionHandler, SubmissionHandler from .Settings import SettingsHandler from .PasswordReset import PasswordResetHandler from .Subject import SubjectListHandler, SubjectHandler __all__ = ["BaseHandler", "UserSessionHandler", "SessionUser", "UserLoginHandler", "HomeHandler", "ExerciseListHandler", "ExerciseHandler", "TaskHandler", "TaskListHandler", "TaskAttachmentHandler", "TaskInstructionHandler", - "AddSubmissionHandler", "SettingsHandler", "PasswordResetHandler", "SubjectHandler", "SubjectListHandler"] + "AddSubmissionHandler", "SettingsHandler", "PasswordResetHandler", "SubjectHandler", "SubjectListHandler", + "SubmissionHandler"] diff --git a/codeGrader/frontend/user/templates/task.html b/codeGrader/frontend/user/templates/task.html index ea68404..a603ddd 100644 --- a/codeGrader/frontend/user/templates/task.html +++ b/codeGrader/frontend/user/templates/task.html @@ -59,6 +59,43 @@

Task {{ name }}

+ + +
+ +
+
From 52c8380ed764e66e9d4f74c3f3ae7a254275bd98 Mon Sep 17 00:00:00 2001 From: Michael Kaiser Date: Wed, 14 Feb 2024 12:21:12 +0100 Subject: [PATCH 2/8] adding first draft of scores in task view of admin panel --- codeGrader/frontend/admin/handlers/Task.py | 3 ++ codeGrader/frontend/admin/templates/task.html | 44 +++++++++++++++++++ doc/System_Prototype.drawio | 2 +- 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/codeGrader/frontend/admin/handlers/Task.py b/codeGrader/frontend/admin/handlers/Task.py index 90ec785..c1fc565 100644 --- a/codeGrader/frontend/admin/handlers/Task.py +++ b/codeGrader/frontend/admin/handlers/Task.py @@ -72,6 +72,9 @@ def get(self, id_: int) -> Union[str, Response]: exercises = self.api.get("/exercises", profile=self.admin.get_filter_profile()) submissions = self.api.get("/submissions", profile=self.admin.get_filter_profile(), task_id=id_) testcases = self.api.get("/testcases", task_id=id_) + scores = self.api.get(f"/scores/task", object_id=id_) + scores = scores["task"][0][str(id_)] + task["scores"] = scores task["exercises"] = exercises["exercise"] if "submission" in submissions.keys(): # handles the case that there are no submissions yet task["submissions"] = submissions["submission"] diff --git a/codeGrader/frontend/admin/templates/task.html b/codeGrader/frontend/admin/templates/task.html index fde24ad..3a12e16 100644 --- a/codeGrader/frontend/admin/templates/task.html +++ b/codeGrader/frontend/admin/templates/task.html @@ -375,6 +375,50 @@
{% endif %} +
+
+
+

+ +

+
+
+
+ {% if scores %} + + + + + + + {% for sc in scores | sort(attribute='user_id') %} + + + + + {% endfor %} +
+ User + + Max Score +
+ + {{ sc["user_id"] }} + + + {{ sc["score"] }} +
+ {% endif %} +
+
+
+
+
+
+
diff --git a/doc/System_Prototype.drawio b/doc/System_Prototype.drawio index bcea91e..3951c58 100644 --- a/doc/System_Prototype.drawio +++ b/doc/System_Prototype.drawio @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From d929caab41b69a57223e9203f621d7ae638a522b Mon Sep 17 00:00:00 2001 From: Michael Kaiser Date: Wed, 14 Feb 2024 13:35:51 +0100 Subject: [PATCH 3/8] adding score overview to the exercise and subject panel --- codeGrader/backend/api/handlers/Score.py | 10 +++-- .../frontend/admin/handlers/Exercise.py | 3 ++ codeGrader/frontend/admin/handlers/Subject.py | 4 ++ .../frontend/admin/templates/exercise.html | 44 +++++++++++++++++++ .../frontend/admin/templates/subject.html | 44 +++++++++++++++++++ codeGrader/frontend/admin/templates/task.html | 2 +- 6 files changed, 102 insertions(+), 5 deletions(-) diff --git a/codeGrader/backend/api/handlers/Score.py b/codeGrader/backend/api/handlers/Score.py index f25e3ac..36bd71b 100644 --- a/codeGrader/backend/api/handlers/Score.py +++ b/codeGrader/backend/api/handlers/Score.py @@ -85,8 +85,9 @@ def get_scores(self, view: str, arguments: dict = {}): db_object = self.sql_session.get_object(self.dbClass, object_id) print(db_object.id) score = db_object.user_score(user_id) + user = self.sql_session.get(User, user_id) - user_data_dict = {"user_id": user_id, "score": score} + user_data_dict = {"user_id": user_id, "username": user.username, "score": score} output_data_list.append(user_data_dict) output[str(self.dbClass.__table__)] = [{object_id: output_data_list}] @@ -94,10 +95,11 @@ def get_scores(self, view: str, arguments: dict = {}): else: # the object_id is not given, so we need to query all objects db_objects = self.sql_session.get_all(self.dbClass) + user = self.sql_session.get(User, user_id) for db_object in db_objects: score = db_object.user_score(user_id) print(score) - user_data_dict = {"user_id": user_id, "score": score} + user_data_dict = {"user_id": user_id, "username": user.username, "score": score} object_list.append({db_object.id: user_data_dict}) output[str(self.dbClass.__table__)] = object_list @@ -113,7 +115,7 @@ def get_scores(self, view: str, arguments: dict = {}): for user in users: score = db_object.user_score(user.id) - user_data_dict = {"user_id": user.id, "score": score} + user_data_dict = {"user_id": user.id, "username": user.username, "score": score} output_data_list.append(user_data_dict) object_list.append({object_id: output_data_list}) @@ -125,7 +127,7 @@ def get_scores(self, view: str, arguments: dict = {}): for db_object in db_objects: for user in users: score = db_object.user_score(user.id) - user_data_dict = {"user_id": user.id, "score": score} + user_data_dict = {"user_id": user.id, "username": user.username, "score": score} output_data_list.append(user_data_dict) object_list.append({db_object.id: output_data_list}) output_data_list = [] diff --git a/codeGrader/frontend/admin/handlers/Exercise.py b/codeGrader/frontend/admin/handlers/Exercise.py index e8ef4dd..3102a2d 100644 --- a/codeGrader/frontend/admin/handlers/Exercise.py +++ b/codeGrader/frontend/admin/handlers/Exercise.py @@ -70,6 +70,9 @@ def get(self, id_: int) -> Union[str, Response]: editable = self.admin.check_permission('w', exercise["profile"]["id"]) subjects = self.api.get("/subjects", profile=self.admin.get_filter_profile()) + scores = self.api.get(f"/scores/exercise", object_id=id_) + scores = scores["exercise"][0][str(id_)] + exercise["scores"] = scores exercise["subjects"] = subjects["subject"] diff --git a/codeGrader/frontend/admin/handlers/Subject.py b/codeGrader/frontend/admin/handlers/Subject.py index 1888d96..589d033 100644 --- a/codeGrader/frontend/admin/handlers/Subject.py +++ b/codeGrader/frontend/admin/handlers/Subject.py @@ -73,6 +73,10 @@ def get(self, id_: int) -> Union[str, Response]: subject["profiles"] = profiles["profile"] subject["memberships"] = memberships["membership"] + scores = self.api.get(f"/scores/subject", object_id=id_) + scores = scores["subject"][0][str(id_)] + subject["scores"] = scores + editable = self.admin.check_permission('w', subject["profile"]["id"]) subject["editable"] = editable diff --git a/codeGrader/frontend/admin/templates/exercise.html b/codeGrader/frontend/admin/templates/exercise.html index 1c46749..81769db 100644 --- a/codeGrader/frontend/admin/templates/exercise.html +++ b/codeGrader/frontend/admin/templates/exercise.html @@ -121,6 +121,50 @@
{% endif %} +
+
+
+

+ +

+
+
+
+ {% if scores %} + + + + + + + {% for sc in scores | sort(attribute='user_id') %} + + + + + {% endfor %} +
+ User + + Max Score +
+ + {{ sc["username"] }} + + + {{ sc["score"] }} +
+ {% endif %} +
+
+
+
+
+
+
diff --git a/codeGrader/frontend/admin/templates/subject.html b/codeGrader/frontend/admin/templates/subject.html index 370f2dc..1639d78 100644 --- a/codeGrader/frontend/admin/templates/subject.html +++ b/codeGrader/frontend/admin/templates/subject.html @@ -117,6 +117,50 @@
{% endif %} +
+
+
+

+ +

+
+
+
+ {% if scores %} + + + + + + + {% for sc in scores | sort(attribute='user_id') %} + + + + + {% endfor %} +
+ User + + Max Score +
+ + {{ sc["username"] }} + + + {{ sc["score"] }} +
+ {% endif %} +
+
+
+
+
+
+
diff --git a/codeGrader/frontend/admin/templates/task.html b/codeGrader/frontend/admin/templates/task.html index 3a12e16..3a7a9c9 100644 --- a/codeGrader/frontend/admin/templates/task.html +++ b/codeGrader/frontend/admin/templates/task.html @@ -402,7 +402,7 @@

- {{ sc["user_id"] }} + {{ sc["username"] }} From 37306612a9477b568944ffc4bd73859b9cfe9562 Mon Sep 17 00:00:00 2001 From: Michael Kaiser Date: Thu, 15 Feb 2024 12:33:07 +0100 Subject: [PATCH 4/8] adding more gifs to the gamification and updating the way they get displayed. --- codeGrader/frontend/config/Config.py | 4 ++ codeGrader/frontend/config/config.conf | 20 +++++++ .../frontend/user/handlers/Submission.py | 13 +++-- codeGrader/frontend/util/API.py | 56 +++++++++++-------- 4 files changed, 65 insertions(+), 28 deletions(-) diff --git a/codeGrader/frontend/config/Config.py b/codeGrader/frontend/config/Config.py index 22269d9..7c81515 100644 --- a/codeGrader/frontend/config/Config.py +++ b/codeGrader/frontend/config/Config.py @@ -69,3 +69,7 @@ def __init__(self): self.admin_rw_partial = self.config["AdminTypes"]["rwPartial"] self.admin_r_full = self.config["AdminTypes"]["rFull"] self.admin_r_partial = self.config["AdminTypes"]["rPartial"] + + self.good_gifs = self.config["GIFS"]["Good_GIFS"].split(',') + self.bad_gifs = self.config["GIFS"]["Bad_GIFS"].split(',') + self.medium_gifs = self.config["GIFS"]["Medium_GIFS"].split(',') diff --git a/codeGrader/frontend/config/config.conf b/codeGrader/frontend/config/config.conf index 5fd278d..18fd0c0 100644 --- a/codeGrader/frontend/config/config.conf +++ b/codeGrader/frontend/config/config.conf @@ -8,6 +8,26 @@ Name = CodeGrader Secret_Key = Port = 8102 +[GIFS] +Good_GIFS = [ + 'Friends Joey GIF - Friends Joey Chandler GIFs', + 'Friends Friends Tv GIF - Friends Friends Tv Tv Friends GIFs', + 'One Hundred Percent Sophie GIF - One Hundred Percent Sophie How I Met Your Father GIFs' + +] + +Bad_GIFS = [ + 'No Score GIF - No Score Judging GIFs', + 'Thisisfine Disaster GIF - Thisisfine Disaster Denial GIFs' +] + +Medium_GIFS = [ + 'Meh Mediocre GIF - Meh Mediocre So So GIFs', + 'Meh The GIF - Meh The Simpsons GIFs', + 'Reasonsimbroke Larry David GIF - Reasonsimbroke Larry David Hbo GIFs' + 'Not Good Not Bad Good And Bad GIF - Not Good Not Bad Not Good Not Bad GIFs' +] + [API] Name = CodeGrader Host = http://127.0.0.1:8001 diff --git a/codeGrader/frontend/user/handlers/Submission.py b/codeGrader/frontend/user/handlers/Submission.py index a9ff523..f43ce58 100644 --- a/codeGrader/frontend/user/handlers/Submission.py +++ b/codeGrader/frontend/user/handlers/Submission.py @@ -24,7 +24,8 @@ from flask import request, render_template, redirect, url_for, flash, Response from .Base import BaseHandler from typing import Union - +from codeGrader.frontend.config import config +from random import randint class SubmissionHandler(BaseHandler): """ @@ -60,13 +61,17 @@ def get_gamification(self, id_: int) -> str: else: # submission has finished in the backend, need to calculate the result base on the score. if score == 100.0: - return 'One Hundred Percent Sophie GIF - One Hundred Percent Sophie How I Met Your Father GIFs' + i = int(id_) % len(config.good_gifs) + return config.good_gifs[i] elif score == 0.0: - return 'Thisisfine Disaster GIF - Thisisfine Disaster Denial GIFs' + i = int(id_) % len(config.bad_gifs) + return config.bad_gifs[i] else: - return 'Not Good Not Bad Good And Bad GIF - Not Good Not Bad Not Good Not Bad GIFs' + # score is between 0 and 100 but not either of those + i = int(id_) % len(config.medium_gifs) + return config.medium_gifs[i] class AddSubmissionHandler(BaseHandler): diff --git a/codeGrader/frontend/util/API.py b/codeGrader/frontend/util/API.py index 83fb97f..f26f3e1 100644 --- a/codeGrader/frontend/util/API.py +++ b/codeGrader/frontend/util/API.py @@ -65,30 +65,38 @@ def _make_request(self, method: str, path: str, body: str = None, data: str = No @return: Formatted response of the api. @rtype: """ - path = f"{self.url}{path}" - headers = dict() - if self.authentication_type is not None: - headers["Authorization"] = f"{self.authentication_type} {self.authentication_token}" - - if method == 'GET': - assert body is None - response = requests.get(path, headers=headers) - - elif method == 'POST': - assert body is not None or data is not None or files is not None - response = requests.post(path, headers=headers, json=body, data=data, files=files) - - elif method == 'PUT': - assert body is not None - response = requests.put(path, headers=headers, json=body, data=data, files=files) - - elif method == 'DELETE': - assert body is None - response = requests.delete(path, headers=headers) - else: - raise TypeError("Method not allowed! Allowed are: DELETE, GET, POST, PUT") - - return response + try: + path = f"{self.url}{path}" + headers = dict() + if self.authentication_type is not None: + headers["Authorization"] = f"{self.authentication_type} {self.authentication_token}" + + if method == 'GET': + assert body is None + response = requests.get(path, headers=headers) + + elif method == 'POST': + assert body is not None or data is not None or files is not None + response = requests.post(path, headers=headers, json=body, data=data, files=files) + + elif method == 'PUT': + assert body is not None + response = requests.put(path, headers=headers, json=body, data=data, files=files) + + elif method == 'DELETE': + assert body is None + response = requests.delete(path, headers=headers) + else: + raise TypeError("Method not allowed! Allowed are: DELETE, GET, POST, PUT") + + return response + + except AssertionError as ass_err: + print(ass_err) + print(path) + print(method) + print(response.status_code) + print(response.text) @staticmethod def _cast_dict(dictionary: str) -> Union[str, dict]: From b309cb3513f0fc7812a762413d244eab7c04639a Mon Sep 17 00:00:00 2001 From: Michael Kaiser Date: Thu, 15 Feb 2024 12:34:38 +0100 Subject: [PATCH 5/8] adding more gifs to the gamification and updating the way they get displayed. --- codeGrader/frontend/config/config.conf | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/codeGrader/frontend/config/config.conf b/codeGrader/frontend/config/config.conf index 18fd0c0..d7977d9 100644 --- a/codeGrader/frontend/config/config.conf +++ b/codeGrader/frontend/config/config.conf @@ -9,24 +9,20 @@ Secret_Key = Port = 8102 [GIFS] -Good_GIFS = [ - 'Friends Joey GIF - Friends Joey Chandler GIFs', - 'Friends Friends Tv GIF - Friends Friends Tv Tv Friends GIFs', - 'One Hundred Percent Sophie GIF - One Hundred Percent Sophie How I Met Your Father GIFs' +Good_GIFS = + Friends Joey GIF - Friends Joey Chandler GIFs, + Friends Friends Tv GIF - Friends Friends Tv Tv Friends GIFs, + One Hundred Percent Sophie GIF - One Hundred Percent Sophie How I Met Your Father GIFs -] - -Bad_GIFS = [ - 'No Score GIF - No Score Judging GIFs', - 'Thisisfine Disaster GIF - Thisisfine Disaster Denial GIFs' -] +Bad_GIFS = + No Score GIF - No Score Judging GIFs + Thisisfine Disaster GIF - Thisisfine Disaster Denial GIFs Medium_GIFS = [ - 'Meh Mediocre GIF - Meh Mediocre So So GIFs', - 'Meh The GIF - Meh The Simpsons GIFs', - 'Reasonsimbroke Larry David GIF - Reasonsimbroke Larry David Hbo GIFs' - 'Not Good Not Bad Good And Bad GIF - Not Good Not Bad Not Good Not Bad GIFs' -] + Meh Mediocre GIF - Meh Mediocre So So GIFs, + Meh The GIF - Meh The Simpsons GIFs, + Reasonsimbroke Larry David GIF - Reasonsimbroke Larry David Hbo GIFs [API] Name = CodeGrader From 4f8012a75999e23210bd7f1c4aa13bb0952c04df Mon Sep 17 00:00:00 2001 From: Michael Kaiser Date: Thu, 15 Feb 2024 14:22:22 +0100 Subject: [PATCH 6/8] making more tables hoverable --- codeGrader/frontend/admin/templates/exercise.html | 2 +- codeGrader/frontend/admin/templates/profile.html | 2 +- codeGrader/frontend/admin/templates/subject.html | 6 +++--- codeGrader/frontend/admin/templates/task.html | 8 ++++---- codeGrader/frontend/admin/templates/user.html | 4 ++-- codeGrader/frontend/admin/templates/users.html | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/codeGrader/frontend/admin/templates/exercise.html b/codeGrader/frontend/admin/templates/exercise.html index 81769db..a950176 100644 --- a/codeGrader/frontend/admin/templates/exercise.html +++ b/codeGrader/frontend/admin/templates/exercise.html @@ -134,7 +134,7 @@

{% if scores %} - +
User diff --git a/codeGrader/frontend/admin/templates/profile.html b/codeGrader/frontend/admin/templates/profile.html index ab2e460..14cc1f3 100644 --- a/codeGrader/frontend/admin/templates/profile.html +++ b/codeGrader/frontend/admin/templates/profile.html @@ -31,7 +31,7 @@

Profile {{ name }}

- +
diff --git a/codeGrader/frontend/admin/templates/subject.html b/codeGrader/frontend/admin/templates/subject.html index 1639d78..8e6ca35 100644 --- a/codeGrader/frontend/admin/templates/subject.html +++ b/codeGrader/frontend/admin/templates/subject.html @@ -33,7 +33,7 @@

Subject {{ name }}

-
Name
+
@@ -130,7 +130,7 @@

{% if scores %} -

Name
+
User @@ -203,7 +203,7 @@

{% if memberships %} - +
Username diff --git a/codeGrader/frontend/admin/templates/task.html b/codeGrader/frontend/admin/templates/task.html index 3a7a9c9..3263698 100644 --- a/codeGrader/frontend/admin/templates/task.html +++ b/codeGrader/frontend/admin/templates/task.html @@ -49,7 +49,7 @@

Task {{ name }}

- +
@@ -289,7 +289,7 @@

{% if testcases %} -

Name
+
Testcase_ID @@ -388,7 +388,7 @@

{% if scores %} - +
User @@ -432,7 +432,7 @@

{% if submissions %} - +
Submission_id diff --git a/codeGrader/frontend/admin/templates/user.html b/codeGrader/frontend/admin/templates/user.html index 95a06e3..ccac46d 100644 --- a/codeGrader/frontend/admin/templates/user.html +++ b/codeGrader/frontend/admin/templates/user.html @@ -39,7 +39,7 @@

User {{ username }}

- +
@@ -194,7 +194,7 @@

{% if memberships %} -

Username
+
Name diff --git a/codeGrader/frontend/admin/templates/users.html b/codeGrader/frontend/admin/templates/users.html index 11849e2..a77a39d 100644 --- a/codeGrader/frontend/admin/templates/users.html +++ b/codeGrader/frontend/admin/templates/users.html @@ -49,7 +49,7 @@

Users

- +
From b6e83129e230f4b286dec73ed9d8c5a61ec6ad27 Mon Sep 17 00:00:00 2001 From: Michael Kaiser Date: Thu, 15 Feb 2024 14:25:52 +0100 Subject: [PATCH 7/8] reverting mistake with hoverable tables --- codeGrader/frontend/admin/templates/profile.html | 2 +- codeGrader/frontend/admin/templates/subject.html | 2 +- codeGrader/frontend/admin/templates/task.html | 2 +- codeGrader/frontend/admin/templates/user.html | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/codeGrader/frontend/admin/templates/profile.html b/codeGrader/frontend/admin/templates/profile.html index 14cc1f3..ab2e460 100644 --- a/codeGrader/frontend/admin/templates/profile.html +++ b/codeGrader/frontend/admin/templates/profile.html @@ -31,7 +31,7 @@

Profile {{ name }}

-
Username
+
diff --git a/codeGrader/frontend/admin/templates/subject.html b/codeGrader/frontend/admin/templates/subject.html index 8e6ca35..0a46f89 100644 --- a/codeGrader/frontend/admin/templates/subject.html +++ b/codeGrader/frontend/admin/templates/subject.html @@ -33,7 +33,7 @@

Subject {{ name }}

-
Name
+
diff --git a/codeGrader/frontend/admin/templates/task.html b/codeGrader/frontend/admin/templates/task.html index 3263698..e41870f 100644 --- a/codeGrader/frontend/admin/templates/task.html +++ b/codeGrader/frontend/admin/templates/task.html @@ -49,7 +49,7 @@

Task {{ name }}

-
Name
+
diff --git a/codeGrader/frontend/admin/templates/user.html b/codeGrader/frontend/admin/templates/user.html index ccac46d..1dc33f9 100644 --- a/codeGrader/frontend/admin/templates/user.html +++ b/codeGrader/frontend/admin/templates/user.html @@ -39,7 +39,7 @@

User {{ username }}

-
Name
+
From 3908457eb522b71339701ec868c9906d87d91c8a Mon Sep 17 00:00:00 2001 From: Michael Kaiser Date: Thu, 15 Feb 2024 15:45:28 +0100 Subject: [PATCH 8/8] adding gif based error pages --- codeGrader/frontend/admin/app.py | 16 +++- codeGrader/frontend/admin/handlers/Error.py | 74 +++++++++++++++++++ .../frontend/admin/handlers/__init__.py | 4 +- .../frontend/admin/templates/error.html | 60 +++++++++++++++ codeGrader/frontend/config/Config.py | 2 + codeGrader/frontend/config/config.conf | 5 ++ codeGrader/frontend/user/app.py | 16 +++- codeGrader/frontend/user/handlers/Error.py | 74 +++++++++++++++++++ codeGrader/frontend/user/handlers/__init__.py | 3 +- codeGrader/frontend/user/templates/error.html | 60 +++++++++++++++ 10 files changed, 308 insertions(+), 6 deletions(-) create mode 100644 codeGrader/frontend/admin/handlers/Error.py create mode 100644 codeGrader/frontend/admin/templates/error.html create mode 100644 codeGrader/frontend/user/handlers/Error.py create mode 100644 codeGrader/frontend/user/templates/error.html diff --git a/codeGrader/frontend/admin/app.py b/codeGrader/frontend/admin/app.py index dffe470..d36dd4b 100644 --- a/codeGrader/frontend/admin/app.py +++ b/codeGrader/frontend/admin/app.py @@ -35,7 +35,8 @@ DeleteExerciseHandler, DeleteProfileHandler, AddTaskAttachmentHandler, DeleteTaskAttachmentHandler, \ AddTaskInstructionHandler, DeleteTaskInstructionHandler, TaskInstructionHandler, TaskAttachmentHandler, \ SubmissionFileHandler, TestCaseInputFileHandler, TestCaseOutputFileHandler, AddTestCaseHandler, \ - DeleteTestCaseHandler, AddMembershipHandler, DeleteMembershipHandler, PasswordResetHandler, AddUserListHandler + DeleteTestCaseHandler, AddMembershipHandler, DeleteMembershipHandler, PasswordResetHandler, AddUserListHandler, \ + ErrorHandler from gevent.pywsgi import WSGIServer import datetime @@ -70,7 +71,7 @@ def app_index(): method = route.methods rule = route.rule endpoint = route.endpoint - output_data.append({rule:{"methods": method, "endpoint": endpoint} }) + output_data.append({rule: {"methods": method, "endpoint": endpoint}}) output["routes"] = output_data return output @@ -89,6 +90,17 @@ def adminUser_login(admin_id): return user +@app.errorhandler(Exception) +def error(err: Exception): + """ + Error Handler for a when a error occurs. + @param err: The Exception that has been raised + @type err: Exception + @return: Rendered Error Page with Information for the user + """ + return ErrorHandler(request).get(err, err.code) + + @app.route("/login", methods=['GET', 'POST']) def login() -> Union[Response, str]: """ diff --git a/codeGrader/frontend/admin/handlers/Error.py b/codeGrader/frontend/admin/handlers/Error.py new file mode 100644 index 0000000..8d6d5a1 --- /dev/null +++ b/codeGrader/frontend/admin/handlers/Error.py @@ -0,0 +1,74 @@ +# CodeGrader - https://github.com/ooemperor/CodeGrader +# Copyright © 2023, 2024 Michael Kaiser +# +# This file is part of CodeGrader. +# +# CodeGrader is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# CodeGrader is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with CodeGrader. If not, see . + +""" +Handler Classes for the Error Sites of the CodeGrader +""" +import flask +import flask_login +from flask import request, render_template, redirect +from .Base import BaseHandler +import datetime +from codeGrader.frontend.config import config + + +class ErrorHandler(BaseHandler): + """ + Handles Rendering of the Error Site + """ + + def __init__(self, request: flask.Request): + """ + Constructor of the Error Handler + @param request: The request from the app route of flask + @type request: flask.request + """ + super().__init__(request) + + def get(self, error: Exception, error_code: int) -> (str, int): + """ + Renders the template of the Error Site of the user frontend + @param error: The Exception of the site + @type error: Exception + @param error_code: The Error Code of the site + @type error_code: int + @return: str + """ + request_data = dict() + request_data["path"] = self.request.path + request_data["method"] = self.request.method + request_data["time"] = datetime.datetime.now() + request_data["error"] = error + + if error_code == 404: + request_data["title"] = "Page not found" + request_data["error_text"] = "It looks like the page you are looking for does not exist" + request_data["gif"] = config.gif_404 + return render_template("error.html", **request_data), error_code + + elif error_code == 500: + request_data["title"] = f"{config.userAppName} not found" + request_data["error_text"] = "An Error occured on the server while processing your request. Please Try Again!" + request_data["gif"] = config.gif_500 + return render_template("error.html", **request_data), error_code + + else: + request_data["title"] = f"{config.userAppName} Error" + request_data["error_text"] = "Something went wrong. Please Try Again!" + request_data["gif"] = config.gif_500 + return render_template("error.html", **request_data), error_code diff --git a/codeGrader/frontend/admin/handlers/__init__.py b/codeGrader/frontend/admin/handlers/__init__.py index 45b9114..840e986 100644 --- a/codeGrader/frontend/admin/handlers/__init__.py +++ b/codeGrader/frontend/admin/handlers/__init__.py @@ -36,6 +36,7 @@ from .TestCase import TestCaseInputFileHandler, TestCaseOutputFileHandler, AddTestCaseHandler, DeleteTestCaseHandler from .Membership import AddMembershipHandler, DeleteMembershipHandler from .PasswordReset import PasswordResetHandler +from .Error import ErrorHandler __all__ = ["AdminUserLoginHandler", "AdminSessionHandler", "SessionAdmin", "UserListHandler", "UserHandler", "HomeHandler", "AdminHandler", "AdminListHandler", "ProfileListHandler", "ProfileHandler", @@ -46,4 +47,5 @@ "AddTaskAttachmentHandler", "AddTaskInstructionHandler", "DeleteTaskAttachmentHandler", "DeleteTaskInstructionHandler", "TaskInstructionHandler", "TaskAttachmentHandler", "SubmissionFileHandler", "TestCaseInputFileHandler", "TestCaseOutputFileHandler", "AddTestCaseHandler", "DeleteTestCaseHandler", - "AddMembershipHandler", "DeleteMembershipHandler", "PasswordResetHandler", "AddUserListHandler"] + "AddMembershipHandler", "DeleteMembershipHandler", "PasswordResetHandler", "AddUserListHandler", + "ErrorHandler"] diff --git a/codeGrader/frontend/admin/templates/error.html b/codeGrader/frontend/admin/templates/error.html new file mode 100644 index 0000000..857de28 --- /dev/null +++ b/codeGrader/frontend/admin/templates/error.html @@ -0,0 +1,60 @@ + + + + + + + + + {{ appname}} Error + + + + +
+
+
+

+ {{ title }} +

+
+
+ {{ error_text }} +
+ If you think this should not happen, please contact your administrator and provide the Information below. +
+
+ {{ gif|safe }} +
+
+ Path: {{path}} +
+ Method: {{method}} +
+ Time: {{time}} +
+ Exception: {{error}} +
+
+
+ + + \ No newline at end of file diff --git a/codeGrader/frontend/config/Config.py b/codeGrader/frontend/config/Config.py index 7c81515..3d02471 100644 --- a/codeGrader/frontend/config/Config.py +++ b/codeGrader/frontend/config/Config.py @@ -73,3 +73,5 @@ def __init__(self): self.good_gifs = self.config["GIFS"]["Good_GIFS"].split(',') self.bad_gifs = self.config["GIFS"]["Bad_GIFS"].split(',') self.medium_gifs = self.config["GIFS"]["Medium_GIFS"].split(',') + self.gif_404 = self.config["GIFS"]["404_GIF"] + self.gif_500 = self.config["GIFS"]["500_GIF"] diff --git a/codeGrader/frontend/config/config.conf b/codeGrader/frontend/config/config.conf index d7977d9..5b95bfa 100644 --- a/codeGrader/frontend/config/config.conf +++ b/codeGrader/frontend/config/config.conf @@ -24,6 +24,11 @@ Medium_GIFS = [ Reasonsimbroke Larry David GIF - Reasonsimbroke Larry David Hbo GIFs +404_GIF = 404 Whoops GIF - 404 Whoops Afas GIFs + +500_GIF = Chicago Med Hannah Asher GIF - Chicago Med Hannah Asher Error GIFs + + [API] Name = CodeGrader Host = http://127.0.0.1:8001 diff --git a/codeGrader/frontend/user/app.py b/codeGrader/frontend/user/app.py index 98e3ff2..3a9b8f9 100644 --- a/codeGrader/frontend/user/app.py +++ b/codeGrader/frontend/user/app.py @@ -29,7 +29,8 @@ from codeGrader.frontend.user import templates from codeGrader.frontend.user.handlers import UserSessionHandler, SessionUser, UserLoginHandler, HomeHandler, \ ExerciseListHandler, ExerciseHandler, TaskHandler, TaskListHandler, TaskAttachmentHandler, TaskInstructionHandler, \ - AddSubmissionHandler, SettingsHandler, PasswordResetHandler, SubjectHandler, SubjectListHandler, SubmissionHandler + AddSubmissionHandler, SettingsHandler, PasswordResetHandler, SubjectHandler, SubjectListHandler, SubmissionHandler, \ + ErrorHandler from gevent.pywsgi import WSGIServer from typing import Union import datetime @@ -65,7 +66,7 @@ def app_index(): method = route.methods rule = route.rule endpoint = route.endpoint - output_data.append({rule:{"methods": method, "endpoint": endpoint} }) + output_data.append({rule: {"methods": method, "endpoint": endpoint}}) output["routes"] = output_data return output @@ -93,6 +94,17 @@ def unauthorized(): return redirect(url_for("login")) +@app.errorhandler(Exception) +def error(err: Exception): + """ + Error Handler for a when a error occurs. + @param err: The Exception that has been raised + @type err: Exception + @return: Rendered Error Page with Information for the user + """ + return ErrorHandler(request).get(err, err.code) + + @app.route("/login", methods=['GET', 'POST']) def login(): """ diff --git a/codeGrader/frontend/user/handlers/Error.py b/codeGrader/frontend/user/handlers/Error.py new file mode 100644 index 0000000..8d6d5a1 --- /dev/null +++ b/codeGrader/frontend/user/handlers/Error.py @@ -0,0 +1,74 @@ +# CodeGrader - https://github.com/ooemperor/CodeGrader +# Copyright © 2023, 2024 Michael Kaiser +# +# This file is part of CodeGrader. +# +# CodeGrader is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# CodeGrader is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with CodeGrader. If not, see . + +""" +Handler Classes for the Error Sites of the CodeGrader +""" +import flask +import flask_login +from flask import request, render_template, redirect +from .Base import BaseHandler +import datetime +from codeGrader.frontend.config import config + + +class ErrorHandler(BaseHandler): + """ + Handles Rendering of the Error Site + """ + + def __init__(self, request: flask.Request): + """ + Constructor of the Error Handler + @param request: The request from the app route of flask + @type request: flask.request + """ + super().__init__(request) + + def get(self, error: Exception, error_code: int) -> (str, int): + """ + Renders the template of the Error Site of the user frontend + @param error: The Exception of the site + @type error: Exception + @param error_code: The Error Code of the site + @type error_code: int + @return: str + """ + request_data = dict() + request_data["path"] = self.request.path + request_data["method"] = self.request.method + request_data["time"] = datetime.datetime.now() + request_data["error"] = error + + if error_code == 404: + request_data["title"] = "Page not found" + request_data["error_text"] = "It looks like the page you are looking for does not exist" + request_data["gif"] = config.gif_404 + return render_template("error.html", **request_data), error_code + + elif error_code == 500: + request_data["title"] = f"{config.userAppName} not found" + request_data["error_text"] = "An Error occured on the server while processing your request. Please Try Again!" + request_data["gif"] = config.gif_500 + return render_template("error.html", **request_data), error_code + + else: + request_data["title"] = f"{config.userAppName} Error" + request_data["error_text"] = "Something went wrong. Please Try Again!" + request_data["gif"] = config.gif_500 + return render_template("error.html", **request_data), error_code diff --git a/codeGrader/frontend/user/handlers/__init__.py b/codeGrader/frontend/user/handlers/__init__.py index f5913f4..3da0706 100644 --- a/codeGrader/frontend/user/handlers/__init__.py +++ b/codeGrader/frontend/user/handlers/__init__.py @@ -32,8 +32,9 @@ from .Settings import SettingsHandler from .PasswordReset import PasswordResetHandler from .Subject import SubjectListHandler, SubjectHandler +from .Error import ErrorHandler __all__ = ["BaseHandler", "UserSessionHandler", "SessionUser", "UserLoginHandler", "HomeHandler", "ExerciseListHandler", "ExerciseHandler", "TaskHandler", "TaskListHandler", "TaskAttachmentHandler", "TaskInstructionHandler", "AddSubmissionHandler", "SettingsHandler", "PasswordResetHandler", "SubjectHandler", "SubjectListHandler", - "SubmissionHandler"] + "SubmissionHandler", "ErrorHandler"] diff --git a/codeGrader/frontend/user/templates/error.html b/codeGrader/frontend/user/templates/error.html new file mode 100644 index 0000000..857de28 --- /dev/null +++ b/codeGrader/frontend/user/templates/error.html @@ -0,0 +1,60 @@ + + + + + + + + + {{ appname}} Error + + + + +
+
+
+

+ {{ title }} +

+
+
+ {{ error_text }} +
+ If you think this should not happen, please contact your administrator and provide the Information below. +
+
+ {{ gif|safe }} +
+
+ Path: {{path}} +
+ Method: {{method}} +
+ Time: {{time}} +
+ Exception: {{error}} +
+
+
+ + + \ No newline at end of file
Username