From 91e6dd649daf5aea5edfac17c287b728491f95c4 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sat, 24 Feb 2024 14:34:26 +0100 Subject: [PATCH 01/82] route /projects/ added --- backend/project/__init__.py | 2 ++ backend/project/__main__.py | 4 ++++ backend/project/endpoints/projects/projects.py | 11 +++++++++++ 3 files changed, 17 insertions(+) create mode 100644 backend/project/endpoints/projects/projects.py diff --git a/backend/project/__init__.py b/backend/project/__init__.py index 8b9f5f94..1dcb13e9 100644 --- a/backend/project/__init__.py +++ b/backend/project/__init__.py @@ -5,6 +5,7 @@ from flask import Flask from flask_sqlalchemy import SQLAlchemy from .endpoints.index.index import index_bp +from .endpoints.projects.projects import projects_bp db = SQLAlchemy() @@ -17,6 +18,7 @@ def create_app(): app = Flask(__name__) app.register_blueprint(index_bp) + app.register_blueprint(projects_bp) return app diff --git a/backend/project/__main__.py b/backend/project/__main__.py index 9448b0eb..fd2f3273 100644 --- a/backend/project/__main__.py +++ b/backend/project/__main__.py @@ -1,4 +1,8 @@ """Main entry point for the application.""" +# TODO: remove is for dev purposes +from sys import path +path.append(".") + from sys import path from os import getenv from dotenv import load_dotenv diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py new file mode 100644 index 00000000..8ac368af --- /dev/null +++ b/backend/project/endpoints/projects/projects.py @@ -0,0 +1,11 @@ +from flask import Blueprint +from flask_restful import Resource, Api + +projects_bp = Blueprint('projects', __name__) + +class Projects(Resource): + def get(self): + return {'hello': 'world'} + + +projects_bp.add_url_rule('/projects/', view_func=Projects.as_view('projects')) From de332a76265930b0506f80e415a36832d47fddbb Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 25 Feb 2024 09:52:27 +0100 Subject: [PATCH 02/82] added functionality for parsing the dict once the query is ran --- backend/project/__init__.py | 4 +-- .../project/endpoints/projects/projects.py | 34 +++++++++++++++++-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/backend/project/__init__.py b/backend/project/__init__.py index 1dcb13e9..7cf6a422 100644 --- a/backend/project/__init__.py +++ b/backend/project/__init__.py @@ -4,8 +4,6 @@ from flask import Flask from flask_sqlalchemy import SQLAlchemy -from .endpoints.index.index import index_bp -from .endpoints.projects.projects import projects_bp db = SQLAlchemy() @@ -15,6 +13,8 @@ def create_app(): Returns: Flask -- A Flask application instance """ + from .endpoints.index.index import index_bp + from .endpoints.projects.projects import projects_bp app = Flask(__name__) app.register_blueprint(index_bp) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 8ac368af..9ebe3256 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -1,11 +1,39 @@ from flask import Blueprint from flask_restful import Resource, Api +from sqlalchemy.orm import sessionmaker -projects_bp = Blueprint('projects', __name__) +from project import db +from project.models.projects import Projects -class Projects(Resource): +projects_bp = Blueprint('projects', __name__) +projects_endpoint = Api(projects_bp) +class Projects_endpoint(Resource): def get(self): + """ + Get method for listing all available projects + that are currently in the API + """ + # TODO: fetch data from database using sqlalchemy + display_data = {} + projects = Projects.query.all() + + for project in projects: + project_dict = { + "id": project.project_id, + "title": project.title, + "descriptions": project.descriptions, + "assignment_file": project.assignment_file, + "deadline": project.deadline, + "course_id": project.course_id, + "visible_for_students": project.visible_for_students, + "archieved": project.archieved, + "test_path": project.test_path, + "script_name": project.script_name, + "regex_expressions": project.regex_expressions + } + print(project_dict) + return {'hello': 'world'} -projects_bp.add_url_rule('/projects/', view_func=Projects.as_view('projects')) +projects_bp.add_url_rule('/projects/', view_func=Projects_endpoint.as_view('projects')) From b032e392295c11afbe909be580ed67d78cf97f91 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 25 Feb 2024 09:54:48 +0100 Subject: [PATCH 03/82] display json data at endpoint instead of dummy data --- backend/project/endpoints/projects/projects.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 9ebe3256..7697d1ed 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -14,7 +14,7 @@ def get(self): that are currently in the API """ # TODO: fetch data from database using sqlalchemy - display_data = {} + display_data = [] projects = Projects.query.all() for project in projects: @@ -31,9 +31,9 @@ def get(self): "script_name": project.script_name, "regex_expressions": project.regex_expressions } - print(project_dict) + display_data.append(project_dict) - return {'hello': 'world'} + return display_data projects_bp.add_url_rule('/projects/', view_func=Projects_endpoint.as_view('projects')) From 9f5e4f31b42c51ab1d9d9c2841d77baaf75d5713 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 25 Feb 2024 10:23:43 +0100 Subject: [PATCH 04/82] detail endpoint created --- backend/project/__init__.py | 2 ++ .../endpoints/projects/project_detail.py | 34 +++++++++++++++++++ .../project/endpoints/projects/projects.py | 2 -- 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 backend/project/endpoints/projects/project_detail.py diff --git a/backend/project/__init__.py b/backend/project/__init__.py index 7cf6a422..66ea1716 100644 --- a/backend/project/__init__.py +++ b/backend/project/__init__.py @@ -15,10 +15,12 @@ def create_app(): """ from .endpoints.index.index import index_bp from .endpoints.projects.projects import projects_bp + from .endpoints.projects.project_detail import project_detail_bp app = Flask(__name__) app.register_blueprint(index_bp) app.register_blueprint(projects_bp) + app.register_blueprint(project_detail_bp) return app diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py new file mode 100644 index 00000000..5913700b --- /dev/null +++ b/backend/project/endpoints/projects/project_detail.py @@ -0,0 +1,34 @@ +from flask import Blueprint +from flask_restful import Resource, Api + +from project import db +from project.models.projects import Projects + +project_detail_bp = Blueprint('project_detail', __name__) +project_detail_endpoint = Api(project_detail_bp) + + +class ProjectDetail(Resource): + def get(self, **kwargs): + id: int = kwargs['project_id'] + print(id) + + project = Projects.query.filter_by(project_id=id).first() + project_dict = { + "id": project.project_id, + "title": project.title, + "descriptions": project.descriptions, + "assignment_file": project.assignment_file, + "deadline": project.deadline, + "course_id": project.course_id, + "visible_for_students": project.visible_for_students, + "archieved": project.archieved, + "test_path": project.test_path, + "script_name": project.script_name, + "regex_expressions": project.regex_expressions + } + + return project_dict + + +project_detail_bp.add_url_rule('/projects/', view_func=ProjectDetail.as_view('project_detail')) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 7697d1ed..19e9a8af 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -1,6 +1,5 @@ from flask import Blueprint from flask_restful import Resource, Api -from sqlalchemy.orm import sessionmaker from project import db from project.models.projects import Projects @@ -13,7 +12,6 @@ def get(self): Get method for listing all available projects that are currently in the API """ - # TODO: fetch data from database using sqlalchemy display_data = [] projects = Projects.query.all() From 2dc13403d4260de456694f1d557eec9e553dfc9f Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 25 Feb 2024 11:42:27 +0100 Subject: [PATCH 05/82] post functionality for projects --- .../endpoints/projects/project_detail.py | 4 ++ .../project/endpoints/projects/projects.py | 47 ++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 5913700b..51428bd4 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -10,6 +10,10 @@ class ProjectDetail(Resource): def get(self, **kwargs): + """ + Get method for listing a specific project + filtered by id of that specific project + """ id: int = kwargs['project_id'] print(id) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 19e9a8af..bc06c92e 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -1,8 +1,23 @@ from flask import Blueprint -from flask_restful import Resource, Api +from flask_restful import Resource, Api, reqparse +from sqlalchemy import insert from project import db from project.models.projects import Projects +from project.models.courses import Courses + +parser = reqparse.RequestParser() +# parser.add_argument('id', type=int, help='Unique to charge for this resource') +parser.add_argument('title', type=str, help='Projects title') +parser.add_argument('descriptions', type=str, help='Projects description') +parser.add_argument('assignment_file', type=str, help='Projects assignment file') +parser.add_argument("deadline", type=str, help='Projects deadline') +parser.add_argument("course_id", type=str, help='Projects course_id') +parser.add_argument("visible_for_students", type=bool, help='Projects visibility for students') +parser.add_argument("archieved", type=bool, help='Projects') +parser.add_argument("test_path", type=str, help='Projects test path') +parser.add_argument("script_name", type=str, help='Projects test script path') +parser.add_argument("regex_expressions", type=str, help='Projects regex expressions') projects_bp = Blueprint('projects', __name__) projects_endpoint = Api(projects_bp) @@ -33,5 +48,33 @@ def get(self): return display_data + def post(self): + args = parser.parse_args() + print(args) + + new_project = Projects( + title=args['title'], + descriptions=args['descriptions'], + assignment_file=args['assignment_file'], + deadline=args['deadline'], + course_id=args['course_id'], + visible_for_students=args['visible_for_students'], + archieved=args['archieved'], + test_path=args['test_path'], + script_name=args['script_name'], + regex_expressions=args['regex_expressions'] + ) + + db.session.add(new_project) + db.session.commit() + + return args, 201 + + def delete(self): + # TODO + pass + + def get_successor_id(self): + pass -projects_bp.add_url_rule('/projects/', view_func=Projects_endpoint.as_view('projects')) +projects_bp.add_url_rule('/projects', view_func=Projects_endpoint.as_view('projects')) From aed5d388a4a39eb09fb06bd7446df311b35fc608 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 25 Feb 2024 13:01:00 +0100 Subject: [PATCH 06/82] remove functionality for projects, only works with updated sql script --- .../endpoints/projects/project_detail.py | 19 ++++++++++++++++++- .../project/endpoints/projects/projects.py | 9 +++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 51428bd4..b37f8985 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -1,8 +1,9 @@ -from flask import Blueprint +from flask import Blueprint, request from flask_restful import Resource, Api from project import db from project.models.projects import Projects +from project.models.submissions import Submissions project_detail_bp = Blueprint('project_detail', __name__) project_detail_endpoint = Api(project_detail_bp) @@ -34,5 +35,21 @@ def get(self, **kwargs): return project_dict + def delete(self, **kwargs): + # TODO + # id = request.args + remove_id = kwargs['project_id'] + + submissions = Submissions.query.all() + print(submissions) + + deleted_project = Projects.query.filter_by(project_id=remove_id).first() + # deleted_project = {} + print(deleted_project) + db.session.delete(deleted_project) + db.session.commit() + + return {}, 200 + project_detail_bp.add_url_rule('/projects/', view_func=ProjectDetail.as_view('project_detail')) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index bc06c92e..ab6b7789 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -1,6 +1,6 @@ from flask import Blueprint from flask_restful import Resource, Api, reqparse -from sqlalchemy import insert +from sqlalchemy import insert, delete from project import db from project.models.projects import Projects @@ -49,6 +49,10 @@ def get(self): return display_data def post(self): + """ + Post functionality for project + using flask_restfull parse lib + """ args = parser.parse_args() print(args) @@ -70,9 +74,6 @@ def post(self): return args, 201 - def delete(self): - # TODO - pass def get_successor_id(self): pass From eeb38b5dda76dd1fa306d9eb23cdbead04bc154f Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 25 Feb 2024 13:14:08 +0100 Subject: [PATCH 07/82] added right exit codes --- .../project/endpoints/projects/project_detail.py | 15 +++++++-------- backend/project/endpoints/projects/projects.py | 5 +---- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index b37f8985..328668a2 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -33,19 +33,18 @@ def get(self, **kwargs): "regex_expressions": project.regex_expressions } - return project_dict + return project_dict, 200 def delete(self, **kwargs): - # TODO - # id = request.args - remove_id = kwargs['project_id'] + """ + Detele a project and all of its submissions in cascade + done by project id + """ - submissions = Submissions.query.all() - print(submissions) + remove_id = kwargs['project_id'] deleted_project = Projects.query.filter_by(project_id=remove_id).first() - # deleted_project = {} - print(deleted_project) + db.session.delete(deleted_project) db.session.commit() diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index ab6b7789..4f82a8d5 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -46,7 +46,7 @@ def get(self): } display_data.append(project_dict) - return display_data + return display_data, 200 def post(self): """ @@ -75,7 +75,4 @@ def post(self): return args, 201 - def get_successor_id(self): - pass - projects_bp.add_url_rule('/projects', view_func=Projects_endpoint.as_view('projects')) From e1125c2715d954a6c3aaafadb56601196ec646ae Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 25 Feb 2024 13:50:42 +0100 Subject: [PATCH 08/82] code reformat --- .../endpoints/projects/project_detail.py | 2 +- .../project/endpoints/projects/projects.py | 23 ++++++++----------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 328668a2..f9de8806 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -48,7 +48,7 @@ def delete(self, **kwargs): db.session.delete(deleted_project) db.session.commit() - return {}, 200 + return {"Message": f"Project with id:{remove_id} deleted successfully!"}, 204 project_detail_bp.add_url_rule('/projects/', view_func=ProjectDetail.as_view('project_detail')) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 4f82a8d5..1a9644e0 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -21,6 +21,10 @@ projects_bp = Blueprint('projects', __name__) projects_endpoint = Api(projects_bp) + +project_fields = ['project_id', 'title', 'descriptions', 'assignment_file', + 'deadline', 'course_id', 'visible_for_students', + 'archieved', 'test_path', 'script_name', 'regex_expressions'] class Projects_endpoint(Resource): def get(self): """ @@ -29,23 +33,16 @@ def get(self): """ display_data = [] projects = Projects.query.all() + print(projects) for project in projects: - project_dict = { - "id": project.project_id, - "title": project.title, - "descriptions": project.descriptions, - "assignment_file": project.assignment_file, - "deadline": project.deadline, - "course_id": project.course_id, - "visible_for_students": project.visible_for_students, - "archieved": project.archieved, - "test_path": project.test_path, - "script_name": project.script_name, - "regex_expressions": project.regex_expressions - } + project_dict = {field: value for field, value in project.__dict__.items() if + not field.startswith('_')} + display_data.append(project_dict) + # display_data = [{field, value} for field, value in zip(project_fields, )] + print(display_data) return display_data, 200 def post(self): From 6475ea565fbd2097e9e7b081d55e38f495f67e74 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 25 Feb 2024 13:55:34 +0100 Subject: [PATCH 09/82] added 404 page if you're trying to remove a non-existing project --- .../endpoints/projects/project_detail.py | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index f9de8806..abb08f26 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -19,19 +19,14 @@ def get(self, **kwargs): print(id) project = Projects.query.filter_by(project_id=id).first() - project_dict = { - "id": project.project_id, - "title": project.title, - "descriptions": project.descriptions, - "assignment_file": project.assignment_file, - "deadline": project.deadline, - "course_id": project.course_id, - "visible_for_students": project.visible_for_students, - "archieved": project.archieved, - "test_path": project.test_path, - "script_name": project.script_name, - "regex_expressions": project.regex_expressions - } + + if project is None: + # project doesn't exist so return a 404 error + return {'message': 'Project doesn\'t exist'}, 404 + + print(project) + project_dict = {field: value for field, value in project.__dict__.items() if + not field.startswith('_')} return project_dict, 200 From c6fb5eb6c23b44be1d6955ca9a8e7f9531f812f9 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 25 Feb 2024 14:04:15 +0100 Subject: [PATCH 10/82] 404 function made --- backend/project/endpoints/projects/project_detail.py | 12 ++++++++---- backend/project/endpoints/projects/projects.py | 2 -- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index abb08f26..4acb55a3 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -1,5 +1,5 @@ from flask import Blueprint, request -from flask_restful import Resource, Api +from flask_restful import Resource, Api, abort from project import db from project.models.projects import Projects @@ -10,6 +10,10 @@ class ProjectDetail(Resource): + + def is_existing_project(self, project): + if project is None: + abort(404) def get(self, **kwargs): """ Get method for listing a specific project @@ -20,9 +24,7 @@ def get(self, **kwargs): project = Projects.query.filter_by(project_id=id).first() - if project is None: - # project doesn't exist so return a 404 error - return {'message': 'Project doesn\'t exist'}, 404 + self.is_existing_project(project) print(project) project_dict = {field: value for field, value in project.__dict__.items() if @@ -40,6 +42,8 @@ def delete(self, **kwargs): deleted_project = Projects.query.filter_by(project_id=remove_id).first() + self.is_existing_project(deleted_project) + db.session.delete(deleted_project) db.session.commit() diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 1a9644e0..fb1734d5 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -41,8 +41,6 @@ def get(self): display_data.append(project_dict) - # display_data = [{field, value} for field, value in zip(project_fields, )] - print(display_data) return display_data, 200 def post(self): From 3399e6df2427805365078420ad17e336a9cdca5c Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 25 Feb 2024 17:49:37 +0100 Subject: [PATCH 11/82] added basic add and remove test for project --- .../endpoints/projects/project_detail.py | 2 +- .../project/endpoints/projects/projects.py | 1 - backend/run_tests.sh | 0 backend/tests/endpoints/conftest.py | 8 +++- backend/tests/endpoints/index_test.py | 1 + backend/tests/endpoints/project_test.py | 41 +++++++++++++++++++ 6 files changed, 49 insertions(+), 4 deletions(-) mode change 100644 => 100755 backend/run_tests.sh create mode 100644 backend/tests/endpoints/project_test.py diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 4acb55a3..a68a749c 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -28,7 +28,7 @@ def get(self, **kwargs): print(project) project_dict = {field: value for field, value in project.__dict__.items() if - not field.startswith('_')} + not field.startswith('_')} return project_dict, 200 diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index fb1734d5..8b11d9b3 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -33,7 +33,6 @@ def get(self): """ display_data = [] projects = Projects.query.all() - print(projects) for project in projects: project_dict = {field: value for field, value in project.__dict__.items() if diff --git a/backend/run_tests.sh b/backend/run_tests.sh old mode 100644 new mode 100755 diff --git a/backend/tests/endpoints/conftest.py b/backend/tests/endpoints/conftest.py index 010ef293..cbc73a8e 100644 --- a/backend/tests/endpoints/conftest.py +++ b/backend/tests/endpoints/conftest.py @@ -1,6 +1,9 @@ """ Configuration for pytest, Flask, and the test client.""" +from os import getenv + import pytest -from project import create_app +from project import create_app, create_app_with_db +from dotenv import load_dotenv @pytest.fixture def app(): @@ -8,7 +11,8 @@ def app(): Returns: Flask -- A Flask application instance """ - app = create_app() + load_dotenv() + app = create_app_with_db(getenv("DB_HOST")) yield app @pytest.fixture diff --git a/backend/tests/endpoints/index_test.py b/backend/tests/endpoints/index_test.py index 5624bba7..9553dbda 100644 --- a/backend/tests/endpoints/index_test.py +++ b/backend/tests/endpoints/index_test.py @@ -5,6 +5,7 @@ def test_home(client): response = client.get("/") assert response.status_code == 200 + def test_openapi_spec(client): """Test whether the required fields of the openapi spec are present""" response = client.get("/") diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py new file mode 100644 index 00000000..9d569260 --- /dev/null +++ b/backend/tests/endpoints/project_test.py @@ -0,0 +1,41 @@ +"""Tests for project endpoints.""" +from project.models.projects import Projects + +def test_projects_home(client): + """Test home project endpoint.""" + response = client.get("/projects") + assert response.status_code == 200 + +def test_post_project(client): + """Test adding a user to the datab and fetching it""" + response = client.get("/projects/1") + assert response.status_code == 404 + + data = { + "title": "YourProject for testing purposes 123451", + "descriptions": ["YourProjectDescription"], + "assignment_file": "@/path/to/your/file.txt", + "deadline": "2024-02-25T12:00:00+00:00", + "course_id": 1, + "visible_for_students": 'true', + "archieved": 'false', + "test_path": "YourTestPath", + "script_name": "YourTestScriptName", + "regex_expressions": "Y" + } + + response = client.post("/projects", json=data) + assert response.status_code == 201 + + response = client.get("/projects", json={"title": data["title"]}) + assert response.status_code == 200 + + json_data = response.json + + to_rem = {} + for json in json_data: + if json["title"] == data["title"]: + to_rem = json + response = client.delete(f"/projects/{to_rem['project_id']}") + + assert response.status_code == 204 From 100572abb822c98289c960ecb11fa2dd5fbcc317 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 25 Feb 2024 18:09:33 +0100 Subject: [PATCH 12/82] wrote extra test for just getting the users --- backend/project/endpoints/projects/projects.py | 4 ++++ backend/tests/endpoints/project_test.py | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 8b11d9b3..b05518b9 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -68,5 +68,9 @@ def post(self): return args, 201 + def patch(self): + # TODO + pass + projects_bp.add_url_rule('/projects', view_func=Projects_endpoint.as_view('projects')) diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index 9d569260..cbaab7b7 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -6,7 +6,13 @@ def test_projects_home(client): response = client.get("/projects") assert response.status_code == 200 -def test_post_project(client): +def test_getting_all_projects(client): + """Test getting all projects""" + response = client.get("/projects") + assert response.status_code == 200 + assert isinstance(response.json, list) + +def test_post_remove_project(client): """Test adding a user to the datab and fetching it""" response = client.get("/projects/1") assert response.status_code == 404 From aa0269df04d19df91f810770a2c46ab853409cf9 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 25 Feb 2024 18:51:24 +0100 Subject: [PATCH 13/82] added put for a project --- .../endpoints/projects/project_detail.py | 35 +++++++++++++++++-- .../project/endpoints/projects/projects.py | 4 --- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index a68a749c..22b5a42d 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -1,5 +1,5 @@ from flask import Blueprint, request -from flask_restful import Resource, Api, abort +from flask_restful import Resource, Api, abort, reqparse from project import db from project.models.projects import Projects @@ -8,30 +8,59 @@ project_detail_bp = Blueprint('project_detail', __name__) project_detail_endpoint = Api(project_detail_bp) +parser = reqparse.RequestParser() +# parser.add_argument('id', type=int, help='Unique to charge for this resource') +parser.add_argument('title', type=str, help='Projects title') +parser.add_argument('descriptions', type=str, help='Projects description') +parser.add_argument('assignment_file', type=str, help='Projects assignment file') +parser.add_argument("deadline", type=str, help='Projects deadline') +parser.add_argument("course_id", type=str, help='Projects course_id') +parser.add_argument("visible_for_students", type=bool, help='Projects visibility for students') +parser.add_argument("archieved", type=bool, help='Projects') +parser.add_argument("test_path", type=str, help='Projects test path') +parser.add_argument("script_name", type=str, help='Projects test script path') +parser.add_argument("regex_expressions", type=str, help='Projects regex expressions') + class ProjectDetail(Resource): def is_existing_project(self, project): if project is None: abort(404) + def get(self, **kwargs): """ Get method for listing a specific project filtered by id of that specific project """ id: int = kwargs['project_id'] - print(id) project = Projects.query.filter_by(project_id=id).first() self.is_existing_project(project) - print(project) project_dict = {field: value for field, value in project.__dict__.items() if not field.startswith('_')} return project_dict, 200 + def put(self, **kwargs): + id: int = kwargs['project_id'] + + args = parser.parse_args() + print(args) + + values = {key: value for key, value in args.items() if value is not None} + + project = Projects.query.filter_by(project_id=id).first() # .update(values=values) + + for key, value in args.items(): + if value is not None: + setattr(project, key, value) + + db.session.commit() + return {"Message": f"Project {id} updated succesfully"}, 200 + def delete(self, **kwargs): """ Detele a project and all of its submissions in cascade diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index b05518b9..8b11d9b3 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -68,9 +68,5 @@ def post(self): return args, 201 - def patch(self): - # TODO - pass - projects_bp.add_url_rule('/projects', view_func=Projects_endpoint.as_view('projects')) From 792ddd9a2a39d7121c378636b055997b8ef784d3 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 25 Feb 2024 19:05:55 +0100 Subject: [PATCH 14/82] added test for put --- .../endpoints/projects/project_detail.py | 3 --- backend/tests/endpoints/project_test.py | 22 +++++++++++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 22b5a42d..ce7a5a3b 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -48,9 +48,6 @@ def put(self, **kwargs): id: int = kwargs['project_id'] args = parser.parse_args() - print(args) - - values = {key: value for key, value in args.items() if value is not None} project = Projects.query.filter_by(project_id=id).first() # .update(values=values) diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index cbaab7b7..e6f97e2f 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -14,8 +14,6 @@ def test_getting_all_projects(client): def test_post_remove_project(client): """Test adding a user to the datab and fetching it""" - response = client.get("/projects/1") - assert response.status_code == 404 data = { "title": "YourProject for testing purposes 123451", @@ -45,3 +43,23 @@ def test_post_remove_project(client): response = client.delete(f"/projects/{to_rem['project_id']}") assert response.status_code == 204 + response = client.delete(f"/projects/{to_rem['project_id']}") + assert response.status_code == 404 + +def test_update_project(client): + response = client.get("/projects") + assert response.status_code == 200 + json_data = response.json[0] + + new_archieved = not json_data["archieved"] + new_title = "just patched title" + + response = client.put(f"/projects/{json_data['project_id']}", json={"title": new_title, "archieved": new_archieved}) + + assert response.status_code == 200 + + response = client.get(f"/projects/{json_data['project_id']}") + data = response.json + assert response.status_code == 200 + assert data['title'] == new_title + assert data['archieved'] == new_archieved From 28b82fa5ca78807347b8bf23148c167dc4343ace Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 25 Feb 2024 22:33:44 +0100 Subject: [PATCH 15/82] testing working --- backend/project/__main__.py | 13 ++++++- backend/tests/endpoints/conftest.py | 49 +++++++++++++++++++++++-- backend/tests/endpoints/project_test.py | 15 ++++++++ 3 files changed, 72 insertions(+), 5 deletions(-) diff --git a/backend/project/__main__.py b/backend/project/__main__.py index fd2f3273..c9182b0b 100644 --- a/backend/project/__main__.py +++ b/backend/project/__main__.py @@ -1,6 +1,9 @@ """Main entry point for the application.""" # TODO: remove is for dev purposes from sys import path + +from sqlalchemy import URL + path.append(".") from sys import path @@ -12,5 +15,13 @@ if __name__ == "__main__": load_dotenv() - app = create_app_with_db(getenv("DB_HOST")) + + url = URL.create( + drivername="postgresql", + username=getenv("POSTGRES_USER"), + password=getenv("POSTGRES_PASSWORD"), + host=getenv("POSTGRES_HOST"), + database=getenv("POSTGRES_DB") + ) + app = create_app_with_db(url) app.run(debug=True) diff --git a/backend/tests/endpoints/conftest.py b/backend/tests/endpoints/conftest.py index cbc73a8e..7e3c30f4 100644 --- a/backend/tests/endpoints/conftest.py +++ b/backend/tests/endpoints/conftest.py @@ -1,9 +1,22 @@ """ Configuration for pytest, Flask, and the test client.""" from os import getenv - -import pytest -from project import create_app, create_app_with_db from dotenv import load_dotenv +from sqlalchemy import URL +import pytest +from project import create_app_with_db +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from project import db + +load_dotenv() + +url = URL.create( + drivername="postgresql", + username=getenv("POSTGRES_USER"), + password=getenv("POSTGRES_PASSWORD"), + host=getenv("POSTGRES_HOST"), + database=getenv("POSTGRES_DB") +) @pytest.fixture def app(): @@ -12,9 +25,37 @@ def app(): Flask -- A Flask application instance """ load_dotenv() - app = create_app_with_db(getenv("DB_HOST")) + + url = URL.create( + drivername="postgresql", + username=getenv("POSTGRES_USER"), + password=getenv("POSTGRES_PASSWORD"), + host=getenv("POSTGRES_HOST"), + database=getenv("POSTGRES_DB") + ) + engine = create_engine(url) + Session = sessionmaker(bind=engine) + app = create_app_with_db(url) + db.metadata.create_all(engine) yield app + +engine = create_engine(url) +Session = sessionmaker(bind=engine) + +@pytest.fixture +def db_session(): + """Create a new database session for a test. + After the test, all changes are rolled back and the session is closed.""" + db.metadata.create_all(engine) + session = Session() + yield session + session.rollback() + session.close() + # Truncate all tables + for table in reversed(db.metadata.sorted_tables): + session.execute(table.delete()) + session.commit() @pytest.fixture def client(app): """A fixture that creates a test client for the app. diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index e6f97e2f..bf4f96d5 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -47,6 +47,21 @@ def test_post_remove_project(client): assert response.status_code == 404 def test_update_project(client): + data = { + "title": "YourProject for testing purposes 123451", + "descriptions": ["YourProjectDescription"], + "assignment_file": "@/path/to/your/file.txt", + "deadline": "2024-02-25T12:00:00+00:00", + "course_id": 1, + "visible_for_students": 'true', + "archieved": 'false', + "test_path": "YourTestPath", + "script_name": "YourTestScriptName", + "regex_expressions": "Y" + } + + response = client.post("/projects", json=data) + response = client.get("/projects") assert response.status_code == 200 json_data = response.json[0] From 5db9ded03f5b4584f212a5c77dfcc7fa055c2113 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 25 Feb 2024 22:49:01 +0100 Subject: [PATCH 16/82] added comments to project_test project and projects_detail --- .../endpoints/projects/project_detail.py | 23 +++++++++++++++++-- .../project/endpoints/projects/projects.py | 11 +++++---- backend/tests/endpoints/project_test.py | 10 ++++++++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index ce7a5a3b..1716e85b 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -25,6 +25,10 @@ class ProjectDetail(Resource): def is_existing_project(self, project): + """ + Check if the project exists in the database + and if not abort the request and give back a 404 not found + """ if project is None: abort(404) @@ -32,29 +36,40 @@ def get(self, **kwargs): """ Get method for listing a specific project filtered by id of that specific project + the id fetched from the url with the reaparse """ - id: int = kwargs['project_id'] + # fetch the project with the id that is specified in the url + id: int = kwargs['project_id'] project = Projects.query.filter_by(project_id=id).first() self.is_existing_project(project) + # remove the invalid alchemysql field project_dict = {field: value for field, value in project.__dict__.items() if not field.startswith('_')} + # return the fetched project and return 200 OK status return project_dict, 200 def put(self, **kwargs): + """ + Update method for updating a specific project + filtered by id of that specific project + """ id: int = kwargs['project_id'] args = parser.parse_args() - + # get the project that need to be edited project = Projects.query.filter_by(project_id=id).first() # .update(values=values) + # check which values are not None is the dict + # if it is not None is needs to be modified in the database for key, value in args.items(): if value is not None: setattr(project, key, value) + # commit the changes and return the 200 OK code db.session.commit() return {"Message": f"Project {id} updated succesfully"}, 200 @@ -66,13 +81,17 @@ def delete(self, **kwargs): remove_id = kwargs['project_id'] + # fetch the project that needs to be removed deleted_project = Projects.query.filter_by(project_id=remove_id).first() + # check if its an existing one self.is_existing_project(deleted_project) + # if it exists delete it and commit the changes in the database db.session.delete(deleted_project) db.session.commit() + # return 204 content delted succesfully return {"Message": f"Project with id:{remove_id} deleted successfully!"}, 204 diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 8b11d9b3..b2b43605 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -22,9 +22,7 @@ projects_bp = Blueprint('projects', __name__) projects_endpoint = Api(projects_bp) -project_fields = ['project_id', 'title', 'descriptions', 'assignment_file', - 'deadline', 'course_id', 'visible_for_students', - 'archieved', 'test_path', 'script_name', 'regex_expressions'] + class Projects_endpoint(Resource): def get(self): """ @@ -34,12 +32,14 @@ def get(self): display_data = [] projects = Projects.query.all() + # remove field from alchemysql that has no value for the API itself for project in projects: project_dict = {field: value for field, value in project.__dict__.items() if not field.startswith('_')} display_data.append(project_dict) + # return all valid entries for a project and return a 200 OK code return display_data, 200 def post(self): @@ -50,6 +50,7 @@ def post(self): args = parser.parse_args() print(args) + # create a new project object to add in the API later new_project = Projects( title=args['title'], descriptions=args['descriptions'], @@ -63,10 +64,12 @@ def post(self): regex_expressions=args['regex_expressions'] ) + # add the new project to the database and commit the changes db.session.add(new_project) db.session.commit() - return args, 201 + # 201 content added succesfully + return {"Message": "New project added succesfully"}, 201 projects_bp.add_url_rule('/projects', view_func=Projects_endpoint.as_view('projects')) diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index bf4f96d5..0d706715 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -47,6 +47,11 @@ def test_post_remove_project(client): assert response.status_code == 404 def test_update_project(client): + """ + Test functionality of the PUT method for projects + """ + + # dummy data for testing data = { "title": "YourProject for testing purposes 123451", "descriptions": ["YourProjectDescription"], @@ -60,19 +65,24 @@ def test_update_project(client): "regex_expressions": "Y" } + # post it so it can be edited later on response = client.post("/projects", json=data) + # get the newly added project is no other is present response = client.get("/projects") assert response.status_code == 200 json_data = response.json[0] + # set a new title and flit the archieved boolean new_archieved = not json_data["archieved"] new_title = "just patched title" + # edit the project response = client.put(f"/projects/{json_data['project_id']}", json={"title": new_title, "archieved": new_archieved}) assert response.status_code == 200 + # fetch the project again and check if the values are the newly edited values response = client.get(f"/projects/{json_data['project_id']}") data = response.json assert response.status_code == 200 From 8bd67aed3715c044b701d8e51e65482eaa33d593 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Mon, 26 Feb 2024 18:10:43 +0100 Subject: [PATCH 17/82] added pytest fixture --- backend/tests/endpoints/conftest.py | 17 +++++++++++++++++ backend/tests/endpoints/project_test.py | 4 ++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/backend/tests/endpoints/conftest.py b/backend/tests/endpoints/conftest.py index 7e3c30f4..fdbb82c9 100644 --- a/backend/tests/endpoints/conftest.py +++ b/backend/tests/endpoints/conftest.py @@ -8,6 +8,9 @@ from sqlalchemy.orm import sessionmaker from project import db +from project.models.users import Users +from project.models.courses import Courses + load_dotenv() url = URL.create( @@ -18,6 +21,20 @@ database=getenv("POSTGRES_DB") ) + +@pytest.fixture +def course_teacher(): + """A user that's a teacher for for testing""" + ad_teacher = Users(uid="Gunnar", is_teacher=True, is_admin=True) + return ad_teacher + + +@pytest.fixture +def course(course_teacher): + """A course for testing, with the course teacher as the teacher.""" + ad2 = Courses(name="Ad2", teacher=course_teacher.uid) + return ad2 + @pytest.fixture def app(): """A fixture that creates and configure a new app instance for each test. diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index 0d706715..1014b7ba 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -46,7 +46,7 @@ def test_post_remove_project(client): response = client.delete(f"/projects/{to_rem['project_id']}") assert response.status_code == 404 -def test_update_project(client): +def test_update_project(client, course_teacher, course): """ Test functionality of the PUT method for projects """ @@ -57,7 +57,7 @@ def test_update_project(client): "descriptions": ["YourProjectDescription"], "assignment_file": "@/path/to/your/file.txt", "deadline": "2024-02-25T12:00:00+00:00", - "course_id": 1, + "course_id": course.course_id, "visible_for_students": 'true', "archieved": 'false', "test_path": "YourTestPath", From 3ec266d4b9f22f2a3bc886df07ae617b59a678ac Mon Sep 17 00:00:00 2001 From: gerwoud Date: Mon, 26 Feb 2024 18:21:00 +0100 Subject: [PATCH 18/82] added right dummy data for fixtures --- backend/tests/endpoints/project_test.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index 1014b7ba..2dc2d0f3 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -12,15 +12,20 @@ def test_getting_all_projects(client): assert response.status_code == 200 assert isinstance(response.json, list) -def test_post_remove_project(client): +def test_post_remove_project(db_session, client, course, course_teacher): """Test adding a user to the datab and fetching it""" + db_session.add(course_teacher) + db_session.commit() + db_session.add(course) + db_session.commit() + data = { "title": "YourProject for testing purposes 123451", "descriptions": ["YourProjectDescription"], "assignment_file": "@/path/to/your/file.txt", "deadline": "2024-02-25T12:00:00+00:00", - "course_id": 1, + "course_id": course.course_id, "visible_for_students": 'true', "archieved": 'false', "test_path": "YourTestPath", @@ -46,11 +51,22 @@ def test_post_remove_project(client): response = client.delete(f"/projects/{to_rem['project_id']}") assert response.status_code == 404 -def test_update_project(client, course_teacher, course): +def test_update_project(db_session, client, course, course_teacher): """ Test functionality of the PUT method for projects """ + db_session.add(course_teacher) + db_session.commit() + db_session.add(course) + db_session.commit() + + print("course") + print(course) + + course_id = course.course_id + print(f"course_id: {course_id}") + # dummy data for testing data = { "title": "YourProject for testing purposes 123451", From f9295fcb21cc1a0fe76077f536307b36b60ef263 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Mon, 26 Feb 2024 18:40:08 +0100 Subject: [PATCH 19/82] linter fixes, project endpoint files now score 10/10 --- .../endpoints/projects/project_detail.py | 26 ++++++++++++++----- .../project/endpoints/projects/projects.py | 16 +++++++++--- backend/tests/endpoints/project_test.py | 5 ++-- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 1716e85b..8ad4ad4a 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -1,9 +1,14 @@ -from flask import Blueprint, request +""" +Module for project details page +for example /projects/1 if the project id of +the corresponding project is 1 +""" + +from flask import Blueprint from flask_restful import Resource, Api, abort, reqparse from project import db from project.models.projects import Projects -from project.models.submissions import Submissions project_detail_bp = Blueprint('project_detail', __name__) project_detail_endpoint = Api(project_detail_bp) @@ -23,6 +28,11 @@ class ProjectDetail(Resource): + """ + Class for projects/id endpoints + Inherits from flask_restful.Resource class + for implementing get, delete and put methods + """ def is_existing_project(self, project): """ @@ -40,8 +50,8 @@ def get(self, **kwargs): """ # fetch the project with the id that is specified in the url - id: int = kwargs['project_id'] - project = Projects.query.filter_by(project_id=id).first() + proj_id: int = kwargs['project_id'] + project = Projects.query.filter_by(project_id=proj_id).first() self.is_existing_project(project) @@ -57,11 +67,11 @@ def put(self, **kwargs): Update method for updating a specific project filtered by id of that specific project """ - id: int = kwargs['project_id'] + proj_id: int = kwargs['project_id'] args = parser.parse_args() # get the project that need to be edited - project = Projects.query.filter_by(project_id=id).first() # .update(values=values) + project = Projects.query.filter_by(project_id=proj_id).first() # .update(values=values) # check which values are not None is the dict # if it is not None is needs to be modified in the database @@ -95,4 +105,6 @@ def delete(self, **kwargs): return {"Message": f"Project with id:{remove_id} deleted successfully!"}, 204 -project_detail_bp.add_url_rule('/projects/', view_func=ProjectDetail.as_view('project_detail')) +project_detail_bp.add_url_rule( + '/projects/', + view_func=ProjectDetail.as_view('project_detail')) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index b2b43605..14adf997 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -1,10 +1,13 @@ +""" +Module that implements the /projects endpoint of the API +""" + from flask import Blueprint from flask_restful import Resource, Api, reqparse -from sqlalchemy import insert, delete from project import db from project.models.projects import Projects -from project.models.courses import Courses + parser = reqparse.RequestParser() # parser.add_argument('id', type=int, help='Unique to charge for this resource') @@ -23,7 +26,12 @@ projects_endpoint = Api(projects_bp) -class Projects_endpoint(Resource): +class ProjectsEndpoint(Resource): + """ + Class for projects endpoints + Inherits from flask_restful.Resource class + for implementing get method + """ def get(self): """ Get method for listing all available projects @@ -72,4 +80,4 @@ def post(self): return {"Message": "New project added succesfully"}, 201 -projects_bp.add_url_rule('/projects', view_func=Projects_endpoint.as_view('projects')) +projects_bp.add_url_rule('/projects', view_func=ProjectsEndpoint.as_view('projects')) diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index 2dc2d0f3..58a8422b 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -1,5 +1,4 @@ """Tests for project endpoints.""" -from project.models.projects import Projects def test_projects_home(client): """Test home project endpoint.""" @@ -94,7 +93,9 @@ def test_update_project(db_session, client, course, course_teacher): new_title = "just patched title" # edit the project - response = client.put(f"/projects/{json_data['project_id']}", json={"title": new_title, "archieved": new_archieved}) + response = client.put(f"/projects/{json_data['project_id']}", json={ + "title": new_title, "archieved": new_archieved + }) assert response.status_code == 200 From 3cedf3e747203b21090a73970cdbf0ca57d13f3c Mon Sep 17 00:00:00 2001 From: gerwoud Date: Mon, 26 Feb 2024 18:55:49 +0100 Subject: [PATCH 20/82] more linter fixes --- backend/project/__main__.py | 4 ++-- backend/tests/endpoints/conftest.py | 15 +++++++-------- backend/tests/endpoints/project_test.py | 6 ------ 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/backend/project/__main__.py b/backend/project/__main__.py index c9182b0b..afba560c 100644 --- a/backend/project/__main__.py +++ b/backend/project/__main__.py @@ -1,14 +1,14 @@ """Main entry point for the application.""" # TODO: remove is for dev purposes from sys import path +from os import getenv +from dotenv import load_dotenv from sqlalchemy import URL path.append(".") from sys import path -from os import getenv -from dotenv import load_dotenv from project import create_app_with_db path.append(".") diff --git a/backend/tests/endpoints/conftest.py b/backend/tests/endpoints/conftest.py index fdbb82c9..48c50bd4 100644 --- a/backend/tests/endpoints/conftest.py +++ b/backend/tests/endpoints/conftest.py @@ -1,12 +1,11 @@ """ Configuration for pytest, Flask, and the test client.""" from os import getenv from dotenv import load_dotenv -from sqlalchemy import URL + import pytest -from project import create_app_with_db -from sqlalchemy import create_engine +from project import create_app_with_db, db +from sqlalchemy import create_engine, URL from sqlalchemy.orm import sessionmaker -from project import db from project.models.users import Users from project.models.courses import Courses @@ -43,16 +42,16 @@ def app(): """ load_dotenv() - url = URL.create( + db_url = URL.create( drivername="postgresql", username=getenv("POSTGRES_USER"), password=getenv("POSTGRES_PASSWORD"), host=getenv("POSTGRES_HOST"), database=getenv("POSTGRES_DB") ) - engine = create_engine(url) - Session = sessionmaker(bind=engine) - app = create_app_with_db(url) + engine = create_engine(db_url) + # Session = sessionmaker(bind=engine) + app = create_app_with_db(db_url) db.metadata.create_all(engine) yield app diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index 58a8422b..ea8aa8e4 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -60,12 +60,6 @@ def test_update_project(db_session, client, course, course_teacher): db_session.add(course) db_session.commit() - print("course") - print(course) - - course_id = course.course_id - print(f"course_id: {course_id}") - # dummy data for testing data = { "title": "YourProject for testing purposes 123451", From 7206efaea4cd5a02af6f1837e9f6890a1416c131 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Mon, 26 Feb 2024 19:02:46 +0100 Subject: [PATCH 21/82] linter import order fix --- backend/project/__main__.py | 5 ++--- backend/tests/endpoints/conftest.py | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/backend/project/__main__.py b/backend/project/__main__.py index afba560c..f2739f5f 100644 --- a/backend/project/__main__.py +++ b/backend/project/__main__.py @@ -3,14 +3,13 @@ from sys import path from os import getenv from dotenv import load_dotenv +from project import create_app_with_db + from sqlalchemy import URL path.append(".") -from sys import path -from project import create_app_with_db - path.append(".") if __name__ == "__main__": diff --git a/backend/tests/endpoints/conftest.py b/backend/tests/endpoints/conftest.py index 48c50bd4..b408177c 100644 --- a/backend/tests/endpoints/conftest.py +++ b/backend/tests/endpoints/conftest.py @@ -1,11 +1,11 @@ """ Configuration for pytest, Flask, and the test client.""" from os import getenv from dotenv import load_dotenv - -import pytest -from project import create_app_with_db, db from sqlalchemy import create_engine, URL from sqlalchemy.orm import sessionmaker +from project import create_app_with_db, db + +import pytest from project.models.users import Users from project.models.courses import Courses From b3a53bd48cbeb30fbff3887710caf5fbb62b0307 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Mon, 26 Feb 2024 19:17:15 +0100 Subject: [PATCH 22/82] added ingores for linter --- backend/project/__init__.py | 6 +++--- backend/project/__main__.py | 13 +++++-------- backend/tests/endpoints/conftest.py | 4 ++-- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/backend/project/__init__.py b/backend/project/__init__.py index 66ea1716..bf044e65 100644 --- a/backend/project/__init__.py +++ b/backend/project/__init__.py @@ -13,9 +13,9 @@ def create_app(): Returns: Flask -- A Flask application instance """ - from .endpoints.index.index import index_bp - from .endpoints.projects.projects import projects_bp - from .endpoints.projects.project_detail import project_detail_bp + from .endpoints.index.index import index_bp # pylint: disable=import-outside-toplevel + from .endpoints.projects.projects import projects_bp # pylint: disable=import-outside-toplevel + from .endpoints.projects.project_detail import project_detail_bp # pylint: disable=import-outside-toplevel app = Flask(__name__) app.register_blueprint(index_bp) diff --git a/backend/project/__main__.py b/backend/project/__main__.py index f2739f5f..febd16af 100644 --- a/backend/project/__main__.py +++ b/backend/project/__main__.py @@ -1,16 +1,13 @@ """Main entry point for the application.""" # TODO: remove is for dev purposes from sys import path -from os import getenv -from dotenv import load_dotenv -from project import create_app_with_db - - -from sqlalchemy import URL - path.append(".") +from os import getenv # pylint: disable=wrong-import-position +from sqlalchemy import URL # pylint: disable=wrong-import-position +from project import create_app_with_db # pylint: disable=wrong-import-position +from dotenv import load_dotenv # pylint: disable=wrong-import-position + -path.append(".") if __name__ == "__main__": load_dotenv() diff --git a/backend/tests/endpoints/conftest.py b/backend/tests/endpoints/conftest.py index b408177c..8832a97e 100644 --- a/backend/tests/endpoints/conftest.py +++ b/backend/tests/endpoints/conftest.py @@ -4,11 +4,11 @@ from sqlalchemy import create_engine, URL from sqlalchemy.orm import sessionmaker from project import create_app_with_db, db +from project.models.users import Users +from project.models.courses import Courses import pytest -from project.models.users import Users -from project.models.courses import Courses load_dotenv() From 5be4c0a2b3183ef69397237389a15d5e3936f401 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Mon, 26 Feb 2024 19:23:25 +0100 Subject: [PATCH 23/82] more linter --- backend/project/__main__.py | 3 +-- backend/tests/endpoints/conftest.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/backend/project/__main__.py b/backend/project/__main__.py index febd16af..844faa8a 100644 --- a/backend/project/__main__.py +++ b/backend/project/__main__.py @@ -5,8 +5,7 @@ from os import getenv # pylint: disable=wrong-import-position from sqlalchemy import URL # pylint: disable=wrong-import-position from project import create_app_with_db # pylint: disable=wrong-import-position -from dotenv import load_dotenv # pylint: disable=wrong-import-position - +from dotenv import load_dotenv # pylint: disable=wrong-import-position, wrong-import-order if __name__ == "__main__": diff --git a/backend/tests/endpoints/conftest.py b/backend/tests/endpoints/conftest.py index 8832a97e..7a0596e5 100644 --- a/backend/tests/endpoints/conftest.py +++ b/backend/tests/endpoints/conftest.py @@ -1,13 +1,13 @@ """ Configuration for pytest, Flask, and the test client.""" from os import getenv from dotenv import load_dotenv +import pytest from sqlalchemy import create_engine, URL from sqlalchemy.orm import sessionmaker from project import create_app_with_db, db from project.models.users import Users from project.models.courses import Courses -import pytest load_dotenv() From 061dbd8c73c1a02ef3f5881a16c8f9e8c9128d77 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Mon, 26 Feb 2024 21:40:57 +0100 Subject: [PATCH 24/82] renamed is_existing_project function --- backend/project/__main__.py | 2 +- backend/project/endpoints/projects/project_detail.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/project/__main__.py b/backend/project/__main__.py index 844faa8a..3a3a7fe5 100644 --- a/backend/project/__main__.py +++ b/backend/project/__main__.py @@ -1,5 +1,5 @@ """Main entry point for the application.""" -# TODO: remove is for dev purposes +# TOD: remove is for dev purposes from sys import path path.append(".") from os import getenv # pylint: disable=wrong-import-position diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 8ad4ad4a..12ec60b3 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -34,7 +34,7 @@ class ProjectDetail(Resource): for implementing get, delete and put methods """ - def is_existing_project(self, project): + def abort_if_not_present(self, project): """ Check if the project exists in the database and if not abort the request and give back a 404 not found @@ -53,7 +53,7 @@ def get(self, **kwargs): proj_id: int = kwargs['project_id'] project = Projects.query.filter_by(project_id=proj_id).first() - self.is_existing_project(project) + self.abort_if_not_present(project) # remove the invalid alchemysql field project_dict = {field: value for field, value in project.__dict__.items() if @@ -95,7 +95,7 @@ def delete(self, **kwargs): deleted_project = Projects.query.filter_by(project_id=remove_id).first() # check if its an existing one - self.is_existing_project(deleted_project) + self.abort_if_not_present(deleted_project) # if it exists delete it and commit the changes in the database db.session.delete(deleted_project) From 5b544f9381eb17097af38821496ade575fe5bb37 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Mon, 26 Feb 2024 21:42:07 +0100 Subject: [PATCH 25/82] changed arguments of get functions to not use **kwargs anymore --- backend/project/endpoints/projects/project_detail.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 12ec60b3..cbf72a9b 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -42,7 +42,7 @@ def abort_if_not_present(self, project): if project is None: abort(404) - def get(self, **kwargs): + def get(self, project_id): """ Get method for listing a specific project filtered by id of that specific project @@ -50,8 +50,7 @@ def get(self, **kwargs): """ # fetch the project with the id that is specified in the url - proj_id: int = kwargs['project_id'] - project = Projects.query.filter_by(project_id=proj_id).first() + project = Projects.query.filter_by(project_id=project_id).first() self.abort_if_not_present(project) From 9f5033aeae3886d605427db4132af806d9b42a39 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Mon, 26 Feb 2024 21:55:27 +0100 Subject: [PATCH 26/82] fixed jsonify issue for response --- .../endpoints/projects/project_detail.py | 8 ++---- backend/project/models/projects.py | 25 +++++++++++-------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index cbf72a9b..278c37de 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -4,7 +4,7 @@ the corresponding project is 1 """ -from flask import Blueprint +from flask import Blueprint, jsonify from flask_restful import Resource, Api, abort, reqparse from project import db @@ -54,12 +54,8 @@ def get(self, project_id): self.abort_if_not_present(project) - # remove the invalid alchemysql field - project_dict = {field: value for field, value in project.__dict__.items() if - not field.startswith('_')} - # return the fetched project and return 200 OK status - return project_dict, 200 + return jsonify(project) def put(self, **kwargs): """ diff --git a/backend/project/models/projects.py b/backend/project/models/projects.py index 0dd37911..1ee38401 100644 --- a/backend/project/models/projects.py +++ b/backend/project/models/projects.py @@ -1,8 +1,11 @@ """Model for projects""" +import dataclasses +import datetime from sqlalchemy import ARRAY, Boolean, Column, DateTime, ForeignKey, Integer, String, Text from project import db +@dataclasses.dataclass class Projects(db.Model): """This class describes the projects table, a projects has an id, a title, a description, @@ -14,14 +17,14 @@ class Projects(db.Model): a test path,script name and regex experssions for automated testing""" __tablename__ = "projects" - project_id = Column(Integer, primary_key=True) - title = Column(String(50), nullable=False, unique=False) - descriptions = Column(Text, nullable=False) - assignment_file = Column(String(50)) - deadline = Column(DateTime(timezone=True)) - course_id = Column(Integer, ForeignKey("courses.course_id"), nullable=False) - visible_for_students = Column(Boolean, nullable=False) - archieved = Column(Boolean, nullable=False) - test_path = Column(String(50)) - script_name = Column(String(50)) - regex_expressions = Column(ARRAY(String(50))) + project_id: int = Column(Integer, primary_key=True) + title: str = Column(String(50), nullable=False, unique=False) + descriptions: str = Column(Text, nullable=False) + assignment_file: str = Column(String(50)) + deadline: str = Column(DateTime(timezone=True)) + course_id: datetime.datetime = Column(Integer, ForeignKey("courses.course_id"), nullable=False) + visible_for_students: bool = Column(Boolean, nullable=False) + archieved: bool = Column(Boolean, nullable=False) + test_path: str = Column(String(50)) + script_name: str = Column(String(50)) + regex_expressions: list[str] = Column(ARRAY(String(50))) From dacc4f69c55cbe16430fa07707cb4d872b301c7f Mon Sep 17 00:00:00 2001 From: gerwoud Date: Mon, 26 Feb 2024 22:06:38 +0100 Subject: [PATCH 27/82] added try catch blocks and changed Message to message --- .../endpoints/projects/project_detail.py | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 278c37de..5f559e71 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -9,6 +9,7 @@ from project import db from project.models.projects import Projects +from sqlalchemy import exc project_detail_bp = Blueprint('project_detail', __name__) project_detail_endpoint = Api(project_detail_bp) @@ -57,16 +58,15 @@ def get(self, project_id): # return the fetched project and return 200 OK status return jsonify(project) - def put(self, **kwargs): + def put(self, project_id): """ Update method for updating a specific project filtered by id of that specific project """ - proj_id: int = kwargs['project_id'] args = parser.parse_args() # get the project that need to be edited - project = Projects.query.filter_by(project_id=proj_id).first() # .update(values=values) + project = Projects.query.filter_by(project_id=project_id).first() # .update(values=values) # check which values are not None is the dict # if it is not None is needs to be modified in the database @@ -75,17 +75,20 @@ def put(self, **kwargs): setattr(project, key, value) # commit the changes and return the 200 OK code - db.session.commit() - return {"Message": f"Project {id} updated succesfully"}, 200 + try: + db.session.commit() + return {"message": f"Project {id} updated succesfully"}, 200 + except exc.SQLAlchemyError: + db.session.rollback() + return {"message": f"Something unexpected happenend when trying to edit project {id}"}, 500 - def delete(self, **kwargs): + + def delete(self, remove_id): """ Detele a project and all of its submissions in cascade done by project id """ - remove_id = kwargs['project_id'] - # fetch the project that needs to be removed deleted_project = Projects.query.filter_by(project_id=remove_id).first() @@ -93,11 +96,14 @@ def delete(self, **kwargs): self.abort_if_not_present(deleted_project) # if it exists delete it and commit the changes in the database - db.session.delete(deleted_project) - db.session.commit() - - # return 204 content delted succesfully - return {"Message": f"Project with id:{remove_id} deleted successfully!"}, 204 + try: + db.session.delete(deleted_project) + db.session.commit() + + # return 204 content delted succesfully + return {"message": f"Project with id:{remove_id} deleted successfully!"}, 204 + except exc.SQLAlchemyError: + return {"message": f"Something unexpected happened when removing project {remove_id}"}, 500 project_detail_bp.add_url_rule( From 672aea802def64faf87e9c5ca2c8952874d441f9 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Mon, 26 Feb 2024 22:10:56 +0100 Subject: [PATCH 28/82] applied jsonify on projects --- backend/project/endpoints/projects/projects.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 14adf997..c25fc74e 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -2,7 +2,7 @@ Module that implements the /projects endpoint of the API """ -from flask import Blueprint +from flask import Blueprint, jsonify from flask_restful import Resource, Api, reqparse from project import db @@ -40,15 +40,8 @@ def get(self): display_data = [] projects = Projects.query.all() - # remove field from alchemysql that has no value for the API itself - for project in projects: - project_dict = {field: value for field, value in project.__dict__.items() if - not field.startswith('_')} - - display_data.append(project_dict) - # return all valid entries for a project and return a 200 OK code - return display_data, 200 + return jsonify(projects) def post(self): """ From 167efd31d491b36c2646b56e5e1b11a7e63b71c2 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Mon, 26 Feb 2024 22:11:16 +0100 Subject: [PATCH 29/82] removed print statement --- backend/project/endpoints/projects/projects.py | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index c25fc74e..96c375ca 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -49,7 +49,6 @@ def post(self): using flask_restfull parse lib """ args = parser.parse_args() - print(args) # create a new project object to add in the API later new_project = Projects( From deaaebbbd5e91a4bc0ed9f1bd76aef87b5151435 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Mon, 26 Feb 2024 22:15:18 +0100 Subject: [PATCH 30/82] removed unnecessary session.commit --- backend/project/endpoints/projects/projects.py | 13 +++++++------ backend/tests/endpoints/index_test.py | 1 + backend/tests/endpoints/project_test.py | 1 - 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 96c375ca..510172f4 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -7,7 +7,7 @@ from project import db from project.models.projects import Projects - +from sqlalchemy import exc parser = reqparse.RequestParser() # parser.add_argument('id', type=int, help='Unique to charge for this resource') @@ -65,11 +65,12 @@ def post(self): ) # add the new project to the database and commit the changes - db.session.add(new_project) - db.session.commit() - - # 201 content added succesfully - return {"Message": "New project added succesfully"}, 201 + try: + db.session.add(new_project) + db.session.commit() + return {"message": "New project added succesfully"}, 201 + except exc.SQLAlchemyError: + return {"message": f"Something unexpected happenend when trying to add a new project"}, 500 projects_bp.add_url_rule('/projects', view_func=ProjectsEndpoint.as_view('projects')) diff --git a/backend/tests/endpoints/index_test.py b/backend/tests/endpoints/index_test.py index 9553dbda..8f3a5d4e 100644 --- a/backend/tests/endpoints/index_test.py +++ b/backend/tests/endpoints/index_test.py @@ -1,5 +1,6 @@ """Test the base routes of the application""" + def test_home(client): """Test whether the index page is accesible""" response = client.get("/") diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index ea8aa8e4..dd831658 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -15,7 +15,6 @@ def test_post_remove_project(db_session, client, course, course_teacher): """Test adding a user to the datab and fetching it""" db_session.add(course_teacher) - db_session.commit() db_session.add(course) db_session.commit() From 794a797a1513e51e46e4a3fd34eca0b20119870f Mon Sep 17 00:00:00 2001 From: gerwoud Date: Mon, 26 Feb 2024 22:15:50 +0100 Subject: [PATCH 31/82] test if tests still work on git --- backend/tests/endpoints/project_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index dd831658..b455813f 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -55,7 +55,6 @@ def test_update_project(db_session, client, course, course_teacher): """ db_session.add(course_teacher) - db_session.commit() db_session.add(course) db_session.commit() From 3b3792f9c48ce6c0320732e24b3daed43dfb60c8 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Mon, 26 Feb 2024 22:18:22 +0100 Subject: [PATCH 32/82] added the commit messages again --- backend/tests/endpoints/project_test.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index b455813f..ea8aa8e4 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -15,6 +15,7 @@ def test_post_remove_project(db_session, client, course, course_teacher): """Test adding a user to the datab and fetching it""" db_session.add(course_teacher) + db_session.commit() db_session.add(course) db_session.commit() @@ -55,6 +56,7 @@ def test_update_project(db_session, client, course, course_teacher): """ db_session.add(course_teacher) + db_session.commit() db_session.add(course) db_session.commit() From af7d44d61cbc4c5e961e71b36e0fc7c39fcfbc64 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Tue, 27 Feb 2024 14:28:03 +0100 Subject: [PATCH 33/82] better tests, pray for git tests --- .../endpoints/projects/project_detail.py | 19 +-- .../project/endpoints/projects/projects.py | 4 +- backend/project/models/projects.py | 2 +- backend/tests/endpoints/conftest.py | 46 ++++++- backend/tests/endpoints/project_test.py | 117 +++++++++--------- 5 files changed, 116 insertions(+), 72 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 5f559e71..80442677 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -70,27 +70,27 @@ def put(self, project_id): # check which values are not None is the dict # if it is not None is needs to be modified in the database - for key, value in args.items(): - if value is not None: - setattr(project, key, value) # commit the changes and return the 200 OK code try: + for key, value in args.items(): + if value is not None: + setattr(project, key, value) db.session.commit() - return {"message": f"Project {id} updated succesfully"}, 200 + # get the updated version + return {"message": f"Succesfully changed project with id: {project.project_id}"}, 200 except exc.SQLAlchemyError: db.session.rollback() return {"message": f"Something unexpected happenend when trying to edit project {id}"}, 500 - - def delete(self, remove_id): + def delete(self, project_id): """ Detele a project and all of its submissions in cascade done by project id """ # fetch the project that needs to be removed - deleted_project = Projects.query.filter_by(project_id=remove_id).first() + deleted_project = Projects.query.filter_by(project_id=project_id).first() # check if its an existing one self.abort_if_not_present(deleted_project) @@ -101,9 +101,10 @@ def delete(self, remove_id): db.session.commit() # return 204 content delted succesfully - return {"message": f"Project with id:{remove_id} deleted successfully!"}, 204 + return {"message": f"Project with id:{project_id} deleted successfully!"}, 204 except exc.SQLAlchemyError: - return {"message": f"Something unexpected happened when removing project {remove_id}"}, 500 + print("delete didnt work") + return {"message": f"Something unexpected happened when removing project {project}"}, 500 project_detail_bp.add_url_rule( diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 510172f4..0fcdf5bf 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -64,11 +64,13 @@ def post(self): regex_expressions=args['regex_expressions'] ) + print(new_project) + # add the new project to the database and commit the changes try: db.session.add(new_project) db.session.commit() - return {"message": "New project added succesfully"}, 201 + return jsonify(new_project), 201 except exc.SQLAlchemyError: return {"message": f"Something unexpected happenend when trying to add a new project"}, 500 diff --git a/backend/project/models/projects.py b/backend/project/models/projects.py index 1ee38401..5d904ae4 100644 --- a/backend/project/models/projects.py +++ b/backend/project/models/projects.py @@ -22,7 +22,7 @@ class Projects(db.Model): descriptions: str = Column(Text, nullable=False) assignment_file: str = Column(String(50)) deadline: str = Column(DateTime(timezone=True)) - course_id: datetime.datetime = Column(Integer, ForeignKey("courses.course_id"), nullable=False) + course_id: int = Column(Integer, ForeignKey("courses.course_id"), nullable=False) visible_for_students: bool = Column(Boolean, nullable=False) archieved: bool = Column(Boolean, nullable=False) test_path: str = Column(String(50)) diff --git a/backend/tests/endpoints/conftest.py b/backend/tests/endpoints/conftest.py index 7a0596e5..08a8522f 100644 --- a/backend/tests/endpoints/conftest.py +++ b/backend/tests/endpoints/conftest.py @@ -1,4 +1,5 @@ """ Configuration for pytest, Flask, and the test client.""" +from datetime import datetime from os import getenv from dotenv import load_dotenv import pytest @@ -7,8 +8,7 @@ from project import create_app_with_db, db from project.models.users import Users from project.models.courses import Courses - - +from project.models.projects import Projects load_dotenv() @@ -25,15 +25,52 @@ def course_teacher(): """A user that's a teacher for for testing""" ad_teacher = Users(uid="Gunnar", is_teacher=True, is_admin=True) + print("teacher") + print(ad_teacher) return ad_teacher @pytest.fixture -def course(course_teacher): +def course(course_teacher: Users): """A course for testing, with the course teacher as the teacher.""" + print("teacher id") + print(course_teacher.uid) ad2 = Courses(name="Ad2", teacher=course_teacher.uid) return ad2 + +@pytest.fixture +def project(course): + """A project for testing, with the course as the course it belongs to""" + date = datetime(2024, 2, 25, 12, 0, 0) + project = Projects( + title="Project", + descriptions="Test project", + course_id=course.course_id, + deadline=date, + visible_for_students=True, + archieved=False, + ) + return project + +@pytest.fixture +def project_json(project: Projects): + """A function that return the json data of a project including the PK neede for testing""" + data = { + "title": project.title, + "descriptions": project.descriptions, + "assignment_file": project.assignment_file, + "deadline": project.deadline, + "course_id": project.course_id, + "visible_for_students": project.visible_for_students, + "archieved": project.archieved, + "test_path": project.test_path, + "script_name": project.script_name, + "regex_expressions": project.regex_expressions + } + return data + + @pytest.fixture def app(): """A fixture that creates and configure a new app instance for each test. @@ -59,6 +96,7 @@ def app(): engine = create_engine(url) Session = sessionmaker(bind=engine) + @pytest.fixture def db_session(): """Create a new database session for a test. @@ -72,6 +110,8 @@ def db_session(): for table in reversed(db.metadata.sorted_tables): session.execute(table.delete()) session.commit() + + @pytest.fixture def client(app): """A fixture that creates a test client for the app. diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index ea8aa8e4..92af9443 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -1,101 +1,102 @@ """Tests for project endpoints.""" +from project.models.projects import Projects def test_projects_home(client): """Test home project endpoint.""" response = client.get("/projects") assert response.status_code == 200 + def test_getting_all_projects(client): """Test getting all projects""" response = client.get("/projects") assert response.status_code == 200 assert isinstance(response.json, list) -def test_post_remove_project(db_session, client, course, course_teacher): - """Test adding a user to the datab and fetching it""" +def test_post_project(db_session, client, project, course, course_teacher, project_json): + """Test posting a project to the datab and testing if it's present""" db_session.add(course_teacher) db_session.commit() + db_session.add(course) db_session.commit() - data = { - "title": "YourProject for testing purposes 123451", - "descriptions": ["YourProjectDescription"], - "assignment_file": "@/path/to/your/file.txt", - "deadline": "2024-02-25T12:00:00+00:00", - "course_id": course.course_id, - "visible_for_students": 'true', - "archieved": 'false', - "test_path": "YourTestPath", - "script_name": "YourTestScriptName", - "regex_expressions": "Y" - } - - response = client.post("/projects", json=data) + project.course_id = course.course_id + + project_json["course_id"] = course.course_id + + # post the project + response = client.post("/projects", json=project_json) assert response.status_code == 201 - response = client.get("/projects", json={"title": data["title"]}) + # check if the project with the id is present + project_id = response.json["project_id"] + response = client.get(f"/projects/{project_id}") assert response.status_code == 200 - json_data = response.json - to_rem = {} - for json in json_data: - if json["title"] == data["title"]: - to_rem = json - response = client.delete(f"/projects/{to_rem['project_id']}") +def test_remove_project(db_session, client, project, course, course_teacher, project_json): + """Test removing a project to the datab and fetching it, testing if its not present anymore""" + + db_session.add(course_teacher) + db_session.commit() + + db_session.add(course) + db_session.commit() + + project.course_id = course.course_id + + project_json["course_id"] = course.course_id + + # post the project + response = client.post("/projects", json=project_json) + assert response.status_code == 201 + + # check if the project with the id is present + project_id = response.json["project_id"] + response = client.get(f"/projects/{project_id}") + assert response.status_code == 200 + # check if the 204 status code is returned + response = client.delete(f"/projects/{project_id}") assert response.status_code == 204 - response = client.delete(f"/projects/{to_rem['project_id']}") + + # check if the project isn't present anymore and the delete indeed went through + response = client.delete(f"/projects/{project_id}") assert response.status_code == 404 -def test_update_project(db_session, client, course, course_teacher): + +def test_update_project(db_session, client, course, course_teacher, project, project_json): """ Test functionality of the PUT method for projects """ db_session.add(course_teacher) db_session.commit() + db_session.add(course) db_session.commit() - # dummy data for testing - data = { - "title": "YourProject for testing purposes 123451", - "descriptions": ["YourProjectDescription"], - "assignment_file": "@/path/to/your/file.txt", - "deadline": "2024-02-25T12:00:00+00:00", - "course_id": course.course_id, - "visible_for_students": 'true', - "archieved": 'false', - "test_path": "YourTestPath", - "script_name": "YourTestScriptName", - "regex_expressions": "Y" - } - - # post it so it can be edited later on - response = client.post("/projects", json=data) - - # get the newly added project is no other is present - response = client.get("/projects") - assert response.status_code == 200 - json_data = response.json[0] + project.course_id = course.course_id + project_json["course_id"] = course.course_id + + # post the project to edit + db_session.add(project) + db_session.commit() + project_id = project.project_id - # set a new title and flit the archieved boolean - new_archieved = not json_data["archieved"] - new_title = "just patched title" + new_title = "patched title" + new_archieved = not project.archieved - # edit the project - response = client.put(f"/projects/{json_data['project_id']}", json={ + response = client.put(f"/projects/{project_id}", json={ "title": new_title, "archieved": new_archieved }) + db_session.commit() + # print(project) + print(response) + updated_project = db_session.get(Projects, {"project_id": project.project_id}) assert response.status_code == 200 - - # fetch the project again and check if the values are the newly edited values - response = client.get(f"/projects/{json_data['project_id']}") - data = response.json - assert response.status_code == 200 - assert data['title'] == new_title - assert data['archieved'] == new_archieved + assert updated_project.title == new_title + assert updated_project.archieved == new_archieved From a9ade40e3fc5884852cb88cf54db7c097befa105 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Tue, 27 Feb 2024 16:07:07 +0100 Subject: [PATCH 34/82] fixed TODO of path.append('.') --- backend/project/__main__.py | 3 --- backend/project/endpoints/projects/projects.py | 2 -- 2 files changed, 5 deletions(-) diff --git a/backend/project/__main__.py b/backend/project/__main__.py index 3a3a7fe5..68586e4d 100644 --- a/backend/project/__main__.py +++ b/backend/project/__main__.py @@ -1,7 +1,4 @@ """Main entry point for the application.""" -# TOD: remove is for dev purposes -from sys import path -path.append(".") from os import getenv # pylint: disable=wrong-import-position from sqlalchemy import URL # pylint: disable=wrong-import-position from project import create_app_with_db # pylint: disable=wrong-import-position diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 0fcdf5bf..47e0f445 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -64,8 +64,6 @@ def post(self): regex_expressions=args['regex_expressions'] ) - print(new_project) - # add the new project to the database and commit the changes try: db.session.add(new_project) From 13554faf03c06cc22111b30bfb0619c7de78c798 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Tue, 27 Feb 2024 16:49:00 +0100 Subject: [PATCH 35/82] fixed code duplication for parser (hopefully) --- backend/project/__init__.py | 9 +++--- .../endpoints/projects/endpoint_parser.py | 26 +++++++++++++++++ .../endpoints/projects/project_detail.py | 29 +++++++++---------- .../endpoints/projects/project_endpoint.py | 18 ++++++++++++ .../project/endpoints/projects/projects.py | 11 +++---- 5 files changed, 67 insertions(+), 26 deletions(-) create mode 100644 backend/project/endpoints/projects/endpoint_parser.py create mode 100644 backend/project/endpoints/projects/project_endpoint.py diff --git a/backend/project/__init__.py b/backend/project/__init__.py index bf044e65..be3c9f47 100644 --- a/backend/project/__init__.py +++ b/backend/project/__init__.py @@ -14,13 +14,14 @@ def create_app(): Flask -- A Flask application instance """ from .endpoints.index.index import index_bp # pylint: disable=import-outside-toplevel - from .endpoints.projects.projects import projects_bp # pylint: disable=import-outside-toplevel - from .endpoints.projects.project_detail import project_detail_bp # pylint: disable=import-outside-toplevel + # from .endpoints.projects.projects import projects_bp # pylint: disable=import-outside-toplevel + # from .endpoints.projects.project_detail import project_detail_bp # pylint: disable=import-outside-toplevel + from .endpoints.projects.project_endpoint import project_bp app = Flask(__name__) app.register_blueprint(index_bp) - app.register_blueprint(projects_bp) - app.register_blueprint(project_detail_bp) + app.register_blueprint(project_bp) + # app.register_blueprint(project_detail_bp) return app diff --git a/backend/project/endpoints/projects/endpoint_parser.py b/backend/project/endpoints/projects/endpoint_parser.py new file mode 100644 index 00000000..8c900ef4 --- /dev/null +++ b/backend/project/endpoints/projects/endpoint_parser.py @@ -0,0 +1,26 @@ +from flask_restful import reqparse + + +parser = reqparse.RequestParser() +# parser.add_argument('id', type=int, help='Unique to charge for this resource') +parser.add_argument('title', type=str, help='Projects title') +parser.add_argument('descriptions', type=str, help='Projects description') +parser.add_argument('assignment_file', type=str, help='Projects assignment file') +parser.add_argument("deadline", type=str, help='Projects deadline') +parser.add_argument("course_id", type=str, help='Projects course_id') +parser.add_argument("visible_for_students", type=bool, help='Projects visibility for students') +parser.add_argument("archieved", type=bool, help='Projects') +parser.add_argument("test_path", type=str, help='Projects test path') +parser.add_argument("script_name", type=str, help='Projects test script path') +parser.add_argument("regex_expressions", type=str, help='Projects regex expressions') + + +def parse_project_params(): + args = parser.parse_args() + result_dict = {} + + for key, value in args.items(): + if value is not None: + result_dict[key] = value + + return result_dict diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 80442677..09cebec5 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -4,15 +4,16 @@ the corresponding project is 1 """ -from flask import Blueprint, jsonify -from flask_restful import Resource, Api, abort, reqparse +from flask import jsonify +from flask_restful import Resource, abort, reqparse from project import db from project.models.projects import Projects from sqlalchemy import exc +from project.endpoints.projects.endpoint_parser import parse_project_params -project_detail_bp = Blueprint('project_detail', __name__) -project_detail_endpoint = Api(project_detail_bp) +# project_detail_bp = Blueprint('project_detail', __name__) +# project_detail_endpoint = Api(project_detail_bp) parser = reqparse.RequestParser() # parser.add_argument('id', type=int, help='Unique to charge for this resource') @@ -64,7 +65,7 @@ def put(self, project_id): filtered by id of that specific project """ - args = parser.parse_args() + # args = parser.parse_args() # get the project that need to be edited project = Projects.query.filter_by(project_id=project_id).first() # .update(values=values) @@ -73,9 +74,13 @@ def put(self, project_id): # commit the changes and return the 200 OK code try: - for key, value in args.items(): - if value is not None: - setattr(project, key, value) + # for key, value in args.items(): + # if value is not None: + # setattr(project, key, value) + var_dict = parse_project_params() + print(var_dict) + for key, value in var_dict.items(): + setattr(project, key, value) db.session.commit() # get the updated version return {"message": f"Succesfully changed project with id: {project.project_id}"}, 200 @@ -103,10 +108,4 @@ def delete(self, project_id): # return 204 content delted succesfully return {"message": f"Project with id:{project_id} deleted successfully!"}, 204 except exc.SQLAlchemyError: - print("delete didnt work") - return {"message": f"Something unexpected happened when removing project {project}"}, 500 - - -project_detail_bp.add_url_rule( - '/projects/', - view_func=ProjectDetail.as_view('project_detail')) + return {"message": f"Something unexpected happened when removing project {project_id}"}, 500 diff --git a/backend/project/endpoints/projects/project_endpoint.py b/backend/project/endpoints/projects/project_endpoint.py new file mode 100644 index 00000000..6b5caffd --- /dev/null +++ b/backend/project/endpoints/projects/project_endpoint.py @@ -0,0 +1,18 @@ +from flask import Blueprint +from flask_restful import Api + +from project.endpoints.projects.projects import ProjectsEndpoint +from project.endpoints.projects.project_detail import ProjectDetail + +project_bp = Blueprint('project_endpoint', __name__) +project_endpoint = Api(project_bp) + +project_bp.add_url_rule( + '/projects', + view_func=ProjectsEndpoint.as_view('project_endpoint') +) + +project_bp.add_url_rule( + '/projects/', + view_func=ProjectDetail.as_view('project_detail') +) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 47e0f445..5c559d2d 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -8,6 +8,8 @@ from project import db from project.models.projects import Projects from sqlalchemy import exc +from project.endpoints.projects.endpoint_parser import parse_project_params + parser = reqparse.RequestParser() # parser.add_argument('id', type=int, help='Unique to charge for this resource') @@ -22,9 +24,6 @@ parser.add_argument("script_name", type=str, help='Projects test script path') parser.add_argument("regex_expressions", type=str, help='Projects regex expressions') -projects_bp = Blueprint('projects', __name__) -projects_endpoint = Api(projects_bp) - class ProjectsEndpoint(Resource): """ @@ -48,7 +47,8 @@ def post(self): Post functionality for project using flask_restfull parse lib """ - args = parser.parse_args() + # args = parser.parse_args() + args = parse_project_params() # create a new project object to add in the API later new_project = Projects( @@ -71,6 +71,3 @@ def post(self): return jsonify(new_project), 201 except exc.SQLAlchemyError: return {"message": f"Something unexpected happenend when trying to add a new project"}, 500 - - -projects_bp.add_url_rule('/projects', view_func=ProjectsEndpoint.as_view('projects')) From f41cd841faf5634f6615cec848043ebc1ed8f7e6 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Tue, 27 Feb 2024 17:09:22 +0100 Subject: [PATCH 36/82] fixed non existing fields in testing bug --- backend/tests/endpoints/conftest.py | 4 ++++ backend/tests/endpoints/project_test.py | 2 ++ 2 files changed, 6 insertions(+) diff --git a/backend/tests/endpoints/conftest.py b/backend/tests/endpoints/conftest.py index 08a8522f..a3cf296a 100644 --- a/backend/tests/endpoints/conftest.py +++ b/backend/tests/endpoints/conftest.py @@ -47,9 +47,13 @@ def project(course): title="Project", descriptions="Test project", course_id=course.course_id, + assignment_file="testfile", deadline=date, visible_for_students=True, archieved=False, + test_path="testpad", + script_name="testscript", + regex_expressions='r' ) return project diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index 92af9443..54dab03f 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -27,6 +27,8 @@ def test_post_project(db_session, client, project, course, course_teacher, proje project_json["course_id"] = course.course_id # post the project + print("project json") + print(project_json) response = client.post("/projects", json=project_json) assert response.status_code == 201 From e27d270d74ae059046af1f0916d6a0f785fb149b Mon Sep 17 00:00:00 2001 From: gerwoud Date: Tue, 27 Feb 2024 17:13:11 +0100 Subject: [PATCH 37/82] fixed parse code duplication --- .../endpoints/projects/project_detail.py | 16 ---------------- backend/project/endpoints/projects/projects.py | 18 ++---------------- 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 09cebec5..9c1d64c6 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -12,22 +12,6 @@ from sqlalchemy import exc from project.endpoints.projects.endpoint_parser import parse_project_params -# project_detail_bp = Blueprint('project_detail', __name__) -# project_detail_endpoint = Api(project_detail_bp) - -parser = reqparse.RequestParser() -# parser.add_argument('id', type=int, help='Unique to charge for this resource') -parser.add_argument('title', type=str, help='Projects title') -parser.add_argument('descriptions', type=str, help='Projects description') -parser.add_argument('assignment_file', type=str, help='Projects assignment file') -parser.add_argument("deadline", type=str, help='Projects deadline') -parser.add_argument("course_id", type=str, help='Projects course_id') -parser.add_argument("visible_for_students", type=bool, help='Projects visibility for students') -parser.add_argument("archieved", type=bool, help='Projects') -parser.add_argument("test_path", type=str, help='Projects test path') -parser.add_argument("script_name", type=str, help='Projects test script path') -parser.add_argument("regex_expressions", type=str, help='Projects regex expressions') - class ProjectDetail(Resource): """ diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 5c559d2d..9a28b710 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -2,8 +2,8 @@ Module that implements the /projects endpoint of the API """ -from flask import Blueprint, jsonify -from flask_restful import Resource, Api, reqparse +from flask import jsonify +from flask_restful import Resource, reqparse from project import db from project.models.projects import Projects @@ -11,20 +11,6 @@ from project.endpoints.projects.endpoint_parser import parse_project_params -parser = reqparse.RequestParser() -# parser.add_argument('id', type=int, help='Unique to charge for this resource') -parser.add_argument('title', type=str, help='Projects title') -parser.add_argument('descriptions', type=str, help='Projects description') -parser.add_argument('assignment_file', type=str, help='Projects assignment file') -parser.add_argument("deadline", type=str, help='Projects deadline') -parser.add_argument("course_id", type=str, help='Projects course_id') -parser.add_argument("visible_for_students", type=bool, help='Projects visibility for students') -parser.add_argument("archieved", type=bool, help='Projects') -parser.add_argument("test_path", type=str, help='Projects test path') -parser.add_argument("script_name", type=str, help='Projects test script path') -parser.add_argument("regex_expressions", type=str, help='Projects regex expressions') - - class ProjectsEndpoint(Resource): """ Class for projects endpoints From 56af6bc729c502eb6d6fab9f6789851eee095d10 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Tue, 27 Feb 2024 17:26:29 +0100 Subject: [PATCH 38/82] linter fixes --- .../project/endpoints/projects/endpoint_parser.py | 8 +++++++- .../project/endpoints/projects/project_detail.py | 14 +++++++++----- .../project/endpoints/projects/project_endpoint.py | 5 +++++ backend/project/endpoints/projects/projects.py | 9 +++++---- backend/project/models/projects.py | 1 - backend/tests/endpoints/project_test.py | 11 +++-------- 6 files changed, 29 insertions(+), 19 deletions(-) diff --git a/backend/project/endpoints/projects/endpoint_parser.py b/backend/project/endpoints/projects/endpoint_parser.py index 8c900ef4..c54455d9 100644 --- a/backend/project/endpoints/projects/endpoint_parser.py +++ b/backend/project/endpoints/projects/endpoint_parser.py @@ -1,5 +1,8 @@ -from flask_restful import reqparse +""" +Parser for the argument when posting or patching a project +""" +from flask_restful import reqparse parser = reqparse.RequestParser() # parser.add_argument('id', type=int, help='Unique to charge for this resource') @@ -16,6 +19,9 @@ def parse_project_params(): + """ + Return a dict of every non None value in the param + """ args = parser.parse_args() result_dict = {} diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 9c1d64c6..96d63b47 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -5,12 +5,12 @@ """ from flask import jsonify -from flask_restful import Resource, abort, reqparse +from flask_restful import Resource, abort +from sqlalchemy import exc +from project.endpoints.projects.endpoint_parser import parse_project_params from project import db from project.models.projects import Projects -from sqlalchemy import exc -from project.endpoints.projects.endpoint_parser import parse_project_params class ProjectDetail(Resource): @@ -70,7 +70,9 @@ def put(self, project_id): return {"message": f"Succesfully changed project with id: {project.project_id}"}, 200 except exc.SQLAlchemyError: db.session.rollback() - return {"message": f"Something unexpected happenend when trying to edit project {id}"}, 500 + return ({"message": + f"Something unexpected happenend when trying to edit project {id}"}, + 500) def delete(self, project_id): """ @@ -92,4 +94,6 @@ def delete(self, project_id): # return 204 content delted succesfully return {"message": f"Project with id:{project_id} deleted successfully!"}, 204 except exc.SQLAlchemyError: - return {"message": f"Something unexpected happened when removing project {project_id}"}, 500 + return ({"message": + f"Something unexpected happened when removing project {project_id}"}, + 500) diff --git a/backend/project/endpoints/projects/project_endpoint.py b/backend/project/endpoints/projects/project_endpoint.py index 6b5caffd..e915ad14 100644 --- a/backend/project/endpoints/projects/project_endpoint.py +++ b/backend/project/endpoints/projects/project_endpoint.py @@ -1,3 +1,8 @@ +""" +Module for providing the blueprint to the api +of both route +""" + from flask import Blueprint from flask_restful import Api diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 9a28b710..ab6ee925 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -3,11 +3,11 @@ """ from flask import jsonify -from flask_restful import Resource, reqparse +from flask_restful import Resource +from sqlalchemy import exc from project import db from project.models.projects import Projects -from sqlalchemy import exc from project.endpoints.projects.endpoint_parser import parse_project_params @@ -22,7 +22,6 @@ def get(self): Get method for listing all available projects that are currently in the API """ - display_data = [] projects = Projects.query.all() # return all valid entries for a project and return a 200 OK code @@ -56,4 +55,6 @@ def post(self): db.session.commit() return jsonify(new_project), 201 except exc.SQLAlchemyError: - return {"message": f"Something unexpected happenend when trying to add a new project"}, 500 + return ({"message": + "Something unexpected happenend when trying to add a new project"}, + 500) diff --git a/backend/project/models/projects.py b/backend/project/models/projects.py index 5d904ae4..cfb8deac 100644 --- a/backend/project/models/projects.py +++ b/backend/project/models/projects.py @@ -1,6 +1,5 @@ """Model for projects""" import dataclasses -import datetime from sqlalchemy import ARRAY, Boolean, Column, DateTime, ForeignKey, Integer, String, Text from project import db diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index 54dab03f..ea83cd9d 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -14,7 +14,7 @@ def test_getting_all_projects(client): assert isinstance(response.json, list) -def test_post_project(db_session, client, project, course, course_teacher, project_json): +def test_post_project(db_session, client, course, course_teacher, project_json): """Test posting a project to the datab and testing if it's present""" db_session.add(course_teacher) db_session.commit() @@ -22,8 +22,6 @@ def test_post_project(db_session, client, project, course, course_teacher, proje db_session.add(course) db_session.commit() - project.course_id = course.course_id - project_json["course_id"] = course.course_id # post the project @@ -38,7 +36,7 @@ def test_post_project(db_session, client, project, course, course_teacher, proje assert response.status_code == 200 -def test_remove_project(db_session, client, project, course, course_teacher, project_json): +def test_remove_project(db_session, client, course, course_teacher, project_json): """Test removing a project to the datab and fetching it, testing if its not present anymore""" db_session.add(course_teacher) @@ -47,8 +45,6 @@ def test_remove_project(db_session, client, project, course, course_teacher, pro db_session.add(course) db_session.commit() - project.course_id = course.course_id - project_json["course_id"] = course.course_id # post the project @@ -69,7 +65,7 @@ def test_remove_project(db_session, client, project, course, course_teacher, pro assert response.status_code == 404 -def test_update_project(db_session, client, course, course_teacher, project, project_json): +def test_update_project(db_session, client, course, course_teacher, project): """ Test functionality of the PUT method for projects """ @@ -81,7 +77,6 @@ def test_update_project(db_session, client, course, course_teacher, project, pro db_session.commit() project.course_id = course.course_id - project_json["course_id"] = course.course_id # post the project to edit db_session.add(project) From f90830339d68a1b1c022399e840be5ecab004485 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Tue, 27 Feb 2024 17:37:27 +0100 Subject: [PATCH 39/82] more linter fixes --- backend/project/models/projects.py | 8 ++++++-- backend/tests/endpoints/conftest.py | 8 +------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/backend/project/models/projects.py b/backend/project/models/projects.py index cfb8deac..08cb8e05 100644 --- a/backend/project/models/projects.py +++ b/backend/project/models/projects.py @@ -5,7 +5,7 @@ from project import db @dataclasses.dataclass -class Projects(db.Model): +class Projects(db.Model): # pylint: disable=too-many-instance-attributes """This class describes the projects table, a projects has an id, a title, a description, an optional assignment file that can contain more explanation of the projects, @@ -13,7 +13,11 @@ class Projects(db.Model): the course id of the course to which the project belongs, visible for students variable so a teacher can decide if the students can see it yet, archieved var so we can implement the archiving functionality, - a test path,script name and regex experssions for automated testing""" + a test path,script name and regex experssions for automated testing + + Pylint disalbe too many intance attributes because we can't reduce the amount + of fields of the model + """ __tablename__ = "projects" project_id: int = Column(Integer, primary_key=True) diff --git a/backend/tests/endpoints/conftest.py b/backend/tests/endpoints/conftest.py index a3cf296a..a0ae9f65 100644 --- a/backend/tests/endpoints/conftest.py +++ b/backend/tests/endpoints/conftest.py @@ -83,13 +83,7 @@ def app(): """ load_dotenv() - db_url = URL.create( - drivername="postgresql", - username=getenv("POSTGRES_USER"), - password=getenv("POSTGRES_PASSWORD"), - host=getenv("POSTGRES_HOST"), - database=getenv("POSTGRES_DB") - ) + db_url = url engine = create_engine(db_url) # Session = sessionmaker(bind=engine) app = create_app_with_db(db_url) From d10fcf68210fad308a79d02388af8f17b7847443 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Wed, 28 Feb 2024 10:35:51 +0100 Subject: [PATCH 40/82] removed linter disables --- backend/project/__main__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/project/__main__.py b/backend/project/__main__.py index 68586e4d..d8b36744 100644 --- a/backend/project/__main__.py +++ b/backend/project/__main__.py @@ -1,8 +1,8 @@ """Main entry point for the application.""" -from os import getenv # pylint: disable=wrong-import-position -from sqlalchemy import URL # pylint: disable=wrong-import-position -from project import create_app_with_db # pylint: disable=wrong-import-position -from dotenv import load_dotenv # pylint: disable=wrong-import-position, wrong-import-order +from os import getenv +from sqlalchemy import URL +from project import create_app_with_db +from dotenv import load_dotenv if __name__ == "__main__": From 054f90d5aa2e334cbc83f7fca99391e8cf238950 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Wed, 28 Feb 2024 10:36:54 +0100 Subject: [PATCH 41/82] fixed linter import order --- backend/project/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/project/__main__.py b/backend/project/__main__.py index d8b36744..15c9d5e3 100644 --- a/backend/project/__main__.py +++ b/backend/project/__main__.py @@ -1,8 +1,8 @@ """Main entry point for the application.""" from os import getenv +from dotenv import load_dotenv from sqlalchemy import URL from project import create_app_with_db -from dotenv import load_dotenv if __name__ == "__main__": From dd0f7fe43a565fca21cf64a6e59ff94ad2902b06 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Wed, 28 Feb 2024 10:38:07 +0100 Subject: [PATCH 42/82] removed unused imports --- backend/project/__init__.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/backend/project/__init__.py b/backend/project/__init__.py index be3c9f47..b076b24d 100644 --- a/backend/project/__init__.py +++ b/backend/project/__init__.py @@ -14,14 +14,11 @@ def create_app(): Flask -- A Flask application instance """ from .endpoints.index.index import index_bp # pylint: disable=import-outside-toplevel - # from .endpoints.projects.projects import projects_bp # pylint: disable=import-outside-toplevel - # from .endpoints.projects.project_detail import project_detail_bp # pylint: disable=import-outside-toplevel from .endpoints.projects.project_endpoint import project_bp app = Flask(__name__) app.register_blueprint(index_bp) app.register_blueprint(project_bp) - # app.register_blueprint(project_detail_bp) return app From 0b58babf11e6772e1791ad6c130022a64ea56f5c Mon Sep 17 00:00:00 2001 From: gerwoud Date: Wed, 28 Feb 2024 10:38:48 +0100 Subject: [PATCH 43/82] removed unused comment --- backend/project/endpoints/projects/endpoint_parser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/project/endpoints/projects/endpoint_parser.py b/backend/project/endpoints/projects/endpoint_parser.py index c54455d9..87f61e69 100644 --- a/backend/project/endpoints/projects/endpoint_parser.py +++ b/backend/project/endpoints/projects/endpoint_parser.py @@ -5,7 +5,6 @@ from flask_restful import reqparse parser = reqparse.RequestParser() -# parser.add_argument('id', type=int, help='Unique to charge for this resource') parser.add_argument('title', type=str, help='Projects title') parser.add_argument('descriptions', type=str, help='Projects description') parser.add_argument('assignment_file', type=str, help='Projects assignment file') From 0e73d09e15a1be607ca541fbe6b32b9cbb2e6e0d Mon Sep 17 00:00:00 2001 From: gerwoud Date: Wed, 28 Feb 2024 10:40:07 +0100 Subject: [PATCH 44/82] removed print statement and unused comments --- backend/project/endpoints/projects/project_detail.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 96d63b47..d074555a 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -49,20 +49,15 @@ def put(self, project_id): filtered by id of that specific project """ - # args = parser.parse_args() # get the project that need to be edited project = Projects.query.filter_by(project_id=project_id).first() # .update(values=values) # check which values are not None is the dict # if it is not None is needs to be modified in the database - # commit the changes and return the 200 OK code + # commit the changes and return the 200 OK code if it succeeds, else 500 try: - # for key, value in args.items(): - # if value is not None: - # setattr(project, key, value) var_dict = parse_project_params() - print(var_dict) for key, value in var_dict.items(): setattr(project, key, value) db.session.commit() From 3b8aee33075112fb700100308b78adb065ce1c2a Mon Sep 17 00:00:00 2001 From: gerwoud Date: Wed, 28 Feb 2024 10:41:03 +0100 Subject: [PATCH 45/82] changed put to patch --- backend/project/endpoints/projects/project_detail.py | 2 +- backend/tests/endpoints/project_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index d074555a..27693f6c 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -43,7 +43,7 @@ def get(self, project_id): # return the fetched project and return 200 OK status return jsonify(project) - def put(self, project_id): + def patch(self, project_id): """ Update method for updating a specific project filtered by id of that specific project diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index ea83cd9d..1fe1fa59 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -86,7 +86,7 @@ def test_update_project(db_session, client, course, course_teacher, project): new_title = "patched title" new_archieved = not project.archieved - response = client.put(f"/projects/{project_id}", json={ + response = client.patch(f"/projects/{project_id}", json={ "title": new_title, "archieved": new_archieved }) db_session.commit() From 42f20f7860af74fdb97d3de06e86278927f0d193 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Wed, 28 Feb 2024 10:42:24 +0100 Subject: [PATCH 46/82] fixed wrong status code --- backend/project/endpoints/projects/project_detail.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 27693f6c..c19e429d 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -86,8 +86,8 @@ def delete(self, project_id): db.session.delete(deleted_project) db.session.commit() - # return 204 content delted succesfully - return {"message": f"Project with id:{project_id} deleted successfully!"}, 204 + # return 200 if content is deleted succesfully + return {"message": f"Project with id:{project_id} deleted successfully!"}, 200 except exc.SQLAlchemyError: return ({"message": f"Something unexpected happened when removing project {project_id}"}, From d16b10fcf97da2358a116d6d90c6ab78a298e27d Mon Sep 17 00:00:00 2001 From: gerwoud Date: Wed, 28 Feb 2024 10:43:12 +0100 Subject: [PATCH 47/82] removed useless comment --- backend/project/endpoints/projects/projects.py | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index ab6ee925..a3b97ed9 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -32,7 +32,6 @@ def post(self): Post functionality for project using flask_restfull parse lib """ - # args = parser.parse_args() args = parse_project_params() # create a new project object to add in the API later From 60ae98b92e62f5bc8b1823643db62aca55f924a6 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Wed, 28 Feb 2024 11:09:43 +0100 Subject: [PATCH 48/82] added message field to return json --- backend/project/endpoints/projects/projects.py | 7 ++++++- backend/tests/endpoints/conftest.py | 1 - backend/tests/endpoints/project_test.py | 6 ++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index a3b97ed9..b88d3fe5 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -49,10 +49,15 @@ def post(self): ) # add the new project to the database and commit the changes + try: + print("new project") + print(new_project) db.session.add(new_project) db.session.commit() - return jsonify(new_project), 201 + new_project_json = jsonify(new_project).json + new_project_json["message"] = "Project posted successfully" + return new_project_json, 201 except exc.SQLAlchemyError: return ({"message": "Something unexpected happenend when trying to add a new project"}, diff --git a/backend/tests/endpoints/conftest.py b/backend/tests/endpoints/conftest.py index a0ae9f65..b77a36ee 100644 --- a/backend/tests/endpoints/conftest.py +++ b/backend/tests/endpoints/conftest.py @@ -85,7 +85,6 @@ def app(): db_url = url engine = create_engine(db_url) - # Session = sessionmaker(bind=engine) app = create_app_with_db(db_url) db.metadata.create_all(engine) yield app diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index 1fe1fa59..2637d5f5 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -25,8 +25,6 @@ def test_post_project(db_session, client, course, course_teacher, project_json): project_json["course_id"] = course.course_id # post the project - print("project json") - print(project_json) response = client.post("/projects", json=project_json) assert response.status_code == 201 @@ -56,9 +54,9 @@ def test_remove_project(db_session, client, course, course_teacher, project_json response = client.get(f"/projects/{project_id}") assert response.status_code == 200 - # check if the 204 status code is returned + # check if the 200 status code is returned response = client.delete(f"/projects/{project_id}") - assert response.status_code == 204 + assert response.status_code == 200 # check if the project isn't present anymore and the delete indeed went through response = client.delete(f"/projects/{project_id}") From ae817d622e19cb3d5aa930645ea26fc2ad3485d4 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Wed, 28 Feb 2024 13:38:04 +0100 Subject: [PATCH 49/82] removed all fields from /projects get except project_id, title and descriptions --- backend/project/endpoints/projects/projects.py | 7 +++---- backend/tests/endpoints/project_test.py | 4 ---- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index b88d3fe5..5e7a80af 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -22,11 +22,10 @@ def get(self): Get method for listing all available projects that are currently in the API """ - projects = Projects.query.all() - + projects = Projects.query.with_entities(Projects.project_id, Projects.title, Projects.descriptions).all() + results = [tuple(row) for row in projects] # return all valid entries for a project and return a 200 OK code - return jsonify(projects) - + return results def post(self): """ Post functionality for project diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index 2637d5f5..d6acd096 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -47,14 +47,10 @@ def test_remove_project(db_session, client, course, course_teacher, project_json # post the project response = client.post("/projects", json=project_json) - assert response.status_code == 201 # check if the project with the id is present project_id = response.json["project_id"] - response = client.get(f"/projects/{project_id}") - assert response.status_code == 200 - # check if the 200 status code is returned response = client.delete(f"/projects/{project_id}") assert response.status_code == 200 From f6d2157be139913b31fe8cb5ee90ed25a1f1d3b2 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Wed, 28 Feb 2024 13:44:27 +0100 Subject: [PATCH 50/82] added try catch block to post of projects endpoint --- backend/project/endpoints/projects/projects.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 5e7a80af..56527b45 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -22,10 +22,17 @@ def get(self): Get method for listing all available projects that are currently in the API """ - projects = Projects.query.with_entities(Projects.project_id, Projects.title, Projects.descriptions).all() - results = [tuple(row) for row in projects] - # return all valid entries for a project and return a 200 OK code - return results + try: + projects = Projects.query.with_entities(Projects.project_id, Projects.title, Projects.descriptions).all() + results = [tuple(row) for row in projects] + # return all valid entries for a project and return a 200 OK code + return results, 200 + except exc.SQLAlchemyError: + return ({"message": + "Something unexpected happenend when trying to get the projects"}, + 500) + + def post(self): """ Post functionality for project From 7a397f1a119c9968b9b2aba39d2989e30e62210c Mon Sep 17 00:00:00 2001 From: gerwoud Date: Wed, 28 Feb 2024 14:13:43 +0100 Subject: [PATCH 51/82] removed print statements --- backend/project/endpoints/projects/projects.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 56527b45..a76759e5 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -17,6 +17,7 @@ class ProjectsEndpoint(Resource): Inherits from flask_restful.Resource class for implementing get method """ + def get(self): """ Get method for listing all available projects @@ -29,10 +30,9 @@ def get(self): return results, 200 except exc.SQLAlchemyError: return ({"message": - "Something unexpected happenend when trying to get the projects"}, + "Something unexpected happenend when trying to get the projects"}, 500) - def post(self): """ Post functionality for project @@ -57,8 +57,6 @@ def post(self): # add the new project to the database and commit the changes try: - print("new project") - print(new_project) db.session.add(new_project) db.session.commit() new_project_json = jsonify(new_project).json @@ -66,5 +64,5 @@ def post(self): return new_project_json, 201 except exc.SQLAlchemyError: return ({"message": - "Something unexpected happenend when trying to add a new project"}, + "Something unexpected happenend when trying to add a new project"}, 500) From 8a1f467cf5f9f4980870cee1e3431358cb46b7bb Mon Sep 17 00:00:00 2001 From: gerwoud Date: Wed, 28 Feb 2024 14:18:14 +0100 Subject: [PATCH 52/82] removed commented code --- backend/project/endpoints/projects/project_detail.py | 2 +- backend/project/endpoints/projects/projects.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index c19e429d..10bfc5b0 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -50,7 +50,7 @@ def patch(self, project_id): """ # get the project that need to be edited - project = Projects.query.filter_by(project_id=project_id).first() # .update(values=values) + project = Projects.query.filter_by(project_id=project_id).first() # check which values are not None is the dict # if it is not None is needs to be modified in the database diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index a76759e5..0303b769 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -25,6 +25,7 @@ def get(self): """ try: projects = Projects.query.with_entities(Projects.project_id, Projects.title, Projects.descriptions).all() + results = [tuple(row) for row in projects] # return all valid entries for a project and return a 200 OK code return results, 200 From d5b07bb778835897d450061a5c434daccb8229ec Mon Sep 17 00:00:00 2001 From: gerwoud Date: Wed, 28 Feb 2024 15:08:16 +0100 Subject: [PATCH 53/82] changed the get from tuple to a json --- backend/project/endpoints/projects/projects.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 0303b769..19cd0989 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -5,12 +5,13 @@ from flask import jsonify from flask_restful import Resource from sqlalchemy import exc +from sqlalchemy.orm import load_only + from project import db from project.models.projects import Projects from project.endpoints.projects.endpoint_parser import parse_project_params - class ProjectsEndpoint(Resource): """ Class for projects endpoints @@ -26,7 +27,12 @@ def get(self): try: projects = Projects.query.with_entities(Projects.project_id, Projects.title, Projects.descriptions).all() - results = [tuple(row) for row in projects] + results = [{ + "project_id": row[0], + "title": row[1], + "descriptions": row[2] + } for row in projects] + # return all valid entries for a project and return a 200 OK code return results, 200 except exc.SQLAlchemyError: From de7a616b70751d36508d99c9bd0ed2357b93a461 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Wed, 28 Feb 2024 15:13:28 +0100 Subject: [PATCH 54/82] linter fix --- backend/project/endpoints/projects/projects.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 19cd0989..61578907 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -5,7 +5,6 @@ from flask import jsonify from flask_restful import Resource from sqlalchemy import exc -from sqlalchemy.orm import load_only from project import db @@ -25,7 +24,11 @@ def get(self): that are currently in the API """ try: - projects = Projects.query.with_entities(Projects.project_id, Projects.title, Projects.descriptions).all() + projects = Projects.query.with_entities( + Projects.project_id, + Projects.title, + Projects.descriptions + ).all() results = [{ "project_id": row[0], From 7648d81a7f24113fddc97a10f9fd1e4d13184f00 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Thu, 29 Feb 2024 16:05:48 +0100 Subject: [PATCH 55/82] edited OpenAPI_Object --- .../endpoints/index/OpenAPI_Object.json | 191 +++++++++++++++++- .../endpoints/projects/project_detail.py | 2 +- 2 files changed, 191 insertions(+), 2 deletions(-) diff --git a/backend/project/endpoints/index/OpenAPI_Object.json b/backend/project/endpoints/index/OpenAPI_Object.json index 7243ff59..9ccb9aaf 100644 --- a/backend/project/endpoints/index/OpenAPI_Object.json +++ b/backend/project/endpoints/index/OpenAPI_Object.json @@ -41,5 +41,194 @@ } ] }, - "paths": [] + "paths": [ + { + "/projects": { + "get": { + "description": "Returns all projects from the database that the user has acces to", + "responses": { + "200": { + "description": "A list of projects", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "project_id": { + "type": "int" + }, + "descriptions": { + "type": "string" + }, + "title": { + "type": "string" + } + } + } + } + } + } + } + } + }, + "post": { + "description": "Upload a new proejct", + "responses": { + "201": { + "description": "Uploaded a new project succesfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": "string" + } + } + } + } + } + } + } + }, + "/projects/{id}": { + "get": { + "description": "Return a project with corresponding id", + "responses": { + "200": { + "description": "A user with corresponding id", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "archieved": { + "type": "bool" + }, + "assignment_file": { + "type": "string" + }, + "course_id": { + "type": "int" + }, + "deadline": { + "type": "date" + }, + "descriptions": { + "type": "array", + "items": { + "description": "string" + } + }, + "project_id": { + "type": "int" + }, + "regex_expressions": { + "type": "array", + "items": { + "regex": "string" + } + }, + "script_name": { + "type": "string" + }, + "test_path": { + "type": "string" + }, + "title": { + "type": "string" + }, + "visible_for_students": { + "type": "bool" + } + } + } + } + } + }, + "404": { + "description": "An id that doens't correspond to a existing user", + "content": { + "application/json": { + "schema": { + "type": "ojbect", + "properties": { + "message": { + "type": "string" + } + } + } + } + } + } + } + }, + "patch": { + "description": "Patch certain fields op a project", + "responses": { + "200": { + "description": "Patched a project succesfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": "string" + } + } + } + } + }, + "404": { + "description": "Tried to patch a user that is not present", + "content": { + "application/json": { + "schema": { + "type": "ojbect", + "properties": { + "message": { + "type": "string" + } + } + } + } + } + } + } + }, + "delete": { + "description": "Delete a project with given id", + "responses": { + "200": { + "description": "Removed a project succesfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": "string" + } + } + } + } + }, + "404": { + "description": "Tried to remove a project that is not present", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": "string" + } + } + } + } + } + } + } + } + } + ] } diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 10bfc5b0..c74efda5 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -41,7 +41,7 @@ def get(self, project_id): self.abort_if_not_present(project) # return the fetched project and return 200 OK status - return jsonify(project) + return jsonify(project), 200 def patch(self, project_id): """ From c2dfb3f03a3b76bda7325a417ce0d2dabb0e9dc1 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Thu, 29 Feb 2024 16:16:35 +0100 Subject: [PATCH 56/82] linter fix --- backend/__init__.py | 0 backend/project/__init__.py | 7 +++---- backend/project/db_in.py | 6 ++++++ 3 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 backend/__init__.py create mode 100644 backend/project/db_in.py diff --git a/backend/__init__.py b/backend/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/project/__init__.py b/backend/project/__init__.py index b076b24d..58a286a7 100644 --- a/backend/project/__init__.py +++ b/backend/project/__init__.py @@ -3,9 +3,10 @@ """ from flask import Flask -from flask_sqlalchemy import SQLAlchemy -db = SQLAlchemy() +from .db_in import db +from .endpoints.index.index import index_bp +from .endpoints.projects.project_endpoint import project_bp def create_app(): """ @@ -13,8 +14,6 @@ def create_app(): Returns: Flask -- A Flask application instance """ - from .endpoints.index.index import index_bp # pylint: disable=import-outside-toplevel - from .endpoints.projects.project_endpoint import project_bp app = Flask(__name__) app.register_blueprint(index_bp) diff --git a/backend/project/db_in.py b/backend/project/db_in.py new file mode 100644 index 00000000..3a35e5ed --- /dev/null +++ b/backend/project/db_in.py @@ -0,0 +1,6 @@ +"""fix for circular import statements""" + +from flask_sqlalchemy import SQLAlchemy + + +db = SQLAlchemy() From 1d23226a65c9e0834803d0e5fb7bf80d16a55615 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Thu, 29 Feb 2024 17:19:36 +0100 Subject: [PATCH 57/82] fixed no module named project error --- backend/__init__.py | 0 backend/project/__init__.py | 9 ++++++--- backend/project/__main__.py | 2 ++ backend/project/db_in.py | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) delete mode 100644 backend/__init__.py diff --git a/backend/__init__.py b/backend/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/backend/project/__init__.py b/backend/project/__init__.py index 58a286a7..4e79276a 100644 --- a/backend/project/__init__.py +++ b/backend/project/__init__.py @@ -3,10 +3,11 @@ """ from flask import Flask +from flask_sqlalchemy import SQLAlchemy +# from .db_in import db -from .db_in import db -from .endpoints.index.index import index_bp -from .endpoints.projects.project_endpoint import project_bp + +db = SQLAlchemy() def create_app(): """ @@ -14,6 +15,8 @@ def create_app(): Returns: Flask -- A Flask application instance """ + from .endpoints.index.index import index_bp + from .endpoints.projects.project_endpoint import project_bp app = Flask(__name__) app.register_blueprint(index_bp) diff --git a/backend/project/__main__.py b/backend/project/__main__.py index 15c9d5e3..ab2f9ab4 100644 --- a/backend/project/__main__.py +++ b/backend/project/__main__.py @@ -1,9 +1,11 @@ """Main entry point for the application.""" +from sys import path from os import getenv from dotenv import load_dotenv from sqlalchemy import URL from project import create_app_with_db +path.append(".") if __name__ == "__main__": load_dotenv() diff --git a/backend/project/db_in.py b/backend/project/db_in.py index 3a35e5ed..d2deb9b3 100644 --- a/backend/project/db_in.py +++ b/backend/project/db_in.py @@ -1,5 +1,5 @@ -"""fix for circular import statements""" +"""db initialization""" from flask_sqlalchemy import SQLAlchemy From 320030e0d46a25e9bdea51722688382caa0f209e Mon Sep 17 00:00:00 2001 From: gerwoud Date: Thu, 29 Feb 2024 17:50:27 +0100 Subject: [PATCH 58/82] added file sessionmaker for code duplication purposes --- backend/project/__init__.py | 9 +++------ backend/project/__main__.py | 10 +--------- backend/project/db_in.py | 1 - backend/project/sessionmaker.py | 18 ++++++++++++++++++ backend/tests/endpoints/conftest.py | 28 +++++++--------------------- backend/tests/models/conftest.py | 23 +---------------------- 6 files changed, 30 insertions(+), 59 deletions(-) create mode 100644 backend/project/sessionmaker.py diff --git a/backend/project/__init__.py b/backend/project/__init__.py index 4e79276a..aedb0614 100644 --- a/backend/project/__init__.py +++ b/backend/project/__init__.py @@ -3,20 +3,17 @@ """ from flask import Flask -from flask_sqlalchemy import SQLAlchemy -# from .db_in import db +from .db_in import db +from .endpoints.index.index import index_bp +from .endpoints.projects.project_endpoint import project_bp -db = SQLAlchemy() - def create_app(): """ Create a Flask application instance. Returns: Flask -- A Flask application instance """ - from .endpoints.index.index import index_bp - from .endpoints.projects.project_endpoint import project_bp app = Flask(__name__) app.register_blueprint(index_bp) diff --git a/backend/project/__main__.py b/backend/project/__main__.py index ab2f9ab4..7d109ed8 100644 --- a/backend/project/__main__.py +++ b/backend/project/__main__.py @@ -1,21 +1,13 @@ """Main entry point for the application.""" from sys import path -from os import getenv from dotenv import load_dotenv -from sqlalchemy import URL from project import create_app_with_db +from .sessionmaker import url path.append(".") if __name__ == "__main__": load_dotenv() - url = URL.create( - drivername="postgresql", - username=getenv("POSTGRES_USER"), - password=getenv("POSTGRES_PASSWORD"), - host=getenv("POSTGRES_HOST"), - database=getenv("POSTGRES_DB") - ) app = create_app_with_db(url) app.run(debug=True) diff --git a/backend/project/db_in.py b/backend/project/db_in.py index d2deb9b3..9cbda056 100644 --- a/backend/project/db_in.py +++ b/backend/project/db_in.py @@ -1,4 +1,3 @@ - """db initialization""" from flask_sqlalchemy import SQLAlchemy diff --git a/backend/project/sessionmaker.py b/backend/project/sessionmaker.py new file mode 100644 index 00000000..9fbf1cad --- /dev/null +++ b/backend/project/sessionmaker.py @@ -0,0 +1,18 @@ +"""initialise a datab session""" +from os import getenv +from dotenv import load_dotenv +from sqlalchemy import create_engine, URL +from sqlalchemy.orm import sessionmaker + +load_dotenv() + +url = URL.create( + drivername="postgresql", + username=getenv("POSTGRES_USER"), + password=getenv("POSTGRES_PASSWORD"), + host=getenv("POSTGRES_HOST"), + database=getenv("POSTGRES_DB") +) + +engine = create_engine(url) +Session = sessionmaker(bind=engine) diff --git a/backend/tests/endpoints/conftest.py b/backend/tests/endpoints/conftest.py index b77a36ee..7f5d285c 100644 --- a/backend/tests/endpoints/conftest.py +++ b/backend/tests/endpoints/conftest.py @@ -1,40 +1,24 @@ """ Configuration for pytest, Flask, and the test client.""" from datetime import datetime -from os import getenv from dotenv import load_dotenv import pytest -from sqlalchemy import create_engine, URL -from sqlalchemy.orm import sessionmaker from project import create_app_with_db, db from project.models.users import Users from project.models.courses import Courses from project.models.projects import Projects - -load_dotenv() - -url = URL.create( - drivername="postgresql", - username=getenv("POSTGRES_USER"), - password=getenv("POSTGRES_PASSWORD"), - host=getenv("POSTGRES_HOST"), - database=getenv("POSTGRES_DB") -) +from project.sessionmaker import engine, Session, url @pytest.fixture def course_teacher(): """A user that's a teacher for for testing""" ad_teacher = Users(uid="Gunnar", is_teacher=True, is_admin=True) - print("teacher") - print(ad_teacher) return ad_teacher @pytest.fixture def course(course_teacher: Users): """A course for testing, with the course teacher as the teacher.""" - print("teacher id") - print(course_teacher.uid) ad2 = Courses(name="Ad2", teacher=course_teacher.uid) return ad2 @@ -75,6 +59,11 @@ def project_json(project: Projects): return data + +# engine = create_engine(url) +# Session = sessionmaker(bind=engine) + + @pytest.fixture def app(): """A fixture that creates and configure a new app instance for each test. @@ -84,15 +73,12 @@ def app(): load_dotenv() db_url = url - engine = create_engine(db_url) + # engine = create_engine(db_url) app = create_app_with_db(db_url) db.metadata.create_all(engine) yield app -engine = create_engine(url) -Session = sessionmaker(bind=engine) - @pytest.fixture def db_session(): diff --git a/backend/tests/models/conftest.py b/backend/tests/models/conftest.py index 150d433e..8b4baa4a 100644 --- a/backend/tests/models/conftest.py +++ b/backend/tests/models/conftest.py @@ -2,36 +2,15 @@ Configuration for the models tests. Contains all the fixtures needed for multiple models tests. """ -import os from datetime import datetime -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker -from sqlalchemy.engine.url import URL -from dotenv import load_dotenv import pytest from project import db from project.models.courses import Courses from project.models.course_relations import CourseAdmins, CourseStudents from project.models.projects import Projects from project.models.users import Users +from project.sessionmaker import Session, engine -load_dotenv() - -DATABSE_NAME = os.getenv('POSTGRES_DB') -DATABASE_USER = os.getenv('POSTGRES_USER') -DATABASE_PASSWORD = os.getenv('POSTGRES_PASSWORD') -DATABASE_HOST = os.getenv('POSTGRES_HOST') - -url = URL.create( - drivername="postgresql", - username=DATABASE_USER, - host=DATABASE_HOST, - database=DATABSE_NAME, - password=DATABASE_PASSWORD -) - -engine = create_engine(url) -Session = sessionmaker(bind=engine) @pytest.fixture def db_session(): From 41400afb44dcefd3eb1a7399dd0c6c33b1a3d142 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Thu, 29 Feb 2024 18:33:07 +0100 Subject: [PATCH 59/82] Thanks for this linter for helping me improve my code :) --- backend/tests/endpoints/conftest.py | 23 ++--------------------- backend/tests/models/conftest.py | 16 ---------------- 2 files changed, 2 insertions(+), 37 deletions(-) diff --git a/backend/tests/endpoints/conftest.py b/backend/tests/endpoints/conftest.py index 7f5d285c..7e396c4d 100644 --- a/backend/tests/endpoints/conftest.py +++ b/backend/tests/endpoints/conftest.py @@ -6,7 +6,7 @@ from project.models.users import Users from project.models.courses import Courses from project.models.projects import Projects -from project.sessionmaker import engine, Session, url +from project.sessionmaker import engine, url @pytest.fixture @@ -41,6 +41,7 @@ def project(course): ) return project + @pytest.fixture def project_json(project: Projects): """A function that return the json data of a project including the PK neede for testing""" @@ -59,11 +60,6 @@ def project_json(project: Projects): return data - -# engine = create_engine(url) -# Session = sessionmaker(bind=engine) - - @pytest.fixture def app(): """A fixture that creates and configure a new app instance for each test. @@ -80,21 +76,6 @@ def app(): -@pytest.fixture -def db_session(): - """Create a new database session for a test. - After the test, all changes are rolled back and the session is closed.""" - db.metadata.create_all(engine) - session = Session() - yield session - session.rollback() - session.close() - # Truncate all tables - for table in reversed(db.metadata.sorted_tables): - session.execute(table.delete()) - session.commit() - - @pytest.fixture def client(app): """A fixture that creates a test client for the app. diff --git a/backend/tests/models/conftest.py b/backend/tests/models/conftest.py index 8b4baa4a..39a3479f 100644 --- a/backend/tests/models/conftest.py +++ b/backend/tests/models/conftest.py @@ -4,28 +4,12 @@ from datetime import datetime import pytest -from project import db from project.models.courses import Courses from project.models.course_relations import CourseAdmins, CourseStudents from project.models.projects import Projects from project.models.users import Users -from project.sessionmaker import Session, engine -@pytest.fixture -def db_session(): - """Create a new database session for a test. - After the test, all changes are rolled back and the session is closed.""" - db.metadata.create_all(engine) - session = Session() - yield session - session.rollback() - session.close() - # Truncate all tables - for table in reversed(db.metadata.sorted_tables): - session.execute(table.delete()) - session.commit() - @pytest.fixture def valid_user(): """A valid user for testing""" From 04d8976d7ffe8ad5e1843ecdc400de26448e6a34 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Thu, 29 Feb 2024 18:35:21 +0100 Subject: [PATCH 60/82] added root point conftest.py --- backend/tests/conftest.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 backend/tests/conftest.py diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py new file mode 100644 index 00000000..148ef6f2 --- /dev/null +++ b/backend/tests/conftest.py @@ -0,0 +1,18 @@ +"""root level fixtures""" +import pytest +from project.sessionmaker import engine, Session +from project import db + +@pytest.fixture +def db_session(): + """Create a new database session for a test. + After the test, all changes are rolled back and the session is closed.""" + db.metadata.create_all(engine) + session = Session() + yield session + session.rollback() + session.close() + # Truncate all tables + for table in reversed(db.metadata.sorted_tables): + session.execute(table.delete()) + session.commit() From d74f764e8bed3e16cd3c2c68b2b817919a674577 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Fri, 1 Mar 2024 16:17:19 +0100 Subject: [PATCH 61/82] added root point conftest.py --- backend/project/endpoints/projects/projects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 61578907..5d7f0eae 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -37,7 +37,7 @@ def get(self): } for row in projects] # return all valid entries for a project and return a 200 OK code - return results, 200 + return jsonify(results), 200 except exc.SQLAlchemyError: return ({"message": "Something unexpected happenend when trying to get the projects"}, From a72ef103acc49923db286b4d139fe47f2c764195 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Fri, 1 Mar 2024 16:25:06 +0100 Subject: [PATCH 62/82] added try catch block --- .../project/endpoints/projects/project_detail.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index c74efda5..0654f102 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -35,13 +35,18 @@ def get(self, project_id): the id fetched from the url with the reaparse """ - # fetch the project with the id that is specified in the url - project = Projects.query.filter_by(project_id=project_id).first() + try: + # fetch the project with the id that is specified in the url + project = Projects.query.filter_by(project_id=project_id).first() - self.abort_if_not_present(project) + self.abort_if_not_present(project) - # return the fetched project and return 200 OK status - return jsonify(project), 200 + # return the fetched project and return 200 OK status + return jsonify(project), 200 + except exc.SQLAlchemyError: + return ({"message": + "Internal server error"}, + 500) def patch(self, project_id): """ From 15fa782a2df6f4cbf4984ed150a0a9025a69b77b Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sat, 2 Mar 2024 12:54:44 +0100 Subject: [PATCH 63/82] removed commented code --- backend/tests/endpoints/conftest.py | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/tests/endpoints/conftest.py b/backend/tests/endpoints/conftest.py index 7e396c4d..7d8b8fc4 100644 --- a/backend/tests/endpoints/conftest.py +++ b/backend/tests/endpoints/conftest.py @@ -69,7 +69,6 @@ def app(): load_dotenv() db_url = url - # engine = create_engine(db_url) app = create_app_with_db(db_url) db.metadata.create_all(engine) yield app From 85cce0bbd421d37ea59d677c7f800d50cbb0d8c3 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sat, 2 Mar 2024 12:55:07 +0100 Subject: [PATCH 64/82] removed print statements --- backend/tests/endpoints/project_test.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index d6acd096..877ee757 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -84,8 +84,6 @@ def test_update_project(db_session, client, course, course_teacher, project): "title": new_title, "archieved": new_archieved }) db_session.commit() - # print(project) - print(response) updated_project = db_session.get(Projects, {"project_id": project.project_id}) assert response.status_code == 200 From 87255c1d780db73b217adfd80871cc382aacee5c Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sat, 2 Mar 2024 13:00:01 +0100 Subject: [PATCH 65/82] return whole URL of project instead of just the id --- backend/project/endpoints/projects/project_detail.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 0654f102..7a02925e 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -3,6 +3,8 @@ for example /projects/1 if the project id of the corresponding project is 1 """ +from os import getenv +from dotenv import load_dotenv from flask import jsonify from flask_restful import Resource, abort @@ -12,6 +14,7 @@ from project import db from project.models.projects import Projects +load_dotenv() class ProjectDetail(Resource): """ @@ -67,7 +70,7 @@ def patch(self, project_id): setattr(project, key, value) db.session.commit() # get the updated version - return {"message": f"Succesfully changed project with id: {project.project_id}"}, 200 + return {"message": f"Succesfully changed project with id: {getenv('API_HOST')}/projects/{id}"}, 200 except exc.SQLAlchemyError: db.session.rollback() return ({"message": @@ -92,7 +95,7 @@ def delete(self, project_id): db.session.commit() # return 200 if content is deleted succesfully - return {"message": f"Project with id:{project_id} deleted successfully!"}, 200 + return {"message": f"Project with id: {getenv('API_HOST')}/projects/{id} deleted successfully!"}, 200 except exc.SQLAlchemyError: return ({"message": f"Something unexpected happened when removing project {project_id}"}, From c994225afdd7c1ae6bae3ee20030230b4bd2913d Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sat, 2 Mar 2024 13:03:22 +0100 Subject: [PATCH 66/82] small linter fixes :) --- backend/project/endpoints/projects/project_detail.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 7a02925e..202a58af 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -70,7 +70,9 @@ def patch(self, project_id): setattr(project, key, value) db.session.commit() # get the updated version - return {"message": f"Succesfully changed project with id: {getenv('API_HOST')}/projects/{id}"}, 200 + return {"message": f"Succesfully changed project with id: " + f"{getenv('API_HOST')}/projects/{id}" + }, 200 except exc.SQLAlchemyError: db.session.rollback() return ({"message": @@ -95,7 +97,9 @@ def delete(self, project_id): db.session.commit() # return 200 if content is deleted succesfully - return {"message": f"Project with id: {getenv('API_HOST')}/projects/{id} deleted successfully!"}, 200 + return ({"message": f"Project with id: " + f"{getenv('API_HOST')}/projects/{id} deleted successfully!"}, + 200) except exc.SQLAlchemyError: return ({"message": f"Something unexpected happened when removing project {project_id}"}, From 4b667bf2f1ed3f14df5f1704be607707356d5f55 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sat, 2 Mar 2024 14:36:16 +0100 Subject: [PATCH 67/82] load the env variable at the start of file as constant --- backend/project/endpoints/projects/project_detail.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 202a58af..fd86af32 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -15,6 +15,7 @@ from project.models.projects import Projects load_dotenv() +API_URL = getenv('API_HOST') class ProjectDetail(Resource): """ @@ -71,7 +72,7 @@ def patch(self, project_id): db.session.commit() # get the updated version return {"message": f"Succesfully changed project with id: " - f"{getenv('API_HOST')}/projects/{id}" + f"{API_URL}/projects/{id}" }, 200 except exc.SQLAlchemyError: db.session.rollback() @@ -98,7 +99,7 @@ def delete(self, project_id): # return 200 if content is deleted succesfully return ({"message": f"Project with id: " - f"{getenv('API_HOST')}/projects/{id} deleted successfully!"}, + f"{API_URL}/projects/{id} deleted successfully!"}, 200) except exc.SQLAlchemyError: return ({"message": From ec323bb71347ccbe790a536390e9b7286ba9c94a Mon Sep 17 00:00:00 2001 From: Aron Buzogany Date: Sat, 2 Mar 2024 18:47:47 +0100 Subject: [PATCH 68/82] paths attribute should be an object, not a list --- .../endpoints/index/OpenAPI_Object.json | 306 +++++++++--------- 1 file changed, 152 insertions(+), 154 deletions(-) diff --git a/backend/project/endpoints/index/OpenAPI_Object.json b/backend/project/endpoints/index/OpenAPI_Object.json index 9ccb9aaf..ec64c9cf 100644 --- a/backend/project/endpoints/index/OpenAPI_Object.json +++ b/backend/project/endpoints/index/OpenAPI_Object.json @@ -41,30 +41,28 @@ } ] }, - "paths": [ - { - "/projects": { - "get": { - "description": "Returns all projects from the database that the user has acces to", - "responses": { - "200": { - "description": "A list of projects", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "object", - "properties": { - "project_id": { - "type": "int" - }, - "descriptions": { - "type": "string" - }, - "title": { - "type": "string" - } + "paths": { + "/projects": { + "get": { + "description": "Returns all projects from the database that the user has acces to", + "responses": { + "200": { + "description": "A list of projects", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "project_id": { + "type": "int" + }, + "descriptions": { + "type": "string" + }, + "title": { + "type": "string" } } } @@ -72,163 +70,163 @@ } } } - }, - "post": { - "description": "Upload a new proejct", - "responses": { - "201": { - "description": "Uploaded a new project succesfully", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": "string" - } + } + }, + "post": { + "description": "Upload a new proejct", + "responses": { + "201": { + "description": "Uploaded a new project succesfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": "string" } } } } } } - }, - "/projects/{id}": { - "get": { - "description": "Return a project with corresponding id", - "responses": { - "200": { - "description": "A user with corresponding id", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "archieved": { - "type": "bool" - }, - "assignment_file": { - "type": "string" - }, - "course_id": { - "type": "int" - }, - "deadline": { - "type": "date" - }, - "descriptions": { - "type": "array", - "items": { - "description": "string" - } - }, - "project_id": { - "type": "int" - }, - "regex_expressions": { - "type": "array", - "items": { - "regex": "string" - } - }, - "script_name": { - "type": "string" - }, - "test_path": { - "type": "string" - }, - "title": { - "type": "string" - }, - "visible_for_students": { - "type": "bool" - } + } + }, + "/projects/{id}": { + "get": { + "description": "Return a project with corresponding id", + "responses": { + "200": { + "description": "A user with corresponding id", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "archieved": { + "type": "bool" + }, + "assignment_file": { + "type": "string" + }, + "course_id": { + "type": "int" + }, + "deadline": { + "type": "date" + }, + "descriptions": { + "type": "array", + "items": { + "description": "string" } - } - } - } - }, - "404": { - "description": "An id that doens't correspond to a existing user", - "content": { - "application/json": { - "schema": { - "type": "ojbect", - "properties": { - "message": { - "type": "string" - } + }, + "project_id": { + "type": "int" + }, + "regex_expressions": { + "type": "array", + "items": { + "regex": "string" } + }, + "script_name": { + "type": "string" + }, + "test_path": { + "type": "string" + }, + "title": { + "type": "string" + }, + "visible_for_students": { + "type": "bool" } } } } } }, - "patch": { - "description": "Patch certain fields op a project", - "responses": { - "200": { - "description": "Patched a project succesfully", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": "string" - } + "404": { + "description": "An id that doens't correspond to a existing user", + "content": { + "application/json": { + "schema": { + "type": "ojbect", + "properties": { + "message": { + "type": "string" } } } - }, - "404": { - "description": "Tried to patch a user that is not present", - "content": { - "application/json": { - "schema": { - "type": "ojbect", - "properties": { - "message": { - "type": "string" - } - } - } + } + } + } + } + }, + "patch": { + "description": "Patch certain fields op a project", + "responses": { + "200": { + "description": "Patched a project succesfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": "string" } } } } }, - "delete": { - "description": "Delete a project with given id", - "responses": { - "200": { - "description": "Removed a project succesfully", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": "string" + "404": { + "description": "Tried to patch a user that is not present", + "content": { + "application/json": { + "schema": { + "type": "ojbect", + "properties": { + "message": { + "type": "string" } } } } - }, - "404": { - "description": "Tried to remove a project that is not present", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": "string" - } + } + } + } + }, + "delete": { + "description": "Delete a project with given id", + "responses": { + "200": { + "description": "Removed a project succesfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": "string" } - } - } - } - } - } + } + } + } + }, + "404": { + "description": "Tried to remove a project that is not present", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": "string" + } + } + } + } + } + } } } - ] + } } From 09c21178dfd336c5e152011fb30e0d8adb11a82a Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 3 Mar 2024 11:39:56 +0100 Subject: [PATCH 69/82] patched patch function --- backend/project/endpoints/projects/project_detail.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index fd86af32..b09cf5a2 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -71,9 +71,10 @@ def patch(self, project_id): setattr(project, key, value) db.session.commit() # get the updated version - return {"message": f"Succesfully changed project with id: " - f"{API_URL}/projects/{id}" - }, 200 + return {"message": f"Succesfully changed project with id: {id}", + "url": f"{API_URL}/projects/{id}", + "project"D: project + }, 200 except exc.SQLAlchemyError: db.session.rollback() return ({"message": From f992a552aa6e91ee449e61611f90a49b1d0e11e7 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 3 Mar 2024 11:40:19 +0100 Subject: [PATCH 70/82] fixed typo in patch --- backend/project/endpoints/projects/project_detail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index b09cf5a2..abbc7d9a 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -73,7 +73,7 @@ def patch(self, project_id): # get the updated version return {"message": f"Succesfully changed project with id: {id}", "url": f"{API_URL}/projects/{id}", - "project"D: project + "project": project }, 200 except exc.SQLAlchemyError: db.session.rollback() From 81b27406bfd5524fb3ad2e84731b8429adfab3bb Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 3 Mar 2024 11:42:45 +0100 Subject: [PATCH 71/82] fixed json return in 500 patch message --- backend/project/endpoints/projects/project_detail.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index abbc7d9a..f102409c 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -73,12 +73,14 @@ def patch(self, project_id): # get the updated version return {"message": f"Succesfully changed project with id: {id}", "url": f"{API_URL}/projects/{id}", - "project": project + "data": project }, 200 except exc.SQLAlchemyError: db.session.rollback() return ({"message": - f"Something unexpected happenend when trying to edit project {id}"}, + f"Something unexpected happenend when trying to edit project {id}", + "url": f"{API_URL}/projects/{id}", + "data": project}, 500) def delete(self, project_id): From c68b01ac64a416183960debe6cc90c5bb40c4e52 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 3 Mar 2024 11:48:10 +0100 Subject: [PATCH 72/82] fixed url returns --- backend/project/endpoints/projects/project_detail.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index f102409c..65b58307 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -101,10 +101,11 @@ def delete(self, project_id): db.session.commit() # return 200 if content is deleted succesfully - return ({"message": f"Project with id: " - f"{API_URL}/projects/{id} deleted successfully!"}, + return ({"message": f"Project with id: {id} deleted successfully", + "url": f"{API_URL}/projects/{id} deleted successfully!",}, 200) except exc.SQLAlchemyError: return ({"message": - f"Something unexpected happened when removing project {project_id}"}, + f"Something unexpected happened when removing project {project_id}", + "url": f"{API_URL}/projects/{id}"}, 500) From 3cfa66f11861659f777cbd16f2af49dfe88f5c9d Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 3 Mar 2024 11:49:10 +0100 Subject: [PATCH 73/82] fix: wrapped project json with seperate message --- backend/project/endpoints/projects/projects.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 5d7f0eae..edb11f4e 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -70,8 +70,11 @@ def post(self): db.session.add(new_project) db.session.commit() new_project_json = jsonify(new_project).json - new_project_json["message"] = "Project posted successfully" - return new_project_json, 201 + + return { + "project": new_project_json, + "message": "Project posted successfully" + }, 201 except exc.SQLAlchemyError: return ({"message": "Something unexpected happenend when trying to add a new project"}, From eabcc853bbfeb82824492b1d16517df4ef9ecc2b Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 3 Mar 2024 12:03:08 +0100 Subject: [PATCH 74/82] fix: keyerror on tests with change of return json in projects POST method --- backend/tests/endpoints/project_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index 877ee757..84439b7a 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -29,7 +29,7 @@ def test_post_project(db_session, client, course, course_teacher, project_json): assert response.status_code == 201 # check if the project with the id is present - project_id = response.json["project_id"] + project_id = response.json["project"]["project_id"] response = client.get(f"/projects/{project_id}") assert response.status_code == 200 @@ -49,7 +49,7 @@ def test_remove_project(db_session, client, course, course_teacher, project_json response = client.post("/projects", json=project_json) # check if the project with the id is present - project_id = response.json["project_id"] + project_id = response.json["project"]["project_id"] response = client.delete(f"/projects/{project_id}") assert response.status_code == 200 From c90dfacd9a3cfcd7f5ebcdda5de60ba37d0076b1 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 3 Mar 2024 13:50:17 +0100 Subject: [PATCH 75/82] fix: return good json representation --- .../endpoints/projects/project_detail.py | 48 +++++++++++-------- .../project/endpoints/projects/projects.py | 31 ++++++++---- backend/tests/endpoints/project_test.py | 7 +-- 3 files changed, 53 insertions(+), 33 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 65b58307..42547472 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -42,15 +42,19 @@ def get(self, project_id): try: # fetch the project with the id that is specified in the url project = Projects.query.filter_by(project_id=project_id).first() - self.abort_if_not_present(project) # return the fetched project and return 200 OK status - return jsonify(project), 200 + return { + "data": jsonify(project).json, + "url": f"{API_URL}/projects/{project_id}", + "message": "Got project successfully" + }, 200 except exc.SQLAlchemyError: - return ({"message": - "Internal server error"}, - 500) + return { + "message": "Internal server error", + "url": f"{API_URL}/projects/{project_id}" + }, 500 def patch(self, project_id): """ @@ -71,17 +75,17 @@ def patch(self, project_id): setattr(project, key, value) db.session.commit() # get the updated version - return {"message": f"Succesfully changed project with id: {id}", - "url": f"{API_URL}/projects/{id}", - "data": project - }, 200 + return { + "message": f"Succesfully changed project with id: {id}", + "url": f"{API_URL}/projects/{id}", + "data": project + }, 200 except exc.SQLAlchemyError: db.session.rollback() - return ({"message": - f"Something unexpected happenend when trying to edit project {id}", - "url": f"{API_URL}/projects/{id}", - "data": project}, - 500) + return { + "message": f"Something unexpected happenend when trying to edit project {id}", + "url": f"{API_URL}/projects/{id}" + }, 500 def delete(self, project_id): """ @@ -101,11 +105,13 @@ def delete(self, project_id): db.session.commit() # return 200 if content is deleted succesfully - return ({"message": f"Project with id: {id} deleted successfully", - "url": f"{API_URL}/projects/{id} deleted successfully!",}, - 200) + return { + "message": f"Project with id: {id} deleted successfully", + "url": f"{API_URL}/projects/{id} deleted successfully!", + "data": deleted_project + }, 200 except exc.SQLAlchemyError: - return ({"message": - f"Something unexpected happened when removing project {project_id}", - "url": f"{API_URL}/projects/{id}"}, - 500) + return { + "message": f"Something unexpected happened when removing project {project_id}", + "url": f"{API_URL}/projects/{id}" + }, 500 diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index edb11f4e..f7c3da4f 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -1,6 +1,8 @@ """ Module that implements the /projects endpoint of the API """ +from os import getenv +from dotenv import load_dotenv from flask import jsonify from flask_restful import Resource @@ -11,6 +13,9 @@ from project.models.projects import Projects from project.endpoints.projects.endpoint_parser import parse_project_params +load_dotenv() +API_URL = getenv('API_HOST') + class ProjectsEndpoint(Resource): """ Class for projects endpoints @@ -37,11 +42,16 @@ def get(self): } for row in projects] # return all valid entries for a project and return a 200 OK code - return jsonify(results), 200 + return { + "data": results, + "url": f"{API_URL}/projects", + "message": "Projects fetched successfully" + }, 200 except exc.SQLAlchemyError: - return ({"message": - "Something unexpected happenend when trying to get the projects"}, - 500) + return { + "message": "Something unexpected happenend when trying to get the projects", + "url": f"{API_URL}/projects" + }, 500 def post(self): """ @@ -72,10 +82,13 @@ def post(self): new_project_json = jsonify(new_project).json return { - "project": new_project_json, - "message": "Project posted successfully" + "url": f"{API_URL}/projects/{new_project_json['project_id']}", + "message": "Project posted successfully", + "data": new_project_json }, 201 except exc.SQLAlchemyError: - return ({"message": - "Something unexpected happenend when trying to add a new project"}, - 500) + return ({ + "url": f"{API_URL}/projects", + "message": "Something unexpected happenend when trying to add a new project", + "data": jsonify(new_project).json + }, 500) diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index 84439b7a..5a99e515 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -11,7 +11,7 @@ def test_getting_all_projects(client): """Test getting all projects""" response = client.get("/projects") assert response.status_code == 200 - assert isinstance(response.json, list) + assert isinstance(response.json['data'], list) def test_post_project(db_session, client, course, course_teacher, project_json): @@ -29,8 +29,9 @@ def test_post_project(db_session, client, course, course_teacher, project_json): assert response.status_code == 201 # check if the project with the id is present - project_id = response.json["project"]["project_id"] + project_id = response.json["data"]["project_id"] response = client.get(f"/projects/{project_id}") + assert response.status_code == 200 @@ -49,7 +50,7 @@ def test_remove_project(db_session, client, course, course_teacher, project_json response = client.post("/projects", json=project_json) # check if the project with the id is present - project_id = response.json["project"]["project_id"] + project_id = response.json["data"]["project_id"] response = client.delete(f"/projects/{project_id}") assert response.status_code == 200 From 604cd92ef085802e623e96f08d28416a5657016e Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 3 Mar 2024 19:53:17 +0100 Subject: [PATCH 76/82] fixed typos in OpenAPI_Object.json --- .../project/endpoints/index/OpenAPI_Object.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/project/endpoints/index/OpenAPI_Object.json b/backend/project/endpoints/index/OpenAPI_Object.json index ec64c9cf..54c5320d 100644 --- a/backend/project/endpoints/index/OpenAPI_Object.json +++ b/backend/project/endpoints/index/OpenAPI_Object.json @@ -44,7 +44,7 @@ "paths": { "/projects": { "get": { - "description": "Returns all projects from the database that the user has acces to", + "description": "Returns all projects from the database that the user has access to", "responses": { "200": { "description": "A list of projects", @@ -73,7 +73,7 @@ } }, "post": { - "description": "Upload a new proejct", + "description": "Upload a new project", "responses": { "201": { "description": "Uploaded a new project succesfully", @@ -96,7 +96,7 @@ "description": "Return a project with corresponding id", "responses": { "200": { - "description": "A user with corresponding id", + "description": "A project with corresponding id", "content": { "application/json": { "schema": { @@ -147,11 +147,11 @@ } }, "404": { - "description": "An id that doens't correspond to a existing user", + "description": "An id that doesn't correspond to an existing project", "content": { "application/json": { "schema": { - "type": "ojbect", + "type": "object", "properties": { "message": { "type": "string" @@ -164,7 +164,7 @@ } }, "patch": { - "description": "Patch certain fields op a project", + "description": "Patch certain fields of a project", "responses": { "200": { "description": "Patched a project succesfully", @@ -180,11 +180,11 @@ } }, "404": { - "description": "Tried to patch a user that is not present", + "description": "Tried to patch a project that is not present", "content": { "application/json": { "schema": { - "type": "ojbect", + "type": "object", "properties": { "message": { "type": "string" From 97f08660305c16dbe6ca7129ce9260af91c8db1c Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 3 Mar 2024 19:55:21 +0100 Subject: [PATCH 77/82] fixed typos in project_detail --- backend/project/endpoints/projects/project_detail.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 42547472..5d8164e6 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -65,8 +65,8 @@ def patch(self, project_id): # get the project that need to be edited project = Projects.query.filter_by(project_id=project_id).first() - # check which values are not None is the dict - # if it is not None is needs to be modified in the database + # check which values are not None in the dict + # if it is not None it needs to be modified in the database # commit the changes and return the 200 OK code if it succeeds, else 500 try: @@ -89,7 +89,7 @@ def patch(self, project_id): def delete(self, project_id): """ - Detele a project and all of its submissions in cascade + Delete a project and all of its submissions in cascade done by project id """ From b89f5b32c44f100379351090ac3d9d7a2b4ae74c Mon Sep 17 00:00:00 2001 From: gerwoud Date: Sun, 3 Mar 2024 19:57:38 +0100 Subject: [PATCH 78/82] fixed typos in model Projects --- backend/project/models/projects.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/project/models/projects.py b/backend/project/models/projects.py index 08cb8e05..4030d1a5 100644 --- a/backend/project/models/projects.py +++ b/backend/project/models/projects.py @@ -13,9 +13,9 @@ class Projects(db.Model): # pylint: disable=too-many-instance-attributes the course id of the course to which the project belongs, visible for students variable so a teacher can decide if the students can see it yet, archieved var so we can implement the archiving functionality, - a test path,script name and regex experssions for automated testing + a test path,script name and regex expressions for automated testing - Pylint disalbe too many intance attributes because we can't reduce the amount + Pylint disable too many instance attributes because we can't reduce the amount of fields of the model """ From 5c2b71880052726a4f993d9a572452cb5c93439e Mon Sep 17 00:00:00 2001 From: gerwoud Date: Mon, 4 Mar 2024 12:25:03 +0100 Subject: [PATCH 79/82] typo fixed --- backend/tests/endpoints/conftest.py | 2 +- backend/tests/endpoints/project_test.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/tests/endpoints/conftest.py b/backend/tests/endpoints/conftest.py index 7d8b8fc4..bc3eafb8 100644 --- a/backend/tests/endpoints/conftest.py +++ b/backend/tests/endpoints/conftest.py @@ -11,7 +11,7 @@ @pytest.fixture def course_teacher(): - """A user that's a teacher for for testing""" + """A user that's a teacher for testing""" ad_teacher = Users(uid="Gunnar", is_teacher=True, is_admin=True) return ad_teacher diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index 5a99e515..93724293 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -15,7 +15,7 @@ def test_getting_all_projects(client): def test_post_project(db_session, client, course, course_teacher, project_json): - """Test posting a project to the datab and testing if it's present""" + """Test posting a project to the database and testing if it's present""" db_session.add(course_teacher) db_session.commit() @@ -36,7 +36,7 @@ def test_post_project(db_session, client, course, course_teacher, project_json): def test_remove_project(db_session, client, course, course_teacher, project_json): - """Test removing a project to the datab and fetching it, testing if its not present anymore""" + """Test removing a project to the datab and fetching it, testing if it's not present anymore""" db_session.add(course_teacher) db_session.commit() @@ -60,7 +60,7 @@ def test_remove_project(db_session, client, course, course_teacher, project_json assert response.status_code == 404 -def test_update_project(db_session, client, course, course_teacher, project): +def test_patch_project(db_session, client, course, course_teacher, project): """ Test functionality of the PUT method for projects """ From 12b1a29081b5540ef19c3832429bb16ab916a16a Mon Sep 17 00:00:00 2001 From: gerwoud Date: Mon, 4 Mar 2024 12:34:34 +0100 Subject: [PATCH 80/82] route -> routes --- backend/project/endpoints/projects/project_endpoint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/project/endpoints/projects/project_endpoint.py b/backend/project/endpoints/projects/project_endpoint.py index e915ad14..c996a514 100644 --- a/backend/project/endpoints/projects/project_endpoint.py +++ b/backend/project/endpoints/projects/project_endpoint.py @@ -1,6 +1,6 @@ """ Module for providing the blueprint to the api -of both route +of both routes """ from flask import Blueprint From cdd94d2aaf6dddfe89a7611566a0db240dde1388 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Tue, 5 Mar 2024 10:49:58 +0100 Subject: [PATCH 81/82] fix: naming of new database schemes --- .../project/endpoints/projects/project_detail.py | 8 ++++---- backend/project/endpoints/projects/projects.py | 12 ++++++------ backend/tests/endpoints/conftest.py | 16 ++++++---------- backend/tests/endpoints/project_test.py | 4 ++-- backend/tests/models/conftest.py | 4 ---- 5 files changed, 18 insertions(+), 26 deletions(-) diff --git a/backend/project/endpoints/projects/project_detail.py b/backend/project/endpoints/projects/project_detail.py index 5d8164e6..88989247 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -12,7 +12,7 @@ from project.endpoints.projects.endpoint_parser import parse_project_params from project import db -from project.models.projects import Projects +from project.models.projects import Project load_dotenv() API_URL = getenv('API_HOST') @@ -41,7 +41,7 @@ def get(self, project_id): try: # fetch the project with the id that is specified in the url - project = Projects.query.filter_by(project_id=project_id).first() + project = Project.query.filter_by(project_id=project_id).first() self.abort_if_not_present(project) # return the fetched project and return 200 OK status @@ -63,7 +63,7 @@ def patch(self, project_id): """ # get the project that need to be edited - project = Projects.query.filter_by(project_id=project_id).first() + project = Project.query.filter_by(project_id=project_id).first() # check which values are not None in the dict # if it is not None it needs to be modified in the database @@ -94,7 +94,7 @@ def delete(self, project_id): """ # fetch the project that needs to be removed - deleted_project = Projects.query.filter_by(project_id=project_id).first() + deleted_project = Project.query.filter_by(project_id=project_id).first() # check if its an existing one self.abort_if_not_present(deleted_project) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index f7c3da4f..f444e283 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -10,7 +10,7 @@ from project import db -from project.models.projects import Projects +from project.models.projects import Project from project.endpoints.projects.endpoint_parser import parse_project_params load_dotenv() @@ -29,10 +29,10 @@ def get(self): that are currently in the API """ try: - projects = Projects.query.with_entities( - Projects.project_id, - Projects.title, - Projects.descriptions + projects = Project.query.with_entities( + Project.project_id, + Project.title, + Project.descriptions ).all() results = [{ @@ -61,7 +61,7 @@ def post(self): args = parse_project_params() # create a new project object to add in the API later - new_project = Projects( + new_project = Project( title=args['title'], descriptions=args['descriptions'], assignment_file=args['assignment_file'], diff --git a/backend/tests/endpoints/conftest.py b/backend/tests/endpoints/conftest.py index 175ac6fb..7322d6b2 100644 --- a/backend/tests/endpoints/conftest.py +++ b/backend/tests/endpoints/conftest.py @@ -3,13 +3,9 @@ import os import pytest -from project import create_app_with_db, db -from project.models.users import Users -from project.models.courses import Courses -from project.models.projects import Projects -from project.sessionmaker import engine, url from project.models.courses import Course from project.models.users import User +from project.models.projects import Project from project.models.course_relations import CourseStudent,CourseAdmin from project import create_app_with_db, db from project.db_in import url @@ -18,14 +14,14 @@ @pytest.fixture def course_teacher(): """A user that's a teacher for testing""" - ad_teacher = Users(uid="Gunnar", is_teacher=True, is_admin=True) + ad_teacher = User(uid="Gunnar", is_teacher=True, is_admin=True) return ad_teacher @pytest.fixture -def course(course_teacher: Users): +def course(course_teacher: User): """A course for testing, with the course teacher as the teacher.""" - ad2 = Courses(name="Ad2", teacher=course_teacher.uid) + ad2 = Course(name="Ad2", teacher=course_teacher.uid) return ad2 @@ -33,7 +29,7 @@ def course(course_teacher: Users): def project(course): """A project for testing, with the course as the course it belongs to""" date = datetime(2024, 2, 25, 12, 0, 0) - project = Projects( + project = Project( title="Project", descriptions="Test project", course_id=course.course_id, @@ -49,7 +45,7 @@ def project(course): @pytest.fixture -def project_json(project: Projects): +def project_json(project: Project): """A function that return the json data of a project including the PK neede for testing""" data = { "title": project.title, diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index 93724293..8b61a228 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -1,5 +1,5 @@ """Tests for project endpoints.""" -from project.models.projects import Projects +from project.models.projects import Project def test_projects_home(client): """Test home project endpoint.""" @@ -85,7 +85,7 @@ def test_patch_project(db_session, client, course, course_teacher, project): "title": new_title, "archieved": new_archieved }) db_session.commit() - updated_project = db_session.get(Projects, {"project_id": project.project_id}) + updated_project = db_session.get(Project, {"project_id": project.project_id}) assert response.status_code == 200 assert updated_project.title == new_title diff --git a/backend/tests/models/conftest.py b/backend/tests/models/conftest.py index c138af18..f362f6dd 100644 --- a/backend/tests/models/conftest.py +++ b/backend/tests/models/conftest.py @@ -32,10 +32,6 @@ def db_session(): for table in reversed(db.metadata.sorted_tables): session.execute(table.delete()) session.commit() -from project.models.courses import Courses -from project.models.course_relations import CourseAdmins, CourseStudents -from project.models.projects import Projects -from project.models.users import Users @pytest.fixture From e0553442da4322a045ede6fa5c5b548a00822e80 Mon Sep 17 00:00:00 2001 From: gerwoud Date: Tue, 5 Mar 2024 11:28:35 +0100 Subject: [PATCH 82/82] i <3 linters --- backend/tests/endpoints/conftest.py | 6 +++--- backend/tests/endpoints/project_test.py | 24 ++++++++++++------------ backend/tests/models/conftest.py | 18 ------------------ 3 files changed, 15 insertions(+), 33 deletions(-) diff --git a/backend/tests/endpoints/conftest.py b/backend/tests/endpoints/conftest.py index 7322d6b2..0e964c22 100644 --- a/backend/tests/endpoints/conftest.py +++ b/backend/tests/endpoints/conftest.py @@ -12,16 +12,16 @@ @pytest.fixture -def course_teacher(): +def course_teacher_ad(): """A user that's a teacher for testing""" ad_teacher = User(uid="Gunnar", is_teacher=True, is_admin=True) return ad_teacher @pytest.fixture -def course(course_teacher: User): +def course_ad(course_teacher_ad: User): """A course for testing, with the course teacher as the teacher.""" - ad2 = Course(name="Ad2", teacher=course_teacher.uid) + ad2 = Course(name="Ad2", teacher=course_teacher_ad.uid) return ad2 diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index 8b61a228..1ebecce4 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -14,15 +14,15 @@ def test_getting_all_projects(client): assert isinstance(response.json['data'], list) -def test_post_project(db_session, client, course, course_teacher, project_json): +def test_post_project(db_session, client, course_ad, course_teacher_ad, project_json): """Test posting a project to the database and testing if it's present""" - db_session.add(course_teacher) + db_session.add(course_teacher_ad) db_session.commit() - db_session.add(course) + db_session.add(course_ad) db_session.commit() - project_json["course_id"] = course.course_id + project_json["course_id"] = course_ad.course_id # post the project response = client.post("/projects", json=project_json) @@ -35,16 +35,16 @@ def test_post_project(db_session, client, course, course_teacher, project_json): assert response.status_code == 200 -def test_remove_project(db_session, client, course, course_teacher, project_json): +def test_remove_project(db_session, client, course_ad, course_teacher_ad, project_json): """Test removing a project to the datab and fetching it, testing if it's not present anymore""" - db_session.add(course_teacher) + db_session.add(course_teacher_ad) db_session.commit() - db_session.add(course) + db_session.add(course_ad) db_session.commit() - project_json["course_id"] = course.course_id + project_json["course_id"] = course_ad.course_id # post the project response = client.post("/projects", json=project_json) @@ -60,18 +60,18 @@ def test_remove_project(db_session, client, course, course_teacher, project_json assert response.status_code == 404 -def test_patch_project(db_session, client, course, course_teacher, project): +def test_patch_project(db_session, client, course_ad, course_teacher_ad, project): """ Test functionality of the PUT method for projects """ - db_session.add(course_teacher) + db_session.add(course_teacher_ad) db_session.commit() - db_session.add(course) + db_session.add(course_ad) db_session.commit() - project.course_id = course.course_id + project.course_id = course_ad.course_id # post the project to edit db_session.add(project) diff --git a/backend/tests/models/conftest.py b/backend/tests/models/conftest.py index f362f6dd..4ed9bdcf 100644 --- a/backend/tests/models/conftest.py +++ b/backend/tests/models/conftest.py @@ -6,7 +6,6 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker import pytest -from project import db from project.models.courses import Course from project.models.course_relations import CourseAdmin, CourseStudent from project.models.projects import Project @@ -16,23 +15,6 @@ engine = create_engine(url) Session = sessionmaker(bind=engine) -@pytest.fixture -def db_session(): - """Create a new database session for a test. - After the test, all changes are rolled back and the session is closed.""" - db.metadata.create_all(engine) - session = Session() - for table in reversed(db.metadata.sorted_tables): - session.execute(table.delete()) - session.commit() - yield session - session.rollback() - session.close() - # Truncate all tables - for table in reversed(db.metadata.sorted_tables): - session.execute(table.delete()) - session.commit() - @pytest.fixture def valid_user():