feat: adjustment on the dependencies
This commit is contained in:
parent
339da89e72
commit
f747880122
Binary file not shown.
|
@ -0,0 +1,2 @@
|
|||
# from flask import Flask
|
||||
from app.main import createApp
|
|
@ -1,6 +1,6 @@
|
|||
from flask import Blueprint
|
||||
from controllers import AuthController
|
||||
from di_container import Container
|
||||
from app.controllers import AuthController
|
||||
from app.di_container import Container
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from flask import Blueprint
|
||||
from controllers import HistoryController
|
||||
from di_container import Container
|
||||
from app.controllers import HistoryController
|
||||
from app.di_container import Container
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
history_blueprint = Blueprint("history", __name__)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from flask import Blueprint, request
|
||||
from di_container import Container
|
||||
from app.di_container import Container
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from controllers import QuizController
|
||||
from app.controllers import QuizController
|
||||
|
||||
|
||||
quiz_bp = Blueprint("quiz", __name__)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from flask import Blueprint, request
|
||||
from di_container import Container
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from controllers import SessionController
|
||||
from app.di_container import Container
|
||||
from app.controllers import SessionController
|
||||
|
||||
session_bp = Blueprint("session", __name__)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from flask import Blueprint, request
|
||||
from di_container import Container
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from controllers import SubjectController
|
||||
from app.di_container import Container
|
||||
from app.controllers import SubjectController
|
||||
|
||||
|
||||
subject_blueprint = Blueprint("subject", __name__)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from flask import Blueprint
|
||||
from controllers import UserController
|
||||
from di_container import Container
|
||||
from app.di_container import Container
|
||||
from app.controllers import UserController
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
user_blueprint = Blueprint("user", __name__)
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
from flask import jsonify, request, current_app
|
||||
from pydantic import ValidationError
|
||||
from models.login.login_response import UserResponseModel
|
||||
from schemas.basic_response_schema import ResponseSchema
|
||||
from schemas.google_login_schema import GoogleLoginSchema
|
||||
from schemas import LoginSchema
|
||||
from services import UserService, AuthService
|
||||
from exception import AuthException
|
||||
from mapper import UserMapper
|
||||
from helpers import make_response
|
||||
from app.models.login.login_response import UserResponseModel
|
||||
from app.schemas.basic_response_schema import ResponseSchema
|
||||
from app.schemas.google_login_schema import GoogleLoginSchema
|
||||
from app.schemas import LoginSchema
|
||||
from app.services import UserService, AuthService
|
||||
from app.exception import AuthException
|
||||
from app.mapper import UserMapper
|
||||
from app.helpers import make_response
|
||||
import logging
|
||||
|
||||
logging = logging.getLogger(__name__)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from services import HistoryService
|
||||
from helpers import make_error_response, make_response
|
||||
from app.services import HistoryService
|
||||
from app.helpers import make_error_response, make_response
|
||||
|
||||
|
||||
class HistoryController:
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import json
|
||||
from pydantic import ValidationError
|
||||
from schemas.requests import QuizCreateSchema, UserAnswerSchema
|
||||
from schemas.response import QuizCreationResponse
|
||||
from services import QuizService, AnswerService
|
||||
from helpers import make_response, make_error_response
|
||||
from exception import ValidationException, DataNotFoundException
|
||||
from app.schemas.requests import QuizCreateSchema, UserAnswerSchema
|
||||
from app.schemas.response import QuizCreationResponse
|
||||
from app.services import QuizService, AnswerService
|
||||
from app.helpers import make_response, make_error_response
|
||||
from app.exception import ValidationException, DataNotFoundException
|
||||
|
||||
|
||||
class QuizController:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from flask import request, jsonify
|
||||
from flask.views import MethodView
|
||||
from services.session_service import SessionService
|
||||
from app.services.session_service import SessionService
|
||||
from app.helpers import make_response
|
||||
|
||||
|
||||
class SessionController(MethodView):
|
||||
|
@ -24,4 +25,8 @@ class SessionController(MethodView):
|
|||
limit_participan=data["limit_participan"],
|
||||
)
|
||||
|
||||
return jsonify(session.dict()), 201
|
||||
return make_response(
|
||||
message="succes create room",
|
||||
data=session,
|
||||
status_code=201,
|
||||
)
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
# socket_controller.py
|
||||
from flask_socketio import SocketIO, emit, join_room, leave_room
|
||||
from flask import request
|
||||
from services import SessionService
|
||||
from app.services import SessionService
|
||||
import threading
|
||||
import time
|
||||
|
||||
|
||||
class SocketController:
|
||||
|
@ -22,33 +23,81 @@ class SocketController:
|
|||
|
||||
@self.socketio.on("join_room")
|
||||
def handle_join_room(data):
|
||||
session_code = data.get("session_code")
|
||||
user_id = data.get("user_id")
|
||||
session_code = data["session_code"]
|
||||
user_id = data["user_id"]
|
||||
|
||||
if not session_code or not user_id:
|
||||
emit("error", {"message": "session_id and user_id are required"})
|
||||
emit("error", {"message": "session_code and user_id are required"})
|
||||
return
|
||||
|
||||
session = self.session_service.join_session(session_code, user_id)
|
||||
session = self.session_service.join_session(
|
||||
session_code=session_code, user_id=user_id
|
||||
)
|
||||
|
||||
if session is None:
|
||||
emit("error", {"message": "Failed to join session or session inactive"})
|
||||
return
|
||||
|
||||
join_room(session_code)
|
||||
user_data = self.session_service.join_session()
|
||||
if session["is_admin"] == True:
|
||||
emit(
|
||||
"room_message",
|
||||
{
|
||||
"message": f"admin has joined the room.",
|
||||
"room": session_code,
|
||||
"argument": "adm_update",
|
||||
},
|
||||
room=session_code,
|
||||
)
|
||||
return
|
||||
|
||||
emit(
|
||||
"room_message",
|
||||
{
|
||||
"message": "someone has joined the room.",
|
||||
"message": f"user {session['username']} has joined the room.",
|
||||
"room": session_code,
|
||||
"argument": "adm_update",
|
||||
"data": session,
|
||||
},
|
||||
room=session_code,
|
||||
)
|
||||
|
||||
@self.socketio.on("submit_answer")
|
||||
def handle_submit_answer(data):
|
||||
session_id = data.get("session_id")
|
||||
user_id = data.get("user_id")
|
||||
question_index = data.get("question_index")
|
||||
answer = data.get("answer")
|
||||
|
||||
if not all([session_id, user_id, question_index is not None, answer]):
|
||||
emit(
|
||||
"error",
|
||||
{
|
||||
"message": "session_id, user_id, question_index, and answer are required"
|
||||
},
|
||||
)
|
||||
return
|
||||
|
||||
print(f"User {user_id} answered question {question_index} with {answer}")
|
||||
|
||||
# TODO: kamu bisa menyimpan jawaban ke database di sini
|
||||
# self.answer_service.save_answer(session_id, user_id, question_index, answer)
|
||||
|
||||
# Kirim notifikasi ke admin (host) atau semua peserta kalau perlu
|
||||
emit(
|
||||
"answer_submitted",
|
||||
{
|
||||
"user_id": user_id,
|
||||
"question_index": question_index,
|
||||
"answer": answer,
|
||||
},
|
||||
room=session_id,
|
||||
)
|
||||
|
||||
@self.socketio.on("leave_room")
|
||||
def handle_leave_room(data):
|
||||
session_id = data.get("session_id")
|
||||
user_id = data.get("user_id")
|
||||
username = data.get("username", "anonymous")
|
||||
|
||||
leave_room(session_id)
|
||||
|
@ -69,3 +118,80 @@ class SocketController:
|
|||
{"message": message, "from": username},
|
||||
room=session_id,
|
||||
)
|
||||
|
||||
@self.socketio.on("end_session")
|
||||
def handle_end_session(data):
|
||||
session_id = data.get("session_id")
|
||||
user_id = data.get("user_id")
|
||||
|
||||
if not session_id or not user_id:
|
||||
emit("error", {"message": "session_id and user_id required"})
|
||||
return
|
||||
|
||||
self.session_service.end_session(session_id=session_id, user_id=user_id)
|
||||
|
||||
emit(
|
||||
"room_closed",
|
||||
{"message": "Session has ended.", "room": session_id},
|
||||
room=session_id,
|
||||
)
|
||||
|
||||
@self.socketio.on("start_quiz")
|
||||
def handle_start_quiz(data):
|
||||
session_code = data.get("session_code")
|
||||
if not session_code:
|
||||
emit("error", {"message": "session_code is required"})
|
||||
return
|
||||
|
||||
emit("quiz_started", {"message": "Quiz has started!"}, room=session_code)
|
||||
|
||||
# Jalankan thread untuk mengirim soal simulasi setiap 5 detik
|
||||
threading.Thread(
|
||||
target=self._simulate_quiz_flow, args=(session_code,)
|
||||
).start()
|
||||
|
||||
def _simulate_quiz_flow(self, session_code):
|
||||
questions = [
|
||||
{
|
||||
"question_index": 0,
|
||||
"question": "Apa ibu kota Indonesia?",
|
||||
"type": "option",
|
||||
"options": ["Jakarta", "Bandung", "Surabaya"],
|
||||
},
|
||||
{
|
||||
"question_index": 1,
|
||||
"question": "2 + 2 = ?",
|
||||
"type": "option",
|
||||
"options": ["3", "4", "5"],
|
||||
},
|
||||
{
|
||||
"question_index": 2,
|
||||
"question": "Siapa presiden pertama Indonesia?",
|
||||
"type": "option",
|
||||
"options": ["Sukarno", "Soeharto", "Jokowi"],
|
||||
},
|
||||
{
|
||||
"question_index": 3,
|
||||
"question": "Tuliskan nama lengkap presiden pertama Indonesia.",
|
||||
"type": "fill_in_the_blank",
|
||||
"options": [],
|
||||
},
|
||||
{
|
||||
"question_index": 4,
|
||||
"question": "Indonesia merdeka pada tahun 1945.",
|
||||
"type": "true_false",
|
||||
"options": [],
|
||||
},
|
||||
]
|
||||
|
||||
for q in questions:
|
||||
print(f"Sending question {q['question_index']} to {session_code}")
|
||||
self.socketio.emit("quiz_question", q, room=session_code)
|
||||
time.sleep(20)
|
||||
# send true ansewr
|
||||
time.sleep(5)
|
||||
|
||||
# Setelah selesai semua soal, kirim command bahwa quiz selesai
|
||||
self.socketio.emit(
|
||||
"quiz_done", {"message": "Quiz has ended!"}, room=session_code
|
||||
)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from services.subject_service import SubjectService
|
||||
from helpers import make_response, make_error_response
|
||||
from app.services.subject_service import SubjectService
|
||||
from app.helpers import make_response, make_error_response
|
||||
|
||||
|
||||
class SubjectController:
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# /controllers/user_controller.py
|
||||
from flask import jsonify, request, current_app
|
||||
from services import UserService
|
||||
from schemas import RegisterSchema
|
||||
from app.services import UserService
|
||||
from app.schemas import RegisterSchema
|
||||
from pydantic import ValidationError
|
||||
from schemas import ResponseSchema
|
||||
from exception import AlreadyExistException
|
||||
from helpers import make_response
|
||||
from app.schemas import ResponseSchema
|
||||
from app.exception import AlreadyExistException
|
||||
from app.helpers import make_response
|
||||
|
||||
|
||||
class UserController:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from dependency_injector import containers, providers
|
||||
from repositories import (
|
||||
from app.repositories import (
|
||||
UserRepository,
|
||||
QuizRepository,
|
||||
UserAnswerRepository,
|
||||
|
@ -7,7 +7,7 @@ from repositories import (
|
|||
SessionRepository,
|
||||
)
|
||||
|
||||
from services import (
|
||||
from app.services import (
|
||||
UserService,
|
||||
AuthService,
|
||||
QuizService,
|
||||
|
@ -17,7 +17,7 @@ from services import (
|
|||
SessionService,
|
||||
)
|
||||
|
||||
from controllers import (
|
||||
from app.controllers import (
|
||||
UserController,
|
||||
AuthController,
|
||||
QuizController,
|
||||
|
@ -80,6 +80,7 @@ class Container(containers.DeclarativeContainer):
|
|||
session_service = providers.Factory(
|
||||
SessionService,
|
||||
session_repository,
|
||||
user_repository,
|
||||
)
|
||||
|
||||
# controllers
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from flask import jsonify, current_app
|
||||
from typing import Optional, Union
|
||||
from schemas import ResponseSchema, MetaSchema
|
||||
from app.schemas import ResponseSchema, MetaSchema
|
||||
import math
|
||||
|
||||
|
||||
|
|
27
app/main.py
27
app/main.py
|
@ -10,11 +10,11 @@ import logging
|
|||
from flask import Flask
|
||||
from flask_socketio import SocketIO
|
||||
|
||||
sys.path.append(os.path.dirname(__file__))
|
||||
# sys.path.append(os.path.dirname(__file__))
|
||||
|
||||
from di_container import Container
|
||||
from configs import Config, LoggerConfig
|
||||
from blueprints import (
|
||||
from app.di_container import Container
|
||||
from app.configs import Config, LoggerConfig
|
||||
from app.blueprints import (
|
||||
auth_blueprint,
|
||||
user_blueprint,
|
||||
quiz_bp,
|
||||
|
@ -23,7 +23,7 @@ from blueprints import (
|
|||
subject_blueprint,
|
||||
session_bp,
|
||||
)
|
||||
from database import init_db
|
||||
from app.database import init_db
|
||||
|
||||
|
||||
socketio = SocketIO(cors_allowed_origins="*")
|
||||
|
@ -52,12 +52,12 @@ def createApp() -> Flask:
|
|||
|
||||
container.wire(
|
||||
modules=[
|
||||
"blueprints.auth",
|
||||
"blueprints.user",
|
||||
"blueprints.quiz",
|
||||
"blueprints.history",
|
||||
"blueprints.subject",
|
||||
"blueprints.session",
|
||||
"app.blueprints.auth",
|
||||
"app.blueprints.user",
|
||||
"app.blueprints.quiz",
|
||||
"app.blueprints.history",
|
||||
"app.blueprints.subject",
|
||||
"app.blueprints.session",
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -70,8 +70,3 @@ def createApp() -> Flask:
|
|||
app.register_blueprint(session_bp, url_prefix="/api/session")
|
||||
|
||||
return app
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = createApp()
|
||||
socketio.run(app, host="0.0.0.0", port=5000, debug=Config.DEBUG)
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
from datetime import datetime
|
||||
from helpers import DatetimeUtil
|
||||
from models import QuizEntity, QuestionItemEntity, UserEntity
|
||||
from models.entities import SubjectEntity
|
||||
from schemas import QuizGetSchema, QuestionItemSchema
|
||||
from schemas.response import ListingQuizResponse
|
||||
from schemas.requests import QuizCreateSchema
|
||||
from app.helpers import DatetimeUtil
|
||||
from app.models import QuizEntity, QuestionItemEntity, UserEntity
|
||||
from app.models.entities import SubjectEntity
|
||||
from app.schemas import QuizGetSchema, QuestionItemSchema
|
||||
from app.schemas.response import ListingQuizResponse
|
||||
from app.schemas.requests import QuizCreateSchema
|
||||
|
||||
|
||||
class QuizMapper:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from datetime import datetime
|
||||
from typing import Dict, Optional
|
||||
from models import UserEntity, UserResponseModel
|
||||
from schemas import RegisterSchema
|
||||
from app.models import UserEntity, UserResponseModel
|
||||
from app.schemas import RegisterSchema
|
||||
|
||||
|
||||
class UserMapper:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
from models.entities import PyObjectId
|
||||
from app.models.entities import PyObjectId
|
||||
|
||||
|
||||
class SessionEntity(BaseModel):
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from typing import Optional
|
||||
from bson import ObjectId
|
||||
from pydantic import BaseModel, Field
|
||||
from models.entities import PyObjectId
|
||||
from app.models.entities import PyObjectId
|
||||
|
||||
|
||||
class SubjectEntity(BaseModel):
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from pymongo.collection import Collection
|
||||
from bson import ObjectId
|
||||
from typing import Optional, List
|
||||
from models import UserAnswerEntity
|
||||
from app.models import UserAnswerEntity
|
||||
|
||||
|
||||
class UserAnswerRepository:
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
from bson import ObjectId
|
||||
from typing import List, Optional
|
||||
from models import QuizEntity
|
||||
from app.models.entities import QuizEntity
|
||||
from pymongo.database import Database
|
||||
from pymongo.collection import Collection
|
||||
from datetime import datetime
|
||||
from helpers import DatetimeUtil
|
||||
|
||||
|
||||
class QuizRepository:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from pymongo.collection import Collection
|
||||
from pymongo.database import Database
|
||||
from typing import Optional
|
||||
from models.entities import SessionEntity
|
||||
from app.models.entities import SessionEntity
|
||||
|
||||
|
||||
class SessionRepository:
|
||||
|
@ -30,7 +30,7 @@ class SessionRepository:
|
|||
"""Update specific fields using $set"""
|
||||
result = self.collection.update_one(
|
||||
{"_id": session_id},
|
||||
{"$set": update_fields.model_dump(exclude_unset=True)},
|
||||
{"$set": update_fields},
|
||||
)
|
||||
return result.modified_count > 0
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ from typing import List, Optional
|
|||
from pymongo.database import Database
|
||||
from pymongo.collection import Collection
|
||||
from bson import ObjectId, errors as bson_errors
|
||||
from models.entities import SubjectEntity
|
||||
from app.models.entities import SubjectEntity
|
||||
|
||||
|
||||
class SubjectRepository:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from typing import Optional
|
||||
from bson import ObjectId
|
||||
from models import UserEntity
|
||||
from app.models.entities import UserEntity
|
||||
|
||||
|
||||
class UserRepository:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from typing import List
|
||||
from pydantic import BaseModel
|
||||
from schemas.response.recomendation.recomendation_response_schema import (
|
||||
from app.schemas.response.recomendation.recomendation_response_schema import (
|
||||
ListingQuizResponse,
|
||||
)
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from repositories import UserAnswerRepository, QuizRepository, UserRepository
|
||||
from schemas.requests import UserAnswerSchema
|
||||
from models import UserAnswerEntity
|
||||
from models.entities import AnswerItemEntity
|
||||
from exception import ValidationException
|
||||
from app.repositories import UserAnswerRepository, QuizRepository, UserRepository
|
||||
from app.schemas.requests import UserAnswerSchema
|
||||
from app.models import UserAnswerEntity
|
||||
from app.models.entities import AnswerItemEntity
|
||||
from app.exception import ValidationException
|
||||
|
||||
|
||||
class AnswerService:
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
from schemas import LoginSchema
|
||||
from repositories import UserRepository
|
||||
from mapper import UserMapper
|
||||
from app.schemas import LoginSchema
|
||||
from app.repositories import UserRepository
|
||||
from app.mapper import UserMapper
|
||||
from google.oauth2 import id_token
|
||||
from google.auth.transport import requests
|
||||
from configs import Config
|
||||
from exception import AuthException
|
||||
from app.configs import Config
|
||||
from app.exception import AuthException
|
||||
from werkzeug.security import check_password_hash
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from repositories import UserAnswerRepository, QuizRepository
|
||||
from schemas.response import HistoryResultSchema, QuizHistoryResponse, QuestionResult
|
||||
from app.repositories import UserAnswerRepository, QuizRepository
|
||||
from app.schemas.response import HistoryResultSchema, QuizHistoryResponse, QuestionResult
|
||||
|
||||
|
||||
class HistoryService:
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
from repositories import QuizRepository, UserRepository, SubjectRepository
|
||||
from schemas.requests import QuizCreateSchema
|
||||
from schemas.response import UserQuizListResponse, ListingQuizResponse, QuizGetSchema
|
||||
from exception import DataNotFoundException
|
||||
from mapper import QuizMapper
|
||||
from exception import ValidationException
|
||||
from helpers import DatetimeUtil
|
||||
from app.repositories import QuizRepository, UserRepository, SubjectRepository
|
||||
from app.schemas.requests import QuizCreateSchema
|
||||
from app.schemas.response import (
|
||||
UserQuizListResponse,
|
||||
ListingQuizResponse,
|
||||
QuizGetSchema,
|
||||
)
|
||||
from app.exception import DataNotFoundException, ValidationException
|
||||
from app.mapper import QuizMapper
|
||||
from app.helpers import DatetimeUtil
|
||||
|
||||
|
||||
class QuizService:
|
||||
|
@ -43,7 +46,7 @@ class QuizService:
|
|||
if author is None:
|
||||
continue
|
||||
mapped_quizzes.append(
|
||||
QuizMapper.quiz_to_recomendation_mapper(
|
||||
QuizMapper.quiz_to_recomendation_app.mapper(
|
||||
quiz_entity=quiz,
|
||||
user_entity=author,
|
||||
)
|
||||
|
@ -65,7 +68,7 @@ class QuizService:
|
|||
user = self.user_repostory.get_user_by_id(user_id)
|
||||
|
||||
quiz_data = [
|
||||
QuizMapper.quiz_to_recomendation_mapper(quiz, user) for quiz in quizzes
|
||||
QuizMapper.quiz_to_recomendation_app.mapper(quiz, user) for quiz in quizzes
|
||||
]
|
||||
|
||||
return UserQuizListResponse(total=total_user_quiz, quizzes=quiz_data)
|
||||
|
@ -107,7 +110,7 @@ class QuizService:
|
|||
for quiz in data:
|
||||
author = self.user_repostory.get_user_by_id(user_id=quiz.author_id)
|
||||
result.append(
|
||||
QuizMapper.quiz_to_recomendation_mapper(
|
||||
QuizMapper.quiz_to_recomendation_app.mapper(
|
||||
quiz_entity=quiz,
|
||||
user_entity=author,
|
||||
)
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from uuid import uuid4
|
||||
from repositories import SessionRepository, UserRepository
|
||||
from models.entities import SessionEntity
|
||||
from helpers import DatetimeUtil
|
||||
from app.repositories import SessionRepository, UserRepository
|
||||
from app.models.entities import SessionEntity
|
||||
from app.helpers import DatetimeUtil
|
||||
|
||||
|
||||
class SessionService:
|
||||
|
@ -11,11 +10,10 @@ class SessionService:
|
|||
self.repository = repository
|
||||
self.user_repository = user_repository
|
||||
|
||||
def create_session(
|
||||
self, quiz_id: str, host_id: str, limit_participan: int
|
||||
) -> SessionEntity:
|
||||
def create_session(self, quiz_id: str, host_id: str, limit_participan: int) -> str:
|
||||
generateed_code = uuid4().hex[:6].upper()
|
||||
session = SessionEntity(
|
||||
session_code=uuid4().hex[:6].upper(),
|
||||
session_code=generateed_code,
|
||||
quiz_id=quiz_id,
|
||||
host_id=host_id,
|
||||
created_at=DatetimeUtil.now_iso(),
|
||||
|
@ -24,36 +22,59 @@ class SessionService:
|
|||
current_question_index=0,
|
||||
is_active=True,
|
||||
)
|
||||
self.repository.insert(session)
|
||||
return session
|
||||
|
||||
def join_session(self, session_code: str, user_id: str) -> Optional[SessionEntity]:
|
||||
return {
|
||||
"session_id": self.repository.insert(session),
|
||||
"session_code": generateed_code,
|
||||
}
|
||||
|
||||
def join_session(self, session_code: str, user_id: str) -> dict:
|
||||
user = self.user_repository.get_user_by_id(user_id)
|
||||
session = self.repository.find_by_session_code(session_code=session_code)
|
||||
|
||||
if session is None or not session["is_active"]:
|
||||
if session is None or session.is_active == False:
|
||||
return None
|
||||
|
||||
if user_id not in session["participants"]:
|
||||
session["participants"].append(user_id)
|
||||
self.repository.update(
|
||||
session.id, {"participants": session["participants"]}
|
||||
)
|
||||
if session.host_id == user_id:
|
||||
return {"is_admin": True, "message": "admin joined"}
|
||||
|
||||
if user_id not in session.participants:
|
||||
session.participants.append(user_id)
|
||||
self.repository.update(session.id, {"participants": session.participants})
|
||||
response = {
|
||||
"user_id": user.id,
|
||||
"username": user.id,
|
||||
"is_admin": False,
|
||||
"user_id": str(user.id),
|
||||
"username": user.name,
|
||||
"user_pic": user.pic_url,
|
||||
"session_id": session.id,
|
||||
"session_id": str(session.id),
|
||||
}
|
||||
return response
|
||||
|
||||
def leave_session(self, session_id: str, user_id: str) -> dict:
|
||||
session = self.repository.get_by_id(session_id)
|
||||
|
||||
if session is None:
|
||||
return {"error": "Session not found"}
|
||||
|
||||
if user_id == session.host_id:
|
||||
return {"message": "Host cannot leave the session"}
|
||||
|
||||
if user_id in session.participants:
|
||||
session.participants.remove(user_id)
|
||||
self.repository.update(session.id, {"participants": session.participants})
|
||||
return {"message": "User has left the session"}
|
||||
|
||||
return {"message": "User not in session"}
|
||||
|
||||
def start_session(self, session_id: str) -> bool:
|
||||
now = DatetimeUtil.now_iso()
|
||||
return self.repository.update(session_id, {"started_at": now})
|
||||
|
||||
def end_session(self, session_id: str) -> bool:
|
||||
now = DatetimeUtil.now_iso()
|
||||
return self.repository.update(session_id, {"ended_at": now, "is_active": False})
|
||||
def end_session(self, session_id: str, user_id: str):
|
||||
session = self.repository.find_by_id(session_id)
|
||||
if session and session.host_id == user_id:
|
||||
session.is_active = False
|
||||
self.repository.update(session_id, {"is_active": False})
|
||||
|
||||
def advance_question(self, session_id: str) -> Optional[int]:
|
||||
session = self.repository.find_by_session_id(session_id)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from typing import List, Optional
|
||||
from models.entities import SubjectEntity
|
||||
from schemas.requests import SubjectCreateRequest, SubjectUpdateRequest
|
||||
from schemas.response import GetSubjectResponse
|
||||
from repositories import SubjectRepository
|
||||
from app.models.entities import SubjectEntity
|
||||
from app.schemas.requests import SubjectCreateRequest, SubjectUpdateRequest
|
||||
from app.schemas.response import GetSubjectResponse
|
||||
from app.repositories import SubjectRepository
|
||||
|
||||
|
||||
class SubjectService:
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from flask import current_app
|
||||
from repositories import UserRepository
|
||||
from schemas import RegisterSchema
|
||||
from mapper import UserMapper
|
||||
from exception import AlreadyExistException
|
||||
from app.repositories import UserRepository
|
||||
from app.schemas import RegisterSchema
|
||||
from app.mapper import UserMapper
|
||||
from app.exception import AlreadyExistException
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# pytest.ini
|
||||
[pytest]
|
||||
pythonpath = .
|
|
@ -0,0 +1,8 @@
|
|||
# run.py
|
||||
from app.main import createApp, socketio
|
||||
|
||||
app = createApp()
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# # Untuk dev/testing
|
||||
# socketio.run(app, host="0.0.0.0", port=5000, debug=True)
|
Binary file not shown.
|
@ -0,0 +1,111 @@
|
|||
import unittest
|
||||
from unittest.mock import MagicMock
|
||||
from app.services import QuizService
|
||||
from app.app.schemas.requests import QuizCreateSchema
|
||||
from app.app.schemas.response import UserQuizListResponse
|
||||
from app.exception import DataNotFoundException, ValidationException
|
||||
|
||||
|
||||
class TestQuizService(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.quiz_repo = MagicMock()
|
||||
self.user_repo = MagicMock()
|
||||
self.subject_repo = MagicMock()
|
||||
|
||||
self.service = QuizService(
|
||||
quiz_repository=self.quiz_repo,
|
||||
user_repository=self.user_repo,
|
||||
subject_repository=self.subject_repo,
|
||||
)
|
||||
|
||||
def test_get_quiz_success(self):
|
||||
fake_quiz = MagicMock(subject_id="subj1")
|
||||
fake_subject = MagicMock()
|
||||
self.quiz_repo.get_by_id.return_value = fake_quiz
|
||||
self.subject_repo.get_by_id.return_value = fake_subject
|
||||
|
||||
result = self.service.get_quiz("quiz123")
|
||||
self.assertIsNotNone(result)
|
||||
self.quiz_repo.get_by_id.assert_called_once()
|
||||
|
||||
def test_get_quiz_not_found(self):
|
||||
self.quiz_repo.get_by_id.return_value = None
|
||||
with self.assertRaises(DataNotFoundException):
|
||||
self.service.get_quiz("invalid_id")
|
||||
|
||||
def test_search_quiz_success(self):
|
||||
fake_quiz = MagicMock(author_id="user123")
|
||||
self.quiz_repo.search_by_title_or_category.return_value = [fake_quiz]
|
||||
self.quiz_repo.count_by_search.return_value = 1
|
||||
self.user_repo.get_user_by_id.return_value = MagicMock()
|
||||
|
||||
result, total = self.service.search_quiz("math", "subj1")
|
||||
self.assertEqual(total, 1)
|
||||
self.assertTrue(len(result) > 0)
|
||||
|
||||
def test_search_quiz_author_missing(self):
|
||||
fake_quiz = MagicMock(author_id="user123")
|
||||
self.quiz_repo.search_by_title_or_category.return_value = [fake_quiz]
|
||||
self.quiz_repo.count_by_search.return_value = 1
|
||||
self.user_repo.get_user_by_id.return_value = None # simulate missing author
|
||||
|
||||
result, total = self.service.search_quiz("math", "subj1")
|
||||
self.assertEqual(result, []) # filtered out
|
||||
self.assertEqual(total, 1)
|
||||
|
||||
def test_create_quiz_with_invalid_options(self):
|
||||
question = MagicMock(type="option", options=["a", "b"]) # only 2 options
|
||||
quiz_schema = MagicMock(question_listings=[question])
|
||||
|
||||
with self.assertRaises(ValidationException):
|
||||
self.service.create_quiz(quiz_schema)
|
||||
|
||||
def test_create_quiz_valid(self):
|
||||
question = MagicMock(type="option", options=["a", "b", "c", "d"], duration=30)
|
||||
quiz_schema = MagicMock(question_listings=[question])
|
||||
self.quiz_repo.create.return_value = "quiz_id_123"
|
||||
|
||||
result = self.service.create_quiz(quiz_schema)
|
||||
self.assertEqual(result, "quiz_id_123")
|
||||
|
||||
def test_get_user_quiz_success(self):
|
||||
self.quiz_repo.get_by_user_id.return_value = [MagicMock()]
|
||||
self.quiz_repo.count_by_user_id.return_value = 1
|
||||
self.user_repo.get_user_by_id.return_value = MagicMock()
|
||||
|
||||
result = self.service.get_user_quiz("user123")
|
||||
self.assertIsInstance(result, UserQuizListResponse)
|
||||
self.assertEqual(result.total, 1)
|
||||
|
||||
def test_get_user_quiz_empty(self):
|
||||
self.quiz_repo.get_by_user_id.return_value = []
|
||||
result = self.service.get_user_quiz("user123")
|
||||
self.assertEqual(result.total, 0)
|
||||
self.assertEqual(result.quizzes, [])
|
||||
|
||||
def test_get_quiz_recommendation_success(self):
|
||||
quiz = MagicMock(author_id="user123")
|
||||
self.quiz_repo.get_top_played_quizzes.return_value = [quiz]
|
||||
self.user_repo.get_user_by_id.return_value = MagicMock()
|
||||
|
||||
result = self.service.get_quiz_recommendation(1, 10)
|
||||
self.assertTrue(len(result) > 0)
|
||||
|
||||
def test_get_quiz_recommendation_empty(self):
|
||||
self.quiz_repo.get_top_played_quizzes.return_value = []
|
||||
with self.assertRaises(DataNotFoundException):
|
||||
self.service.get_quiz_recommendation(1, 10)
|
||||
|
||||
def test_update_quiz(self):
|
||||
self.quiz_repo.update.return_value = True
|
||||
result = self.service.update_quiz("quiz_id", {"title": "updated"})
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_delete_quiz(self):
|
||||
self.quiz_repo.delete.return_value = True
|
||||
result = self.service.delete_quiz("quiz_id")
|
||||
self.assertTrue(result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
Loading…
Reference in New Issue