Skip to content

Commit

Permalink
Merge pull request #43 from ooemperor/dev
Browse files Browse the repository at this point in the history
Updating doc, adding settings page to admin user and fixing loading gif in user frontend task template
  • Loading branch information
ooemperor authored Mar 13, 2024
2 parents 35dedd6 + 0afd276 commit dcb6e6c
Show file tree
Hide file tree
Showing 35 changed files with 942 additions and 106 deletions.
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
# CodeGrader (CG)

This repository will provide the scripts and code for a running Grader.
This will be a web application with User Management and automatic Code Execution and evaluation.
This repository contains the source code for the CodeGrader.
The CodeGrader is an application with User Management and automatic Code Execution and evaluation.

For more detailed information on how to setup this Project please visit the doc Folder.

This project is created as a Bachelor Thesis at the University of Bern.
This project is created as a Bachelor Thesis at the University of Bern in 2023/2024.

## Installation / Deployment
For running the application you can find a detailed Guid in the doc folder for a complete guide.

## Help
If you have any questions about the application, feel free to get in touch. \
E-Mail: [[email protected]](mailto:[email protected])

## License
This application is avaible under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3.


5 changes: 4 additions & 1 deletion codeGrader/backend/api/handlers/Base.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,10 @@ def get_all(self, arguments: dict={}) -> dict:
objects = self.sql_session.get_all(self.dbClass)

if len(objects) == 0: # no data found
return self.create_generic_response('GET', f"No Objects entries to display")
#return self.create_generic_response('GET', f"No Objects entries to display")
output = dict()
output[str(self.dbClass.__table__)] = []
return output

