From aac6f40cd66c24f9b76741b4a38d7a66edc2f28d Mon Sep 17 00:00:00 2001 From: Aron Buzogany Date: Thu, 4 Apr 2024 21:51:41 +0200 Subject: [PATCH 1/6] Fix #181 --- backend/db_construct.sql | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/backend/db_construct.sql b/backend/db_construct.sql index e3f6af41..0c6e6ee6 100644 --- a/backend/db_construct.sql +++ b/backend/db_construct.sql @@ -38,17 +38,19 @@ CREATE TABLE course_students ( PRIMARY KEY(course_id, uid) ); +CREATE TYPE deadline ( + description TEXT NOT NULL, + deadline TIMESTAMP WITH TIME ZONE NOT NULL +); + CREATE TABLE projects ( project_id INT GENERATED ALWAYS AS IDENTITY, title VARCHAR(50) NOT NULL, description TEXT NOT NULL, - assignment_file VARCHAR(50), - deadline TIMESTAMP WITH TIME ZONE, + deadline deadline[], course_id INT NOT NULL, visible_for_students BOOLEAN NOT NULL, archived BOOLEAN NOT NULL, - test_path VARCHAR(50), - script_name VARCHAR(50), regex_expressions VARCHAR(50)[], PRIMARY KEY(project_id), CONSTRAINT fk_course FOREIGN KEY(course_id) REFERENCES courses(course_id) ON DELETE CASCADE From 194b0f43cd33bdaf2f84bec22385469fa3851f97 Mon Sep 17 00:00:00 2001 From: abuzogan Date: Fri, 5 Apr 2024 09:19:22 +0200 Subject: [PATCH 2/6] resolved syntax errors --- backend/db_construct.sql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/db_construct.sql b/backend/db_construct.sql index 0c6e6ee6..e2283f59 100644 --- a/backend/db_construct.sql +++ b/backend/db_construct.sql @@ -38,9 +38,9 @@ CREATE TABLE course_students ( PRIMARY KEY(course_id, uid) ); -CREATE TYPE deadline ( - description TEXT NOT NULL, - deadline TIMESTAMP WITH TIME ZONE NOT NULL +CREATE TYPE deadline AS( + description TEXT, + deadline TIMESTAMP WITH TIME ZONE ); CREATE TABLE projects ( From cb45fdd5c0590d69ece7b83345d31d2f9922837c Mon Sep 17 00:00:00 2001 From: abuzogan Date: Fri, 5 Apr 2024 13:27:17 +0200 Subject: [PATCH 3/6] Fix #181 --- backend/db_construct.sql | 2 +- .../endpoints/projects/endpoint_parser.py | 20 ++++++++++++++----- .../endpoints/projects/project_detail.py | 2 +- .../project/endpoints/projects/projects.py | 7 ++++--- backend/project/models/project.py | 16 +++++++++++---- backend/requirements.txt | 1 + backend/tests/conftest.py | 10 ++-------- backend/tests/endpoints/conftest.py | 6 ++---- backend/tests/endpoints/project_test.py | 4 ++++ backend/tests/models/project_test.py | 5 +---- 10 files changed, 43 insertions(+), 30 deletions(-) diff --git a/backend/db_construct.sql b/backend/db_construct.sql index e2283f59..347354fd 100644 --- a/backend/db_construct.sql +++ b/backend/db_construct.sql @@ -47,7 +47,7 @@ CREATE TABLE projects ( project_id INT GENERATED ALWAYS AS IDENTITY, title VARCHAR(50) NOT NULL, description TEXT NOT NULL, - deadline deadline[], + deadlines deadline[], course_id INT NOT NULL, visible_for_students BOOLEAN NOT NULL, archived BOOLEAN NOT NULL, diff --git a/backend/project/endpoints/projects/endpoint_parser.py b/backend/project/endpoints/projects/endpoint_parser.py index d9737826..fafafecb 100644 --- a/backend/project/endpoints/projects/endpoint_parser.py +++ b/backend/project/endpoints/projects/endpoint_parser.py @@ -4,6 +4,7 @@ from flask_restful import reqparse from werkzeug.datastructures import FileStorage +import json parser = reqparse.RequestParser() parser.add_argument('title', type=str, help='Projects title', location="form") @@ -14,7 +15,7 @@ help='Projects assignment file', location="form" ) -parser.add_argument("deadline", type=str, help='Projects deadline', location="form") +parser.add_argument('deadlines', type=str, help='Projects deadlines', location="form") parser.add_argument("course_id", type=str, help='Projects course_id', location="form") parser.add_argument( "visible_for_students", @@ -23,8 +24,6 @@ location="form" ) parser.add_argument("archived", type=bool, help='Projects', location="form") -parser.add_argument("test_path", type=str, help='Projects test path', location="form") -parser.add_argument("script_name", type=str, help='Projects test script path', location="form") parser.add_argument( "regex_expressions", type=str, @@ -39,9 +38,20 @@ 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 + if "deadlines" == key: + deadlines_parsed = json.loads(value) + new_deadlines = [] + for deadline in deadlines_parsed: + new_deadlines.append( + ( + deadline["description"], + deadline["deadline"] + ) + ) + result_dict[key] = new_deadlines + else: + 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 060587c7..d2affa57 100644 --- a/backend/project/endpoints/projects/project_detail.py +++ b/backend/project/endpoints/projects/project_detail.py @@ -89,7 +89,7 @@ def patch(self, project_id): zip_location = os.path.join(project_upload_directory, filename) with zipfile.ZipFile(zip_location) as upload_zip: upload_zip.extractall(project_upload_directory) - project_json["assignment_file"] = filename + except zipfile.BadZipfile: db.session.rollback() return ({ diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index 835e692d..f84547c1 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -3,6 +3,7 @@ """ import os from urllib.parse import urljoin +import json import zipfile from sqlalchemy.exc import SQLAlchemyError @@ -38,7 +39,7 @@ def get(self, teacher_id=None): return query_selected_from_model( Project, response_url, - select_values=["project_id", "title", "description", "deadline"], + select_values=["project_id", "title", "description", "deadlines"], url_mapper={"project_id": response_url}, filters=request.args ) @@ -55,7 +56,6 @@ def post(self, teacher_id=None): if "assignment_file" in request.files: file = request.files["assignment_file"] filename = os.path.basename(file.filename) - project_json["assignment_file"] = filename # save the file that is given with the request try: @@ -70,7 +70,8 @@ def post(self, teacher_id=None): "visible_for_students", "archived"] ) - except SQLAlchemyError: + except SQLAlchemyError as e: + print(e) return jsonify({"error": "Something went wrong while inserting into the database.", "url": f"{API_URL}/projects"}), 500 diff --git a/backend/project/models/project.py b/backend/project/models/project.py index 8ba901ff..624f9ed0 100644 --- a/backend/project/models/project.py +++ b/backend/project/models/project.py @@ -2,6 +2,7 @@ from dataclasses import dataclass from sqlalchemy import ARRAY, Boolean, Column, DateTime, ForeignKey, Integer, String, Text +from sqlalchemy_utils import CompositeType from project.db_in import db @dataclass @@ -23,11 +24,18 @@ class Project(db.Model): # pylint: disable=too-many-instance-attributes project_id: int = Column(Integer, primary_key=True) title: str = Column(String(50), nullable=False, unique=False) description: str = Column(Text, nullable=False) - assignment_file: str = Column(String(50)) - deadline: str = Column(DateTime(timezone=True)) + deadlines: list = Column(ARRAY( + CompositeType( + "deadline", + [ + Column("description", Text), + Column("deadline", DateTime(timezone=True)) + ] + ), + dimensions=1 + ) + ) course_id: int = Column(Integer, ForeignKey("courses.course_id"), nullable=False) visible_for_students: bool = Column(Boolean, nullable=False) archived: 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))) diff --git a/backend/requirements.txt b/backend/requirements.txt index f47e98e6..0c8d687f 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -2,6 +2,7 @@ flask~=3.0.2 flask-cors flask-restful flask-sqlalchemy +sqlalchemy_utils python-dotenv~=1.0.1 psycopg2-binary pytest~=8.0.1 diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index a7cc092b..f7bacfa3 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -69,25 +69,19 @@ def projects(session): Project( title="B+ Trees", description="Implement B+ trees", - assignment_file="assignement.pdf", - deadline=datetime(2024,3,15,13,0,0), + deadlines=[("Deadline 1",datetime(2024,3,15,13,0,0))], course_id=course_id_ad3, visible_for_students=True, archived=False, - test_path="/tests", - script_name="script.sh", regex_expressions=["solution"] ), Project( title="Predicaten", description="Predicaten project", - assignment_file="assignment.pdf", - deadline=datetime(2023,3,15,13,0,0), + deadlines=[("Deadline 1", datetime(2023,3,15,13,0,0))], course_id=course_id_raf, visible_for_students=False, archived=True, - test_path="/tests", - script_name="script.sh", regex_expressions=[".*"] ) ] diff --git a/backend/tests/endpoints/conftest.py b/backend/tests/endpoints/conftest.py index fd46dd8a..7748fef8 100644 --- a/backend/tests/endpoints/conftest.py +++ b/backend/tests/endpoints/conftest.py @@ -4,6 +4,7 @@ import os from datetime import datetime from zoneinfo import ZoneInfo +import json import pytest from pytest import fixture, FixtureRequest from flask.testing import FlaskClient @@ -190,13 +191,10 @@ def valid_project(valid_course_entry): data = { "title": "Project", "description": "Test project", - "assignment_file": "testfile", - "deadline": "2024-02-25T12:00:00", + "deadlines": [{"deadline": "2024-02-25T12:00:00", "description": "Deadline 1"}], "course_id": valid_course_entry.course_id, "visible_for_students": True, "archived": False, - "test_path": "tests", - "script_name": "script.sh", "regex_expressions": ["*.pdf", "*.txt"] } return data diff --git a/backend/tests/endpoints/project_test.py b/backend/tests/endpoints/project_test.py index 46f7bcbc..510e24ce 100644 --- a/backend/tests/endpoints/project_test.py +++ b/backend/tests/endpoints/project_test.py @@ -1,10 +1,13 @@ """Tests for project endpoints.""" +import json + def test_assignment_download(client, valid_project): """ Method for assignment download """ + valid_project["deadlines"] = json.dumps(valid_project["deadlines"]) with open("tests/resources/testzip.zip", "rb") as zip_file: valid_project["assignment_file"] = zip_file # post the project @@ -48,6 +51,7 @@ def test_getting_all_projects(client): def test_post_project(client, valid_project): """Test posting a project to the database and testing if it's present""" + valid_project["deadlines"] = json.dumps(valid_project["deadlines"]) with open("tests/resources/testzip.zip", "rb") as zip_file: valid_project["assignment_file"] = zip_file # post the project diff --git a/backend/tests/models/project_test.py b/backend/tests/models/project_test.py index b99a6134..cda7c562 100644 --- a/backend/tests/models/project_test.py +++ b/backend/tests/models/project_test.py @@ -16,13 +16,10 @@ def test_create_project(self, session: Session): project = Project( title="Pigeonhole", description="A new project", - assignment_file="assignment.pdf", - deadline=datetime(2024,12,31,23,59,59), + deadlines=[("Deadline 1", datetime(2024,12,31,23,59,59))], course_id=course.course_id, visible_for_students=True, archived=False, - test_path="/test", - script_name="script", regex_expressions=[r".*"] ) session.add(project) From d1b395c0397dde9cf6a96116d4cf7c16e6302000 Mon Sep 17 00:00:00 2001 From: abuzogan Date: Fri, 5 Apr 2024 13:27:57 +0200 Subject: [PATCH 4/6] removed unused statement --- 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 7748fef8..401de3d0 100644 --- a/backend/tests/endpoints/conftest.py +++ b/backend/tests/endpoints/conftest.py @@ -4,7 +4,6 @@ import os from datetime import datetime from zoneinfo import ZoneInfo -import json import pytest from pytest import fixture, FixtureRequest from flask.testing import FlaskClient From caf8b419a39e6231056722bebf54f0a609d760c1 Mon Sep 17 00:00:00 2001 From: abuzogan Date: Fri, 5 Apr 2024 13:36:20 +0200 Subject: [PATCH 5/6] resolved linting issues --- backend/project/endpoints/projects/endpoint_parser.py | 2 +- backend/project/endpoints/projects/projects.py | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/backend/project/endpoints/projects/endpoint_parser.py b/backend/project/endpoints/projects/endpoint_parser.py index fafafecb..f4ab93ea 100644 --- a/backend/project/endpoints/projects/endpoint_parser.py +++ b/backend/project/endpoints/projects/endpoint_parser.py @@ -2,9 +2,9 @@ Parser for the argument when posting or patching a project """ +import json from flask_restful import reqparse from werkzeug.datastructures import FileStorage -import json parser = reqparse.RequestParser() parser.add_argument('title', type=str, help='Projects title', location="form") diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index f84547c1..d72fd539 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -3,7 +3,6 @@ """ import os from urllib.parse import urljoin -import json import zipfile from sqlalchemy.exc import SQLAlchemyError @@ -82,9 +81,9 @@ def post(self, teacher_id=None): os.makedirs(project_upload_directory, exist_ok=True) if filename is not None: try: - file.save(os.path.join(project_upload_directory, filename)) - zip_location = os.path.join(project_upload_directory, filename) - with zipfile.ZipFile(zip_location) as upload_zip: + file_path = os.path.join(project_upload_directory, filename) + file.save(file_path) + with zipfile.ZipFile(file_path) as upload_zip: upload_zip.extractall(project_upload_directory) except zipfile.BadZipfile: os.remove(os.path.join(project_upload_directory, filename)) From 8368e7df5b85a2fa13501b0084311b6ae5fca90f Mon Sep 17 00:00:00 2001 From: Aron Buzogany Date: Sat, 6 Apr 2024 15:19:25 +0200 Subject: [PATCH 6/6] removed print statement --- backend/project/endpoints/projects/projects.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/project/endpoints/projects/projects.py b/backend/project/endpoints/projects/projects.py index d72fd539..01873379 100644 --- a/backend/project/endpoints/projects/projects.py +++ b/backend/project/endpoints/projects/projects.py @@ -69,8 +69,7 @@ def post(self, teacher_id=None): "visible_for_students", "archived"] ) - except SQLAlchemyError as e: - print(e) + except SQLAlchemyError: return jsonify({"error": "Something went wrong while inserting into the database.", "url": f"{API_URL}/projects"}), 500