Skip to content

Commit

Permalink
Merge pull request #38 from ooemperor/dev
Browse files Browse the repository at this point in the history
Adding Gamification, hoverable tables, custom error pages and score view with more fixes
  • Loading branch information
ooemperor authored Feb 15, 2024
2 parents e7d2567 + 3908457 commit 1deb687
Show file tree
Hide file tree
Showing 24 changed files with 638 additions and 54 deletions.
10 changes: 6 additions & 4 deletions codeGrader/backend/api/handlers/Score.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,21 @@ 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}]

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
Expand All @@ -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})

Expand All @@ -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 = []
Expand Down
16 changes: 14 additions & 2 deletions codeGrader/frontend/admin/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand All @@ -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]:
"""
Expand Down
74 changes: 74 additions & 0 deletions codeGrader/frontend/admin/handlers/Error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# CodeGrader - https://github.com/ooemperor/CodeGrader
# Copyright © 2023, 2024 Michael Kaiser <michael.kaiser@emplabs.ch>
#
# 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 <http://www.gnu.org/licenses/>.

"""
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
3 changes: 3 additions & 0 deletions codeGrader/frontend/admin/handlers/Exercise.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
4 changes: 4 additions & 0 deletions codeGrader/frontend/admin/handlers/Subject.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 3 additions & 0 deletions codeGrader/frontend/admin/handlers/Task.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
4 changes: 3 additions & 1 deletion codeGrader/frontend/admin/handlers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -46,4 +47,5 @@
"AddTaskAttachmentHandler", "AddTaskInstructionHandler", "DeleteTaskAttachmentHandler",
"DeleteTaskInstructionHandler", "TaskInstructionHandler", "TaskAttachmentHandler", "SubmissionFileHandler",
"TestCaseInputFileHandler", "TestCaseOutputFileHandler", "AddTestCaseHandler", "DeleteTestCaseHandler",
"AddMembershipHandler", "DeleteMembershipHandler", "PasswordResetHandler", "AddUserListHandler"]
"AddMembershipHandler", "DeleteMembershipHandler", "PasswordResetHandler", "AddUserListHandler",
"ErrorHandler"]
60 changes: 60 additions & 0 deletions codeGrader/frontend/admin/templates/error.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<!--
CodeGrader - https://github.com/ooemperor/CodeGrader
Copyright © 2023, 2024 Michael Kaiser <michael.kaiser@emplabs.ch>
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 <http://www.gnu.org/licenses/>.
-->
<!DOCTYPE html>
<html lang="en" data-bs-theme="dark">

<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
<script src="{{ url_for('static', filename='js/bootstrap.bundle.min.js') }}"></script>
<title>{{ appname}} Error</title>
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='img/favicon.ico')}}">
</head>

<body class="min-vw-100">
<div class="container-fluid min-vw-100" id="error">
<div class="row">
<div class="row justify-content-center p-2">
<h1 class="row justify-content-center">
{{ title }}
</h1>
</div>
<div class="row justify-content-center p-2">
{{ error_text }}
<br>
If you think this should not happen, please contact your administrator and provide the Information below.
</div>
<div class="row justify-content-center p-2">
{{ gif|safe }}
</div>
<div class="row justify-content-center">
Path: {{path}}
<br>
Method: {{method}}
<br>
Time: {{time}}
<br>
Exception: {{error}}
</div>
</div>
</div>
</body>

</html>
44 changes: 44 additions & 0 deletions codeGrader/frontend/admin/templates/exercise.html
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,50 @@ <h5 class="modal-title fs-5" id="deleteExerciseTitle">Delete Exercise {{ name }}
</div>
{% endif %}

<div class="row py-4">
<div class="accordion">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
data-bs-target="#panelscores" aria-expanded="false" aria-controls="panelscores">
Scores
</button>
</h2>
<div id="panelscores" class="accordion-collapse collapse">
<div class="accordion-body">
<div>
{% if scores %}
<table class="table table-hover">
<tr>
<th>
User
</th>
<th>
Max Score
</th>
</tr>

{% for sc in scores | sort(attribute='user_id') %}
<tr>
<td>
<a href="{{ url_for('user', id_ = sc['user_id']|string) }}">
{{ sc["username"] }}
</a>
</td>
<td>
{{ sc["score"] }}
</td>
</tr>
{% endfor %}
</table>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>

<div class="row py-4">
<div class="accordion">
<div class="accordion-item">
Expand Down
46 changes: 45 additions & 1 deletion codeGrader/frontend/admin/templates/subject.html
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,50 @@ <h5 class="modal-title fs-5" id="deleteSubjectTitle">Delete Subject {{ name }} ?
</div>
{% endif %}

<div class="row py-4">
<div class="accordion">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
data-bs-target="#panelscores" aria-expanded="false" aria-controls="panelscores">
Scores
</button>
</h2>
<div id="panelscores" class="accordion-collapse collapse">
<div class="accordion-body">
<div>
{% if scores %}
<table class="table table-hover">
<tr>
<th>
User
</th>
<th>
Max Score
</th>
</tr>

{% for sc in scores | sort(attribute='user_id') %}
<tr>
<td>
<a href="{{ url_for('user', id_ = sc['user_id']|string) }}">
{{ sc["username"] }}
</a>
</td>
<td>
{{ sc["score"] }}
</td>
</tr>
{% endfor %}
</table>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>


<div class="row py-4">
<div class="accordion">
Expand Down Expand Up @@ -159,7 +203,7 @@ <h2 class="accordion-header">
<div class="accordion-body">
<div>
{% if memberships %}
<table class="table">
<table class="table table-hover">
<tr>
<th>
Username
Expand Down
Loading

0 comments on commit 1deb687

Please sign in to comment.