else:
object_list = []
Expand Down
2 changes: 1 addition & 1 deletion codeGrader/backend/config/Config.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def __init__(self):
"""
self.system = platform.system()
if self.system == 'Windows':
configFile = os.path.join(os.path.dirname(__file__), "config.conf")
configFile = os.path.join(os.path.dirname(__file__), "config.sec.conf")
code = open(os.path.join(os.path.dirname(__file__), "codeLanguages.json"))
elif self.system == 'Linux':
configFile = "/etc/codeGrader/backendConfig.conf"
Expand Down
12 changes: 6 additions & 6 deletions codeGrader/backend/config/config.conf
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,26 @@ sender = <MAIL_SENDER_ADDRESS>
[Database]
Dialect = postgresql
DBDriver = psycopg2
Username = codeGrader
Password = codeGrader
Host = 10.101.10.80
Username = <DB_USERNAME>
Password = <DB_PASSWORD>
Host = 127.0.0.1
Port = 5432
Database = codeGraderDB
Database = <DATABASE_NAME>

MetaDataColumnsCount = 3
ColumnIgnoreList = [id, creation_dts, updated_dts]


[ExecutionService]
Host = 10.101.10.80
Host = 127.0.0.1
Port = 8003
PathToExecutionFiles = /opt
IP_Address_Whitelist = [127.0.0.1]
LXC_Install_Command = -t download -- -r bullseye -a amd64 -d debian


[EvaluationService]
Host = 10.101.10.80
Host = 127.0.0.1
Port = 8002
IP_Address_Whitelist = [127.0.0.1]

Expand Down
23 changes: 22 additions & 1 deletion codeGrader/frontend/admin/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
AddTaskInstructionHandler, DeleteTaskInstructionHandler, TaskInstructionHandler, TaskAttachmentHandler, \
SubmissionFileHandler, TestCaseInputFileHandler, TestCaseOutputFileHandler, AddTestCaseHandler, \
DeleteTestCaseHandler, AddMembershipHandler, DeleteMembershipHandler, PasswordResetHandler, AddUserListHandler, \
ErrorHandler
ErrorHandler, SettingsHandler, AdminPasswordResetHandler
from gevent.pywsgi import WSGIServer
import datetime

Expand Down Expand Up @@ -153,6 +153,13 @@ def home() -> str:
return HomeHandler(request).get()


@app.route("/settings", methods=['GET'])
@login_required
def settings():
if request.method == 'GET':
return SettingsHandler(request).get()


@app.route("/user/<int:id_>", methods=['GET', 'POST'])
@login_required
def user(id_) -> Union[Response, str]:
Expand Down Expand Up @@ -231,6 +238,7 @@ def user_password_reset(id_: int) -> Union[Response, str]:
return PasswordResetHandler(request).post(id_)



@app.route("/admin/<int:id_>", methods=['GET', 'POST'])
@login_required
def admin(id_) -> Union[Response, str]:
Expand Down Expand Up @@ -283,6 +291,19 @@ def deleteAdmin(id_: int) -> Union[Response, str]:
return DeleteAdminHandler(request).post(id_)


@app.route("/admin/<int:id_>/passsword/reset", methods=['POST'])
@login_required
def admin_password_reset(id_: int) -> Union[Response, str]:
"""
Resetting the password for a admin defined by the id_
@param id_: The identifier of the object
@type id_: int
@return: Redirect to another view.
"""
if request.method == 'POST':
return AdminPasswordResetHandler(request).post()


@app.route("/profile/<int:id_>", methods=['GET', 'POST'])
@login_required
def profile(id_) -> Union[Response, str]:
Expand Down
59 changes: 58 additions & 1 deletion codeGrader/frontend/admin/handlers/PasswordReset.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

class PasswordResetHandler(BaseHandler):
"""
Handler Class for the PasswordReset
Handler Class for the PasswordReset of a User
"""

def __init__(self, request: flask.Request) -> None:
Expand Down Expand Up @@ -61,3 +61,60 @@ def post(self, id_) -> Union[str, Response]:
self.flash("You are not allowed to update this user")

return redirect(url_for("user", id_=id_))


class AdminPasswordResetHandler(BaseHandler):
"""
Handler Class for the PasswordReset of an Admin User
"""

def __init__(self, request: flask.Request) -> None:
"""
Constructor for the handler of a PasswordReset of the admin user
@param request: The request provided by the routes file
@type request: flask.Request
@return: Nothing
@rtype: None
"""
super().__init__(request)

def post(self) -> Union[str, Response]:
"""
Post method to reset the password of the admin via an api call to the backend
@return: The rendered site or a redirect
@rtype: str|Response
"""

user = self.admin
old_password = self.get_value("old-password")
new_password = self.get_value("new-password")
repeat_new_password = self.get_value("repeat-new-password")

if new_password != repeat_new_password:
self.flash("The new passwords do not match!")

elif new_password == repeat_new_password:
body = dict()
body["password"] = old_password
body["username"] = self.admin.username
response = self.api.post('/admin/login', body)
id_ = response["response"]["id"] # is None when the Authentication failed
if id_ is not None: # authentication successful
body["password"] = new_password
body["id"] = self.admin.id
# resetting the password
response = self.api.post(f"/admin/{self.admin.id}/password/update", body)

if "password" in response.keys():
# change was successful
self.flash("Password has been changed!")

else:
# an error occured while changing the password
self.flash(
"An Error occurred in the process of changing the password. Please Contact your administrator!")
else:
self.flash("Your old password is not correct!")

# finally redirect back to the settings page.
return redirect(url_for("settings"))
52 changes: 52 additions & 0 deletions codeGrader/frontend/admin/handlers/Settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# CodeGrader - https://github.com/ooemperor/CodeGrader
# Copyright © 2023, 2024 Michael Kaiser <[email protected]>
#
# 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 Class for the Settings View of the admin frontend
"""

import flask
from flask import request, render_template, redirect, url_for, flash, Response
from .Base import BaseHandler
from typing import Union


class SettingsHandler(BaseHandler):
"""
Handler Class for the Settings
"""

def __init__(self, request: flask.Request) -> None:
"""
Constructor for the handler of a single Task
@param request: The request provided by the routes file
@type request: flask.Request
@return: Nothing
@rtype: None
"""
super().__init__(request)

