Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bck 67/knowledge base #107

Merged
merged 6 commits into from
Aug 20, 2024
42 changes: 42 additions & 0 deletions alembic/versions/5a2d8eed5666_add_model_question.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""add model Question

Revision ID: 5a2d8eed5666
Revises: 53fadd799e96
Create Date: 2024-08-13 20:36:59.932459

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '5a2d8eed5666'
down_revision = '53fadd799e96'
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('question',
sa.Column('problem', sa.String(length=100), nullable=False),
sa.Column('solution', sa.Text(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('problem')
)
with op.batch_alter_table('auth', schema=None) as batch_op:
batch_op.add_column(sa.Column('register_', sa.String(), nullable=False))
batch_op.drop_column('register')

# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('auth', schema=None) as batch_op:
batch_op.add_column(sa.Column('register', sa.VARCHAR(), nullable=False))
batch_op.drop_column('register_')

op.drop_table('question')
# ### end Alembic commands ###
1 change: 1 addition & 0 deletions app/admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
SubscriptionAdmin, TasksAdmin,
)
from app.admin.profile import ProfileAdmin # noqa
from app.admin.question import QuestionAdmin # noqa
from app.admin.tariff import TariffAdmin # noqa
from app.admin.task import TaskAdmin # noqa
from app.admin.user import UserAdmin # noqa
8 changes: 6 additions & 2 deletions app/admin/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
AchievementAdmin, AchievementsAdmin, AuthAdmin, CommonAdmin, ContactsAdmin,
CourseAdmin, ErrorsAdmin, ExaminationAdmin, GroupAdmin, HeaderAdmin,
HelpAdmin, LocaleAdmin, MainAdmin, MonthsAdmin, ProfileAdmin,
ProfileUserAdmin, QuestionBannerAdmin, RestoreAdmin, SecureAdmin,
SubscriptionAdmin, TariffAdmin, TaskAdmin, TasksAdmin, UserAdmin,
ProfileUserAdmin, QuestionAdmin, QuestionBannerAdmin, RestoreAdmin,
SecureAdmin, SubscriptionAdmin, TariffAdmin, TaskAdmin, TasksAdmin,
UserAdmin,
)

admin_models = [
Expand All @@ -21,6 +22,9 @@
HelpAdmin, MainAdmin, RestoreAdmin, SubscriptionAdmin,
ProfileUserAdmin, SecureAdmin, AchievementsAdmin, TasksAdmin,
QuestionBannerAdmin, ErrorsAdmin, MonthsAdmin,

# category = 'HELP'
QuestionAdmin,
]


Expand Down
17 changes: 17 additions & 0 deletions app/admin/question.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from sqladmin import ModelView

from app.models import Question


class QuestionAdmin(ModelView, model=Question):
column_list = [Question.id, Question.problem]
column_searchable_list = [Question.problem]
can_create = True
can_edit = True
can_delete = True
can_view_details = True
name = 'Question'
name_plural = 'Questions'
icon = 'fa-solid fa-question'
category = 'HELP'
column_formatters = {Question.problem: lambda m, a: m.problem[:50]}
1 change: 1 addition & 0 deletions app/api/endpoints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from .locale import router as locale_router # noqa
from .notification import router as notification_router # noqa
from .profile import router as profile_router # noqa
from .question import router as question_router # noqa
from .tariff import router as tariff_router # noqa
from .task import router as task_router # noqa
from .user import router as user_router # noqa
50 changes: 50 additions & 0 deletions app/api/endpoints/question.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from fastapi import APIRouter, Depends, Path
from sqlalchemy.ext.asyncio import AsyncSession
from typing_extensions import Annotated

from app.api.validators import check_obj_exists
from app.api_docs_responses.question import GET_QUESTION, GET_QUESTIONS
from app.core.db import get_async_session
from app.core.user import current_user
from app.crud import question_crud
from app.schemas.question import QuestionRead

router = APIRouter()


@router.get(
'/',
response_model=list[QuestionRead],
dependencies=[Depends(current_user)],
**GET_QUESTIONS,
)
async def get_all_questions(
session: AsyncSession = Depends(get_async_session),
) -> list[QuestionRead]:
"""
Получение всех часто задаваемых вопросов, которые есть в БД.
"""
return await question_crud.get_multi(session)


@router.get(
'/{question_id}',
response_model=QuestionRead,
dependencies=[Depends(current_user)],
**GET_QUESTION,
)
async def get_question(
question_id: Annotated[int, Path(ge=0)],
session: AsyncSession = Depends(get_async_session)
):
"""
Получение вопроса по его id или
получение ошибки 404 в случае отсутствия данного вопроса.
"""
obj = await question_crud.get_by_attr(
attr_name='id',
attr_value=question_id,
session=session,
)
await check_obj_exists(obj=obj)
return obj
7 changes: 5 additions & 2 deletions app/api/routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

