diff --git a/web-app/adapters/__init__.py b/web-app/adapters/__init__.py index eec1779..e5034e2 100644 --- a/web-app/adapters/__init__.py +++ b/web-app/adapters/__init__.py @@ -1 +1,2 @@ +# static analysis: ignore[import_failed] from . import orm, repository \ No newline at end of file diff --git a/web-app/adapters/orm.py b/web-app/adapters/orm.py index e7c1826..a340c2a 100644 --- a/web-app/adapters/orm.py +++ b/web-app/adapters/orm.py @@ -1,14 +1,10 @@ +# static analysis: ignore[import_failed] from sqlalchemy import Column, MetaData, Integer, String, ForeignKey, Text, Enum, Table, Boolean from sqlalchemy.orm import registry -from sqlalchemy.orm import mapper +from sqlalchemy.orm import mapper, relationship from domain import model -# Заглушка -# To distinguish between question types -# class QuestionTypes: -# pass - # Base = declarative_base() mapper_registry = registry() @@ -24,65 +20,48 @@ # Table for users user_table = Table("users", metadata, Column('id', Integer, primary_key=True, autoincrement=True), - Column('nickname', Text, unique=True), + Column('name', Text), + Column('surname', Text), Column('email', Text, unique=True), Column('password', Text)) # Table for quizzes quiz_table = Table("quizzes", metadata, - Column('user_id', Integer, ForeignKey('user.id')), + Column('id', Integer, primary_key=True, autoincrement=True), + Column('user_id', Integer, ForeignKey('users.id', ondelete="CASCADE")), Column('name', Text)) # Table for questions question_table = Table("questions", metadata, Column('id', Integer, primary_key=True, autoincrement=True), - Column('quiz_id', Integer, ForeignKey('quiz.id')), - Column('type', Enum(model.QuestionTypes)), - Column('description', Text)) + Column('quiz_id', Integer, ForeignKey('quizzes.id', ondelete="CASCADE")), + Column('question_type', Text), + Column('text', Text), + Column('time', Integer)) # Table for answers answer_table = Table("answers", metadata, Column('id', Integer, primary_key=True, autoincrement=True), - Column('question_id', ForeignKey('question.id')), + Column('question_id', Integer, ForeignKey('questions.id', ondelete="CASCADE")), Column('text', Text), Column('correct_answer', Boolean)) def start_mappers(): - user_mapper = mapper_registry.map_imperatively(model.User, user_table) question_mapper = mapper_registry.map_imperatively(model.Question, question_table) + user_mapper = mapper_registry.map_imperatively(model.User, user_table) answer_mapper = mapper_registry.map_imperatively(model.Answer, answer_table) + quiz_mapper = mapper_registry.map_imperatively(model.Quiz, quiz_table) + + +def create_all(engine): + metadata.bind = engine + metadata.create_all() + + +def delete_all(engine): + metadata.drop_all(engine) if __name__ == '__main__': start_mappers() -# Draft -# class User(Base): -# __tablename__ = 'user' -# -# id = Column(Integer, primary_key=True, autoincrement=True) -# nickname = Column(Text, unique=True) -# email = Column(Text, unique=True) -# password = Column(Text) -# -# -# class Quiz(Base): -# __tablename__ = 'quiz' -# -# id = Column(Integer, primary_key=True, autoincrement=True) -# user_id = Column(Integer, ForeignKey('user.id')) -# quiz_name = Column(Text) -# -# -# class Question(Base): -# __tablename__ = 'question' -# -# id = Column(Integer, primary_key=True, autoincrement=True) -# quiz_id = Column(Integer, ForeignKey('quiz.id')) -# type = Column(Enum(QuestionTypes)) -# description = Column(Text) -# possible_answers = () -# -# -# class Answers(Base): -# pass diff --git a/web-app/adapters/repository.py b/web-app/adapters/repository.py index 5846708..2fc2bf6 100644 --- a/web-app/adapters/repository.py +++ b/web-app/adapters/repository.py @@ -1,4 +1,4 @@ -# from abc import ABC, abstractmethod +# static analysis: ignore[import_failed] from domain import model @@ -24,17 +24,25 @@ class SqlAlchemyRepository: def __init__(self, session): self.session = session - def add_user(self, user: model.Quiz): + def add_user(self, user: model.User): self.session.add(user) self.session.commit() - def get_user(self, user_id): + def get_user_by_id(self, user_id): return self.session.query(model.User).filter_by(id=user_id).one() + def get_user_by_email(self, email): + return self.session.query(model.User).filter_by(email=email).one() + + def delete_user(self, user: model.User): + self.session.delete(user) + self.session.commit() + # Working with quizzes - def add_quiz(self, quiz: model.Quiz): + def add_quiz(self, quiz: model.Quiz) -> int: self.session.add(quiz) self.session.commit() + return quiz.id def get_quiz(self, quiz_id): return self.session.query(model.Quiz).filter_by(id=quiz_id).one() @@ -43,22 +51,26 @@ def put_quiz(self, quiz: model.Quiz): # self.session.query(model.Quiz).f raise NotImplemented - def delete_quiz(self, quiz_id): - self.session.query(model.Quiz).filter_by(id=quiz_id).delete(synchronize_session=False) - # TODO: deletion of questions and answers + def delete_quiz(self, quiz: model.Quiz): + self.session.delete(quiz) self.session.commit() def list_quizzes(self, user_id): - return self.session.query(model.User).filter_by(user_id=user_id).all() + return self.session.query(model.Quiz).filter_by(user_id=user_id).all() # Working with questions def add_question(self, question: model.Question): self.session.add(question) self.session.commit() + return question.id def get_question(self, question_id): return self.session.query(model.Question).filter_by(id=question_id).one() + def delete_question(self, question: model.Question): + self.session.delete(question) + self.session.commit() + def list_questions(self, quiz_id): return self.session.query(model.Question).filter_by(quiz_id=quiz_id).all() @@ -66,12 +78,30 @@ def list_questions(self, quiz_id): def add_answer(self, answer: model.Answer): self.session.add(answer) self.session.commit() + return answer.id def get_answer(self, answer_id): - return self.session.query(model.Answer).filter_by(id=answer_id) + return self.session.query(model.Answer).filter_by(id=answer_id).one() + + def delete_answer(self, answer: model.Answer): + self.session.delete(answer) + self.session.commit() def list_answers(self, question_id): - return self.session.query(model.Answer).filter_by(question_id=question_id) + return self.session.query(model.Answer).filter_by(question_id=question_id).all() + + # -- for test + def get_users(self): + return self.session.query(model.User).all() + + def get_quizzes(self): + return self.session.query(model.Quiz).all() + + def get_answers(self): + return self.session.query(model.Answer).all() + + def get_questions(self): + return self.session.query(model.Question).all() # Testing Repository diff --git a/web-app/app_start.py b/web-app/app_start.py index 7247b59..eb07319 100644 --- a/web-app/app_start.py +++ b/web-app/app_start.py @@ -1,3 +1,4 @@ +# static analysis: ignore[import_failed] from entrypoints.flask_app import app if __name__ == "__main__": diff --git a/web-app/config.py b/web-app/config.py index e69de29..cf9f1d3 100644 --- a/web-app/config.py +++ b/web-app/config.py @@ -0,0 +1,2 @@ +def get_postgres_uri(): + return "postgresql://test:test@postgres_db:5432/test" diff --git a/web-app/domain/__init__.py b/web-app/domain/__init__.py index 36ec720..48d0fae 100644 --- a/web-app/domain/__init__.py +++ b/web-app/domain/__init__.py @@ -1 +1,2 @@ +# static analysis: ignore[import_failed] from . import model \ No newline at end of file diff --git a/web-app/domain/model.py b/web-app/domain/model.py index 7418dfb..b20ada2 100644 --- a/web-app/domain/model.py +++ b/web-app/domain/model.py @@ -4,7 +4,8 @@ # Types declaration # For user -NickName = NewType('NickName', str) +Name = NewType('Name', str) +Surname = NewType('Surname', str) Email = NewType('Email', str) Password = NewType('Password', str) @@ -18,41 +19,68 @@ class Answer: - def __init__(self, text: QuestionAnswer, is_correct: bool): + def __init__(self, question_id: int, text: QuestionAnswer, correct_answer: bool): self.text = text - self.is_correct = is_correct + self.correct_answer = correct_answer + self.question_id = question_id + + def to_dict(self): + return { + "text": self.text, + "correct_answer": self.correct_answer, + "question_id": self.question_id + } # Possible types of question in a quiz class QuestionTypes(enum.Enum): - poll = "polls" + poll = "poll" quiz = "quiz" # Question class, to store information about question: # question type, description, possible answers, and correct answers class Question: - def __init__(self, question_type: QuestionTypes, text: QuestionDescription, quiz_id: int): + def __init__(self, question_type: str, text: QuestionDescription, quiz_id: int, time_limit: int): self.question_type = question_type self.text = text self.quiz_id = quiz_id + self.time = time_limit - # [maybe deleted] - # return next question - def next(self): - pass + def to_dict(self): + return { + "quiz_id": self.quiz_id, + "text": self.text, + "question_type": self.question_type, + "time": self.time + } # Quiz class, class Quiz: def __init__(self, quiz_name: QuizName, user_id: int = 0): - self.quiz_name = quiz_name + self.name = quiz_name self.user_id = user_id + def to_dict(self): + return { + "user_id": self.user_id, + "name": self.name + } + # User class, to manage stored in db information through nickname/email class User: - def __init__(self, nickname: NickName, email: Email, password: Password): - self.nickname = nickname + def __init__(self, name: Name, surname: Surname, email: Email, password: Password): + self.name = name + self.surname = surname self.email = email self.password = password + + def to_dict(self): + return { + "name": self.name, + "surname": self.surname, + "email": self.email, + "password": self.password + } diff --git a/web-app/entrypoints/__init__.py b/web-app/entrypoints/__init__.py index e69de29..1a975b4 100644 --- a/web-app/entrypoints/__init__.py +++ b/web-app/entrypoints/__init__.py @@ -0,0 +1 @@ +# static analysis: ignore[import_failed] diff --git a/web-app/entrypoints/api/__init__.py b/web-app/entrypoints/api/__init__.py index e69de29..1a975b4 100644 --- a/web-app/entrypoints/api/__init__.py +++ b/web-app/entrypoints/api/__init__.py @@ -0,0 +1 @@ +# static analysis: ignore[import_failed] diff --git a/web-app/entrypoints/api/api_initialization.py b/web-app/entrypoints/api/api_initialization.py index 1d23479..0660342 100644 --- a/web-app/entrypoints/api/api_initialization.py +++ b/web-app/entrypoints/api/api_initialization.py @@ -1,13 +1,17 @@ +# static analysis: ignore[import_failed] from entrypoints.api.resource import AnswerResource, QuizResource, QuestionResource, UserResource + from flask_restful import Api def api_initialization(app) -> Api: api = Api(app) - api.add_resource(UserResource.UserResource, '/api/user/') - api.add_resource(UserResource.UserRegistrationResource, '/api/user') + api.add_resource(UserResource.UserLoginResource, '/api/user/login') + api.add_resource(UserResource.UserRegistrationResource, '/api/user/register') api.add_resource(QuizResource.QuizResource, '/api/user//quiz/') api.add_resource(QuizResource.QuizListResource, '/api/user//quiz') + api.add_resource(QuestionResource.QuestionResource, '/api/quiz//question/') + return api diff --git a/web-app/entrypoints/api/resource/AnswerResource.py b/web-app/entrypoints/api/resource/AnswerResource.py index c46472e..474772f 100644 --- a/web-app/entrypoints/api/resource/AnswerResource.py +++ b/web-app/entrypoints/api/resource/AnswerResource.py @@ -1,3 +1,4 @@ +# static analysis: ignore[import_failed] from flask import jsonify from flask_restful import reqparse, abort, Resource from adapters.repository import SqlAlchemyRepository, get_repo diff --git a/web-app/entrypoints/api/resource/QuestionResource.py b/web-app/entrypoints/api/resource/QuestionResource.py index 74ee5fe..edbc3ea 100644 --- a/web-app/entrypoints/api/resource/QuestionResource.py +++ b/web-app/entrypoints/api/resource/QuestionResource.py @@ -1,3 +1,4 @@ +# static analysis: ignore[import_failed] from flask import jsonify from flask_restful import reqparse, abort, Resource from adapters.repository import SqlAlchemyRepository, get_repo diff --git a/web-app/entrypoints/api/resource/QuizResource.py b/web-app/entrypoints/api/resource/QuizResource.py index 7f63be5..827cdee 100644 --- a/web-app/entrypoints/api/resource/QuizResource.py +++ b/web-app/entrypoints/api/resource/QuizResource.py @@ -1,50 +1,138 @@ -from flask import jsonify +# static analysis: ignore[import_failed] +from flask import jsonify, make_response from flask_restful import reqparse, abort, Resource from adapters.repository import SqlAlchemyRepository, get_repo + from domain.model import Quiz -parser = reqparse.RequestParser() -parser.add_argument('name', required=True) -parser.add_argument('questions', required=True) +from service_layer import services + +quiz_creation_parser = reqparse.RequestParser() +quiz_creation_parser.add_argument('name', required=True) +quiz_creation_parser.add_argument('questions', type=dict, action="append", required=True) + + +def check_for_credentials(args: dict): + email, password = services.email_and_pass_from(args) + + repo = get_repo() + user = repo.get_user_by_email(email) + + if not user: + abort(404, message="Unauthorized") + + if not user.password == password: + abort(404, message="Unauthorized") + + +def abort_if_user_not_found(user_id: int): + repo = get_repo() + user = repo.get_user_by_id(user_id) + if not user: + abort(404, message="Unauthorized") + + +def abort_if_quiz_not_found(quiz_id): + repo = get_repo() + quiz = repo.get_quiz(quiz_id) + + if not quiz: + abort(404, message="Quiz is not found") + + +def create_quiz(user_id, args): + abort_if_user_not_found(user_id) + + repo = get_repo() + + quiz = services.quiz_from(args, user_id) + quiz_id = repo.add_quiz(quiz) + + questions_and_answers = services.questions_of_quiz_creation_from(args, quiz_id) + + for (question, answers_args) in questions_and_answers: + question_id = repo.add_question(question) + for answers in services.answers_for_one_question_of_quiz_creation_from(answers_args, question_id): + repo.add_answer(answers) class QuizResource(Resource): - def get(self, quiz_id): + def get(self, user_id, quiz_id): + abort_if_quiz_not_found(quiz_id) + repo = get_repo() quiz = repo.get_quiz(quiz_id) - return quiz - def put(self, quiz_id): - args = parser.parse_args() + if quiz.user_id != user_id: + abort(403, message="You are not allowed to get this information") + + questions = repo.list_questions(quiz_id) + questions_list = [] + # assert len(questions) != 0 + for question in questions: + questions_list.append(question.to_dict()) + + answers_list = [] + answers = repo.list_answers(question.id) + for answer in answers: + answers_list.append(answer.to_dict()) + questions_list[-1]["answers"] = answers_list + + quiz_dict = quiz.to_dict() + quiz_dict["questions"] = questions_list + return make_response(quiz_dict, 200) + + def put(self, user_id, quiz_id): + abort_if_quiz_not_found(quiz_id) + + args = quiz_creation_parser.parse_args() repo = get_repo() - repo.put_quiz(Quiz(quiz_id=quiz_id, name=args['name'], user_id=args['user_id'])) + quiz = repo.get_quiz(quiz_id) + + if quiz.user_id != user_id: + abort(403, message="You are not allowed to perform this action") + + repo.delete_quiz(quiz) + create_quiz(user_id, args) - # TODO: Putting questions + answers + return make_response("Quiz recreated", 200) - return jsonify({"success": "OK"}) + def delete(self, user_id, quiz_id): + abort_if_quiz_not_found(quiz_id) - def delete(self, quiz_id): repo = get_repo() - quiz = repo.delete_quiz(quiz_id) - # TODO: Deleting questions + answers + quiz = repo.get_quiz(quiz_id) + + if quiz.user_id != user_id: + abort(403, message="You are not allowed to perform this action") + + repo.delete_quiz(quiz) - return jsonify({"success": "OK"}) + return make_response("Quiz deleted", 200) class QuizListResource(Resource): def get(self, user_id): + abort_if_user_not_found(user_id) + repo = get_repo() quizzes = repo.list_quizzes(user_id) - return quizzes - def post(self, user_id): - args = parser.parse_args() - repo = get_repo() + user = repo.get_user_by_id(user_id) + + quizzes_as_dict = [] - repo.add_quiz(Quiz(quiz_name=args['name'], user_id=user_id)) + for quiz in quizzes: + quiz_as_dict = quiz.to_dict + quiz_as_dict["quiz_id"] = quiz.id + quizzes_as_dict.append(dicted_quiz) - # TODO: parse questions and answers to add them to SQL - # + return make_response( + {"quizzes": dicted_quizzes, "quiz_number": len(quizzes), "user": user.to_dict()}, 200) + + def post(self, user_id): + args = quiz_creation_parser.parse_args() - return jsonify({"success": "OK"}) + create_quiz(user_id, args) + return make_response("Quiz created", 201) diff --git a/web-app/entrypoints/api/resource/UserResource.py b/web-app/entrypoints/api/resource/UserResource.py index 2e65f1e..e85f051 100644 --- a/web-app/entrypoints/api/resource/UserResource.py +++ b/web-app/entrypoints/api/resource/UserResource.py @@ -1,30 +1,80 @@ -from flask import jsonify +# static analysis: ignore[import_failed] +import flask +from flask import jsonify, make_response from flask_restful import reqparse, abort, Resource from adapters.repository import SqlAlchemyRepository, get_repo + from domain.model import User -parser = reqparse.RequestParser() -parser.add_argument('nickname', required=True) -parser.add_argument('email', required=True) -parser.add_argument('password', required=True) +from service_layer import services + +from sqlalchemy.exc import IntegrityError + +login_parser = reqparse.RequestParser() +login_parser.add_argument('email', required=True) +login_parser.add_argument('password', required=True) +registration_parser = reqparse.RequestParser() +registration_parser.add_argument('name', required=True) +registration_parser.add_argument('surname', required=True) +registration_parser.add_argument('email', required=True) +registration_parser.add_argument('password', required=True) + + +def check_for_credentials(args: dict) -> int: + email, password = services.email_and_pass_from(args) -def abort_if_user_not_found(user_id): repo = get_repo() - user = repo.get_user(user_id) + user = repo.get_user_by_email(email) + if not user: - abort(404, message=f"User {user_id} not found") + abort(404, message="Unauthorized") + + if not user.password == password: + abort(404, message="Unauthorized") + + return user.id + + +def abort_if_user_already_exists(email): + repo = get_repo() + + user = repo.get_user_by_email(email) + if user: + abort(403, message="User already exists") -class UserResource(Resource): - def get(self, user_id): - abort_if_user_not_found(user_id) - user = get_repo().get_user(user_id) - return user + +def abort_if_user_not_found(user_id: int, args: dict): + email, password = services.email_and_pass_from(args) + + repo = get_repo() + user = repo.get_user_by_id(user_id) + if not user or user.email != email or user.password != password: + abort(404, message="Unauthorized") + + +class UserLoginResource(Resource): + def post(self): + args = login_parser.parse_args() + + user_id = check_for_credentials(args) + + return {"user_id": user_id} class UserRegistrationResource(Resource): def post(self): - args = parser.parse_args() + args = registration_parser.parse_args() repo = get_repo() - repo.add_user(User(nickname=args['nickname'], email=args['email'], password=args['password'])) + + user = services.user_from(args) + + # abort_if_user_already_exists(user.email) + try: + repo.add_user(user) + return make_response("User created", 201) + + except IntegrityError: + repo.session.rollback() + return make_response("User already exists", 403) diff --git a/web-app/entrypoints/api/resource/__init__.py b/web-app/entrypoints/api/resource/__init__.py index e69de29..1a975b4 100644 --- a/web-app/entrypoints/api/resource/__init__.py +++ b/web-app/entrypoints/api/resource/__init__.py @@ -0,0 +1 @@ +# static analysis: ignore[import_failed] diff --git a/web-app/entrypoints/flask_app.py b/web-app/entrypoints/flask_app.py index 18eb1e2..a701e53 100644 --- a/web-app/entrypoints/flask_app.py +++ b/web-app/entrypoints/flask_app.py @@ -1,4 +1,8 @@ -from flask import Flask, request +# static analysis: ignore[import_failed] +import json + +from flask import Flask, request, render_template +from flask_cors import CORS from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker @@ -7,10 +11,88 @@ from adapters import orm, repository from service_layer import services from entrypoints.api import api_initialization +from tests import test_orm +engine = create_engine(config.get_postgres_uri()) +get_session = sessionmaker(bind=engine) +repository.initialize_repo(get_session()) orm.start_mappers() -# get_session = sessionmaker(bind=create_engine(config.get_postgres_uri())) -# initialize_repo(get_session()) -app = Flask(__name__) + +# To delete all tables +# orm.delete_all(engine) + +orm.create_all(engine) +app = Flask(__name__, template_folder='./../static/templates') api = api_initialization.api_initialization(app) +cors = CORS(app, resources={r"/api/*": {"origins": "*"}}) + + +@app.route('/') +def main_page(): + return render_template("main_page.html") + + +@app.route('/login') +def login_page(): + return render_template('authorization_page.html') + + +@app.route('/register') +def registration_page(): + return render_template("registration_page.html") + + +# [will be deleted] +@app.route('/lobby') +def lobby_page(): + return render_template("waiting_hall.html") + + +# For tests through docker +@app.route('/tests/users') +def test(): + repo = repository.get_repo() + + new_items = list() + for item in repo.get_users(): + new_items.append(item.to_dict()) + + return json.dumps({"items": new_items}) + + +@app.route('/tests/user_creation') +def test_user_creation(): + repo = repository.get_repo() + + test_orm.test_add_user(repo) + + return "