def get(self) -> Union[str, Response]:
"""
Get Method to render the settings view for the user that is logged in
@return: The rendered site or a redirect
@rtype: str|Response
"""
settings = self.api.get(f"/admin/{self.admin.id}")

return render_template("settings.html", **settings)
5 changes: 3 additions & 2 deletions codeGrader/frontend/admin/handlers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@
from .Submission import SubmissionFileHandler
from .TestCase import TestCaseInputFileHandler, TestCaseOutputFileHandler, AddTestCaseHandler, DeleteTestCaseHandler
from .Membership import AddMembershipHandler, DeleteMembershipHandler
from .PasswordReset import PasswordResetHandler
from .PasswordReset import PasswordResetHandler, AdminPasswordResetHandler
from .Error import ErrorHandler
from .Settings import SettingsHandler

__all__ = ["AdminUserLoginHandler", "AdminSessionHandler", "SessionAdmin", "UserListHandler", "UserHandler",
"HomeHandler", "AdminHandler", "AdminListHandler", "ProfileListHandler", "ProfileHandler",
Expand All @@ -48,4 +49,4 @@
"DeleteTaskInstructionHandler", "TaskInstructionHandler", "TaskAttachmentHandler", "SubmissionFileHandler",
"TestCaseInputFileHandler", "TestCaseOutputFileHandler", "AddTestCaseHandler", "DeleteTestCaseHandler",
"AddMembershipHandler", "DeleteMembershipHandler", "PasswordResetHandler", "AddUserListHandler",
"ErrorHandler"]
"ErrorHandler", "SettingsHandler", "AdminPasswordResetHandler"]
9 changes: 9 additions & 0 deletions codeGrader/frontend/admin/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,15 @@ <h1>{{ appname }}</h1>
<i class="bi bi-person-circle h5 bg-black link-light"></i>
</a>
<ul class="dropdown-menu text-small shadow">
<li>
<a class="dropdown-item link-light" href="{{ url_for('settings') }}">
<i class="bi bi-gear-fill h5"></i>
<span>Settings</span>
</a>
</li>
<li>
<hr class="dropdown-divider">
</li>
<li>
<a class="dropdown-item" href="{{ url_for('logout') }}">
<i class="bi bi-box-arrow-left h5"></i>
Expand Down
48 changes: 26 additions & 22 deletions codeGrader/frontend/admin/templates/profiles.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ <h1 class="header1 core_content">Profile</h1>
{% if this.admin.check_permission('w', create_object='profile') %}
<div class="col-4">
<form action="{{ url_for('addProfile') }}" method="GET">
<button class="btn btn-success" type="submit">New Profile</button>
<button class="btn btn-success" type="submit">New
Profile</button>
</form>
</div>
{% endif %}
Expand All @@ -40,26 +41,29 @@ <h1 class="header1 core_content">Profile</h1>
<div class="row">
<p>Profile</p>
</div>

<table class="table table-hover">
<thead>
<tr>
<th>Name</th>
<th>Tag</th>
</tr>
</thead>
<tbody>
{% for prof in profile | sort(attribute='name') %}
<tr class="clickable-row" data-href="{{ url_for('profile', id_ = prof['id']|string) }}">
<td>
<a href="{{ url_for('profile', id_ = prof['id']|string) }}">
{{ prof["name"] }}
</a>
</td>
<td>{{ prof["tag"] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="container-fluid">
<table class="table table-hover">
<thead>
<tr>
<th>Name</th>
<th>Tag</th>
</tr>
</thead>
<tbody>
{% for prof in profile | sort(attribute='name') %}
<tr class="clickable-row"
data-href="{{ url_for('profile', id_ = prof['id']|string) }}">
<td>
<a
href="{{ url_for('profile', id_ = prof['id']|string) }}">
{{ prof["name"] }}
</a>
</td>
<td>{{ prof["tag"] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock core %}
Loading

0 comments on commit dcb6e6c

Please sign in to comment.