from app.api.endpoints import (
achievement_router, course_router, examination_router, group_router,
locale_router, notification_router, profile_router, tariff_router,
task_router, user_router,
locale_router, notification_router, profile_router, question_router,
tariff_router, task_router, user_router,
)

main_router = APIRouter()
Expand All @@ -22,6 +22,9 @@
main_router.include_router(
profile_router, prefix='/profiles', tags=['Profiles']
)
main_router.include_router(
question_router, prefix='/questions', tags=['Questions']
)
main_router.include_router(
locale_router, prefix='/locales', tags=['Locales']
)
Expand Down
87 changes: 87 additions & 0 deletions app/api_docs_responses/question.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from app.api_docs_responses.utils_docs import (
get_200_docs, get_401_docs, get_404_docs,
)

questions_with_all_fields = {
'id': 0,
'problem': 'Описание проблемы',
'solution': 'Решение проблемы',
}

questions_without_sulution = {
'id': 0,
'problem': 'Описание проблемы',
}

content_questions = {
'application/json': {
'examples': {
'questions_with_all_fields': {
'summary': 'Вопрос со всеми заполненными полями',
'value': [questions_with_all_fields],
},
'questions_without_sulution': {
'summary': 'Вопрос без решения',
'value': [questions_without_sulution],
}
}
}
}

content_question = {
'application/json': {
'examples': {
'question_with_all_fields': {
'summary': 'Вопрос со всеми заполненными полями',
'value': questions_with_all_fields,
},
'question_without_sulution': {
'summary': 'Вопрос без решения',
'value': questions_without_sulution,
}
}
}
}

get_questions_response = {
**get_200_docs(content_questions),
**get_401_docs(content_questions),
}

get_question_response = {
**get_200_docs(content_question),
**get_401_docs(content_questions),
**get_404_docs('Объект question не найден.'),
}


GET_QUESTIONS = dict(
responses=get_questions_response,
summary='Получение всего списка часто задаваемых вопросов',
description="""
## Получение всех часто задаваемых вопросов.

Permissions:
- Для всех пользователей.

Returns:
- HTTP 200 OK: Если список часто задаваемых вопросов успешно получен.
- HTTP 401 Unauthorized: Если пользователь не авторизован.
"""
)

GET_QUESTION = dict(
responses=get_question_response,
summary='Получение часто задаваемого вопроса по ID',
description="""
## Получение информации о вопросе по его идентификатору.

Permissions:
- Для всех пользователей.

Returns:
- HTTP 200 OK: Если вопрос успешно получен.
- HTTP 401 Unauthorized: Если пользователь не авторизован.
- HTTP 404 Not Found: Если вопрос с указанным `question_id` не существует.
"""
)
1 change: 0 additions & 1 deletion app/api_docs_responses/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from app.api_docs_responses.utils_docs import get_201_docs


REGISTER_SUMMARY = 'Регистрация нового пользователя.'

CREATE_REGISTER = {
Expand Down
1 change: 1 addition & 0 deletions app/crud/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from .locale import locale_crud # noqa
from .notification import notification_crud # noqa
from .profile import profile_crud # noqa
from .question import question_crud # noqa
from .tariff import tariff_crud # noqa
from .task import task_crud # noqa
from .user import user_crud # noqa
9 changes: 9 additions & 0 deletions app/crud/question.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from app.crud.base import CRUDBase
from app.models import Question


class CRUDQuestion(CRUDBase):
pass


question_crud = CRUDQuestion(Question)
1 change: 1 addition & 0 deletions app/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
)
from .notification import Notification # noqa
from .profile import Profile # noqa
from .question import Question # noqa
from .tariff import Tariff # noqa
from .task import Task # noqa
from .user import User # noqa
14 changes: 14 additions & 0 deletions app/models/question.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from sqlalchemy import Column, String, Text

from app.core.config import settings
from app.core.db import Base


class Question(Base):
problem: str = Column(
String(length=settings.max_length_string), unique=True, nullable=False
)
solution: str = Column(Text)

def __repr__(self):
return self.name
12 changes: 12 additions & 0 deletions app/schemas/question.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from typing import Optional

from pydantic import BaseModel


class QuestionRead(BaseModel):
id: int
problem: Optional[str]
solution: Optional[str]

class Config:
from_attributes = True
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
f'{fixtures}.profile',
f'{fixtures}.group',
f'{fixtures}.achievement',
f'{fixtures}.question',
f'{fixtures}.tariff',
f'{fixtures}.task',
f'{fixtures}.course',
Expand Down
20 changes: 20 additions & 0 deletions tests/fixtures/question.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import pytest_asyncio
from sqlalchemy.ext.asyncio import AsyncSession

from app.models import Question

TEST_QUESTION_COUNT = 6


@pytest_asyncio.fixture
async def moc_questions(
db_session: AsyncSession
) -> None:
moc_questions = [
Question(
problem=f'Problem_{i}',
solution=f'Solution of Problem_{i}'
) for i in range(TEST_QUESTION_COUNT)
]
db_session.add_all(moc_questions)
await db_session.commit()
Loading
Loading