diff --git a/tests/database/test_check.py b/tests/database/test_check.py index bf9c148..176e679 100644 --- a/tests/database/test_check.py +++ b/tests/database/test_check.py @@ -1,9 +1,34 @@ +from secrets import token_hex + from tests.utils import arrange_task, unique_str -from webapp.models import Student, TaskStatus from webapp.repositories import AppDatabase +def test_get_by_student(db: AppDatabase): + group_id, variant_id, task_id = arrange_task(db) + + student_email = f"{unique_str()}@test.ru" + + student = db.students.create(student_email, unique_str()) + db.students.confirm(student_email) + db.students.update_group(db.students.find_by_email(student_email), group_id) + + code = "main = lambda: 42" + ip = "0.0.0.0" + + message = db.messages.submit_task(task_id, variant_id, group_id, code, ip, student.id) + stat = db.statuses.submit_task(task_id, variant_id, group_id, code, ip) + db.checks.record_check(message.id, stat.status, "test") + + result = db.checks.get_by_student(student, 0, 10) + + assert len(result) == 1 + assert result[0][1].code == code + assert result[0][1].ip == ip + assert result[0][0].output == "test" + + def test_count_student_submissions(db: AppDatabase): group_id, variant_id, task_id = arrange_task(db) @@ -44,3 +69,38 @@ def test_count_submissions_by_info(db: AppDatabase): result = db.checks.count_submissions_by_info(group_id, variant_id, task_id, False) assert result == 1 + + +def test_count_session_id_submissions(db: AppDatabase): + group_id, variant_id, task_id = arrange_task(db) + + session_id = token_hex(16) + + code = "main = lambda: 42" + ip = "0.0.0.0" + + message = db.messages.submit_task(task_id, variant_id, group_id, code, ip, None, session_id) + stat = db.statuses.submit_task(task_id, variant_id, group_id, code, ip) + db.checks.record_check(message.id, stat.status, "test") + + result = db.checks.count_session_id_submissions(session_id=session_id) + + assert result == 1 + + +def test_get_by_session_id(db: AppDatabase): + group_id, variant_id, task_id = arrange_task(db) + + session_id = token_hex(16) + + code = "main = lambda: 42" + ip = "0.0.0.0" + + message = db.messages.submit_task(task_id, variant_id, group_id, code, ip, None, session_id) + stat = db.statuses.submit_task(task_id, variant_id, group_id, code, ip) + db.checks.record_check(message.id, stat.status, "test") + + result = db.checks.get_by_session_id(session_id=session_id, skip=0, take=10) + + assert len(result) == 1 + assert result[0][1].session_id == session_id diff --git a/tests/ui/test_submissions.py b/tests/ui/test_submissions.py new file mode 100644 index 0000000..f09d17f --- /dev/null +++ b/tests/ui/test_submissions.py @@ -0,0 +1,68 @@ +from secrets import token_hex + +from tests.database.test_check import arrange_task +from tests.utils import mode, unique_str + +from flask.testing import FlaskClient + +from webapp.repositories import AppDatabase + + +@mode("registration") +def test_unauthorized_submissions(db: AppDatabase, client: FlaskClient): + response = client.get('/submissions') + html = response.get_data(as_text=True) + + assert "401 Unauthorized" in html + + +@mode("exam") +def test_empty_submissions(db: AppDatabase, client: FlaskClient): + session_id = token_hex(16) + client.set_cookie("localhost", "anonymous_identifier", session_id) + response = client.get('/submissions') + html = response.get_data(as_text=True) + + assert response.status_code == 200 + assert "Список отправленных решений пуст" in html + + +@mode("exam") +def test_exam_anonymous_submissions(db: AppDatabase, client: FlaskClient): + group_id, variant_id, task_id = arrange_task(db) + + session_id = token_hex(16) + + code = "main = lambda: 42" + unique_str() + ip = "0.0.0.0" + + message = db.messages.submit_task(task_id, variant_id, group_id, code, ip, None, session_id) + stat = db.statuses.submit_task(task_id, variant_id, group_id, code, ip) + db.checks.record_check(message.id, stat.status, "test_exam_anonymous_submissions") + + client.set_cookie("localhost", "anonymous_identifier", session_id) + response = client.get('/submissions') + html = response.get_data(as_text=True) + + assert response.status_code == 200 + assert code in html + + +def test_no_reg_anonymous_submissions(db: AppDatabase, client: FlaskClient): + group_id, variant_id, task_id = arrange_task(db) + + session_id = token_hex(16) + + code = "main = lambda: 42" + unique_str() + ip = "0.0.0.0" + + message = db.messages.submit_task(task_id, variant_id, group_id, code, ip, None, session_id) + stat = db.statuses.submit_task(task_id, variant_id, group_id, code, ip) + db.checks.record_check(message.id, stat.status, "test_no_reg_anonymous_submissions") + + client.set_cookie("localhost", "anonymous_identifier", session_id) + response = client.get('/submissions') + html = response.get_data(as_text=True) + + assert response.status_code == 200 + assert code in html diff --git a/webapp/alembic/versions/20240229.01-04.add_session_id.py b/webapp/alembic/versions/20240229.01-04.add_session_id.py new file mode 100644 index 0000000..8715969 --- /dev/null +++ b/webapp/alembic/versions/20240229.01-04.add_session_id.py @@ -0,0 +1,24 @@ +""" + +Revision ID: 30b579748cec +Revises: eeff291eb0fb +Create Date: 2024-02-29 01:04:59.268423 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '30b579748cec' +down_revision = 'eeff291eb0fb' +branch_labels = None +depends_on = None + + +def upgrade(): + op.add_column("messages", sa.Column("session_id", sa.String, nullable=True)) + + +def downgrade(): + op.drop_column("messages", "session_id") diff --git a/webapp/managers.py b/webapp/managers.py index 0848cac..eb6cf5d 100644 --- a/webapp/managers.py +++ b/webapp/managers.py @@ -256,9 +256,19 @@ def get_submissions_statuses(self, student: Student, skip: int, take: int) -> li submissions.append(self.__get_submissions(check, message, None)) return submissions + def get_anonymous_submissions_statuses(self, session_id: str, skip: int, take: int) -> list[SubmissionDto]: + checks_and_messages: list[tuple[MessageCheck, Message]] = self.checks.get_by_session_id(session_id, skip, take) + submissions = [] + for check, message in checks_and_messages: + submissions.append(self.__get_submissions(check, message, None)) + return submissions + def count_student_submissions(self, student: Student) -> int: return self.checks.count_student_submissions(student) + def count_session_id_submissions(self, session_id: str) -> int: + return self.checks.count_session_id_submissions(session_id) + def __get_submissions( self, check: MessageCheck, diff --git a/webapp/models.py b/webapp/models.py index 86241a3..01dbe58 100644 --- a/webapp/models.py +++ b/webapp/models.py @@ -100,6 +100,7 @@ class Message(Base): time = sa.Column("time", sa.DateTime, nullable=False) code = sa.Column("code", sa.String, nullable=False) ip = sa.Column("ip", sa.String, nullable=False) + session_id = sa.Column("session_id", sa.String, nullable=True) processed = sa.Column("processed", sa.Boolean, nullable=False) student = sa.Column("student", sa.Integer, sa.ForeignKey("students.id"), nullable=True) diff --git a/webapp/repositories.py b/webapp/repositories.py index f2817f3..d6c94b6 100644 --- a/webapp/repositories.py +++ b/webapp/repositories.py @@ -255,6 +255,7 @@ def submit_task( code: str, ip: str, student: int | None, + session_id: str | None = None ) -> Message: with self.db.create_session() as session: message = Message( @@ -266,6 +267,7 @@ def submit_task( code=code, ip=ip, student=student, + session_id=session_id ) session.add(message) return message @@ -348,6 +350,16 @@ def get_by_student(self, student: Student, skip: int, take: int) -> list[tuple[M .limit(take) \ .all() + def get_by_session_id(self, session_id: str, skip: int, take: int) -> list[tuple[MessageCheck, Message]]: + with self.db.create_session() as session: + return session.query(MessageCheck, Message) \ + .join(Message, Message.id == MessageCheck.message) \ + .filter(Message.session_id == session_id) \ + .order_by(desc(Message.time)) \ + .offset(skip) \ + .limit(take) \ + .all() + def count_student_submissions(self, student: Student) -> int: with self.db.create_session() as session: return session.query(MessageCheck, Message) \ @@ -355,6 +367,13 @@ def count_student_submissions(self, student: Student) -> int: .filter(Message.student == student.id) \ .count() + def count_session_id_submissions(self, session_id: str) -> int: + with self.db.create_session() as session: + return session.query(MessageCheck, Message) \ + .join(Message, Message.id == MessageCheck.message) \ + .filter(Message.session_id == session_id) \ + .count() + def get_by_task(self, group: int, variant: int, task: int, skip: int, take: int, registration: bool): with self.db.create_session() as session: query = session \ diff --git a/webapp/templates/student/student_layout.jinja b/webapp/templates/student/student_layout.jinja index 96ca7f9..14b7def 100644 --- a/webapp/templates/student/student_layout.jinja +++ b/webapp/templates/student/student_layout.jinja @@ -48,6 +48,9 @@