OK

" + + +@app.route('/tests/quizzes') +def test_quizzes_list(): + repo = repository.get_repo() + + new_items = list() + for item in repo.get_quizzes(): + new_items.append(item.to_dict()) + + return json.dumps({"items": new_items}) + + +@app.route('/tests/get_user_by_email/') +def test_user_by_email(email): + repo = repository.get_repo() + + user = repo.get_user_by_email(email) + return json.dumps({"user": user.to_dict()}) + + +@app.route('/tests/answers') +def test_answers_list(): + repo = repository.get_repo() + dict_items = [] + for item in repo.get_answers(): + dict_items.append(item.to_dict()) + return json.dumps({"answers": dict_items}) diff --git a/web-app/main.py b/web-app/main.py index 695cac7..18dac90 100644 --- a/web-app/main.py +++ b/web-app/main.py @@ -1,3 +1,4 @@ +# static analysis: ignore[import_failed] from entrypoints import flask_app flask_app.main() diff --git a/web-app/requirements.txt b/web-app/requirements.txt index 556c972..60e6639 100644 --- a/web-app/requirements.txt +++ b/web-app/requirements.txt @@ -13,3 +13,5 @@ SQLAlchemy==1.4.37 waitress==2.1.2 Werkzeug==2.1.2 zipp==3.8.0 +psycopg2==2.9.3 +flask-cors==3.0.10 diff --git a/web-app/service_layer/__init__.py b/web-app/service_layer/__init__.py index e69de29..1a975b4 100644 --- a/web-app/service_layer/__init__.py +++ b/web-app/service_layer/__init__.py @@ -0,0 +1 @@ +# static analysis: ignore[import_failed] diff --git a/web-app/service_layer/services.py b/web-app/service_layer/services.py index e69de29..aeef318 100644 --- a/web-app/service_layer/services.py +++ b/web-app/service_layer/services.py @@ -0,0 +1,77 @@ +# static analysis: ignore[import_failed] +from domain import model + + +# user_from gets args and creates User class +# user_args should contain following: +# name +# surname +# email +# password +def user_from(user_args: dict) -> model.User: + user_name = user_args['name'] + user_surname = user_args['surname'] + user_email = user_args['email'] + user_password = user_args['password'] + + return model.User( + name=user_name, + surname=user_surname, + email=user_email, + password=user_password + ) + + +def quiz_from(quiz_args: dict, user_id: int) -> model.Quiz: + quiz = model.Quiz( + quiz_name=quiz_args["name"], + user_id=user_id + ) + return quiz + + +def questions_of_quiz_creation_from(quiz_args: dict, quiz_id: int) -> [(model.Question, dict)]: + questions = [ + (model.Question( + question_type=question_args["question_type"], + text=question_args["text"], + time_limit=question_args["time"], + quiz_id=quiz_id + ), question_args["answers"]) + for question_args in quiz_args["questions"] + ] + # return quiz_args["questions"] + # for question_args in quiz_args["questions"]: + # questions.append((model.Question( + # question_type=question_args["question_type"], + # text=question_args["text"], + # time_limit=question_args["time"], + # quiz_id=quiz_id), + # question_args["answers"])) + + return questions + + +def answers_for_one_question_of_quiz_creation_from(answers_args: dict, question_id) -> [model.Answer]: + answers = [ + model.Answer( + text=answer_args["text"], + correct_answer=answer_args["correct_answer"], + question_id=question_id + ) + for answer_args in answers_args + ] + return answers + + +def email_and_pass_from(args: dict) -> (str, str): + return args['email'], args['password'] + + +def user_from(user_args) -> model.User: + return model.User( + name=user_args["name"], + surname=user_args["surname"], + email=user_args["email"], + password=user_args["password"] + ) diff --git a/web-app/tests/test_api.py b/web-app/tests/test_api.py index 8eb91b7..8410451 100644 --- a/web-app/tests/test_api.py +++ b/web-app/tests/test_api.py @@ -1,40 +1,144 @@ +# static analysis: ignore[import_failed] import json -from requests import get, post +from requests import get, post, delete, put -def test_quiz_post_request(): +def test_quizzes_post_request(): + answers1 = [ + {"text": "Hello", "correct_answer": True}, + {"text": "World", "correct_answer": False} + ] + question1 = {"question_type": "poll", + "text": "questions1", + "time": 60, + "answers": answers1} + + answers2 = [ + {"text": "Hello", "correct_answer": True}, + {"text": "World", "correct_answer": False} + ] + + question2 = { + "question_type": "quiz", + "text": "questions2", + "time": 15, + "answers": answers2 + } + quiz_post_request_body = { - "id": 500, - "questions": {"question1": {"Hello": True, "World": True}, "question2": {"Hello": True, "World": True}}, + "questions": [question1, question2], "name": "Quiz name" } - url = "http://127.0.0.1:8080/api/user/1/quiz" - return post(url, data=quiz_post_request_body).text + # print(type(quiz_post_request_body["questions"])) + + url = "http://127.0.0.1:8888/api/user/1/quiz" + assert post(url, json=quiz_post_request_body, + headers={"Content-Type": "application/json"}).status_code == 201 def test_quiz_get_request(): - url = "http://127.0.0.1:8080/api/user/1/quiz" - return get(url).text + url = "http://127.0.0.1:8888/api/user/1/quiz/1" + assert get(url).status_code == 200 + + +# def test_quiz_delete_request(): +# quiz_post_request_body = { +# "questions": {"question1": {"Hello": True, "World": True}, "question2": {"Hello": True, "World": True}}, +# "name": "Quiz name" +# } +# url = "http://127.0.0.1:8888/api/user/1/quiz" +# +# post(url, data=json.dumps(quiz_post_request_body), headers={"Content-Type": "application/json"}) +# +# assert delete(url + "/1").status_code == 200 + + +def test_quizzes_get_request(): + url = "http://127.0.0.1:8888/api/user/1/quiz" + + assert get(url).status_code == 200 + + +def test_quiz_put_request(): + url = "http://127.0.0.1:8888/api/user/1/quiz/1" + + answers1 = [ + {"text": "Hello", "correct_answer": False}, + {"text": "Worldzzz", "correct_answer": False} + ] + question1 = {"question_type": "poll", + "text": "questions1", + "time": 60, + "answers": answers1} + + answers2 = [ + {"text": "Hello", "correct_answer": True}, + {"text": "World", "correct_answer": False} + ] + + question2 = { + "question_type": "quiz", + "text": "questions2zzz", + "time": 15, + "answers": answers2 + } + + quiz_put_request_body = { + "questions": [question1, question2], + "name": "Quiz recreated name" + } + + assert put(url, json=quiz_put_request_body, headers={"Content-Type": "application/json"}).status_code == 200 -def test_user_get_request(): - url = "http://127.0.0.1:8080/api/user/1" - return get(url).text +def test_user_login_request(): + url = "http://127.0.0.1:8888/api/user/login" + + body = {"email": "vikochka_kruk@mail.ru", + "password": "123"} + + assert post(url, json=body).status_code == 200 def test_user_post_request(): - url = "http://127.0.0.1:8080/api/user" + url = "http://127.0.0.1:8888/api/user/register" user_post_request_body = { - 'nickname': 'Il', + 'name': 'Il', + 'surname': 'hello', 'email': 'il@k.r', 'password': 'strong_pass' } - post(url, data=user_post_request_body) + assert post(url, json=user_post_request_body, headers={"Content-Type": "application/json"}).status_code == 201 + + +def test_question_get_request(): + url = "http://127.0.0.1:8888/api/quiz/1/question/2" + assert get(url).status_code == 200 -# print(test_quiz_post_request()) +def test_question_post_request(): + url = "http://127.0.0.1:8888/api/quiz/1/question/2" + + question_post_request_body = {"answers": ["Incorrect answer", "Correct answer"]} + assert post(url, json=question_post_request_body, headers={"Content-Type": "application/json"}).status_code == 201 + + +def test_quiz_delete_request(): + url = "http://127.0.0.1:8888/api/user/1/quiz/1" + + assert delete(url).status_code == 200 + + +# print(test_user_post_request()) +# print(test_quiz_delete_request()) +# print(test_quiz_put_request()) +# print(test_quizzes_post_request()) # print(test_quiz_get_request()) -print(test_user_post_request()) +# print(test_user_login_request()) +# print(test_question_get_request()) +# print(test_user_get_request()) + +# print(str([{"Hello": True, "World": False}])) diff --git a/web-app/tests/test_orm.py b/web-app/tests/test_orm.py index e69de29..40e5d27 100644 --- a/web-app/tests/test_orm.py +++ b/web-app/tests/test_orm.py @@ -0,0 +1,68 @@ +# static analysis: ignore[import_failed] +from adapters import repository +from domain import model + + +# Unit tests can be written only for user or quiz (With user) +# Because we use foreign key to link all info in db + +def test_add_user(repo): + new_user = model.User("Fedor", "Surname", "fedya@gg.ru", "CoolPass") + + repo.add_user(new_user) + db_user = repo.session.query(model.User).filter_by(email=new_user.email).one() + + assert db_user == new_user + + repo.delete_user(new_user) + + +def test_add_and_quiz(repo): + new_user = model.User("Fedor", "Surname", "fedya@gg.ru", "CoolPass") + user_id = repo.add_user(new_user) + + new_quiz = model.Quiz(quiz_name="Quiz name", user_id=user_id) + quiz_id = repo.add_quiz(new_quiz) + + db_quiz = repo.get_quiz(quiz_id) + + assert db_quiz == new_quiz + + repo.delete_user(new_user) + + +def test_add_and_question(repo): + new_user = model.User("Fedor", "Surname", "fedya@gg.ru", "CoolPass") + user_id = repo.add_user(new_user) + + new_quiz = model.Quiz(quiz_name="Quiz name", user_id=user_id) + quiz_id = repo.add_quiz(new_quiz) + + question = model.Question(question_type="poll", text="questions1", time_limit=60, quiz_id=quiz_id) + question_id = repo.add_questions(question1) + + db_question = repo.get_question(question_id) + + assert db_question == question + + repo.delete_user(new_user) + + +def test_add_and_answer(repo): + new_user = model.User("Fedor", "Surname", "fedya@gg.ru", "CoolPass") + user_id = repo.add_user(new_user) + + new_quiz = model.Quiz(quiz_name="Quiz name", user_id=user_id) + quiz_id = repo.add_quiz(new_quiz) + + question = model.Question(question_type="poll", text="questions1", time_limit=60, quiz_id=quiz_id) + question_id = repo.add_questions(question) + + answer = model.Answer(text="Hello", correct_answer=True, question_id=question_id) + answer_id = repo.add_answer(answer) + + db_answer = repo.get_answer(answer_id) + + assert db_answer == answer + + repo.delete_user(new_user)