Skip to content

Commit

Permalink
Merge pull request #82 from w1tnessbtwwwww/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
w1tnessbtwwwww authored Jan 13, 2025
2 parents 150e783 + e5886d2 commit 85af529
Show file tree
Hide file tree
Showing 13 changed files with 131 additions and 34 deletions.
15 changes: 14 additions & 1 deletion SpeedSolverBackend/SpeedSolverAPI/app/database/database.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from contextlib import asynccontextmanager
from sqlalchemy.ext.asyncio import (
AsyncSession,
create_async_engine,
Expand All @@ -8,14 +9,26 @@


from app.cfg.settings import settings
from app.utils.logger.telegram_bot.telegram_logger import logger
from app.database.models.models import Base

async def get_engine() -> AsyncEngine:
return create_async_engine(str(settings.db_url))



async def get_session():
async_session = sessionmaker(await get_engine(), class_=AsyncSession, expire_on_commit=False)
async with async_session() as session:
try:
yield session
finally:
await session.close()
await session.close()

async def create_tables():
try:
engine = await get_engine()
async with engine.begin() as eng:
await eng.run_sync(Base.metadata.create_all)
except Exception as e:
logger.fatal("Не удалось создать таблицы. ", str(e))
13 changes: 13 additions & 0 deletions SpeedSolverBackend/SpeedSolverAPI/app/database/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,18 @@ class Project(Base):
projectId: Mapped[UUID] = mapped_column(UUID, primary_key=True, default=uuid.uuid4)
title: Mapped[str] = mapped_column()
description: Mapped[str] = mapped_column(nullable=True)
created_at: Mapped[datetime.datetime] = mapped_column(default=datetime.datetime.now())

objectives: Mapped[List["Objective"]] = relationship("Objective", back_populates="project")

class TeamModerator(Base):
__tablename__ = "team_moderators"
teamModeratorId: Mapped[UUID] = mapped_column(UUID, primary_key=True, default=uuid.uuid4)
userId: Mapped[UUID] = mapped_column(ForeignKey("users.userId"))
teamId: Mapped[UUID] = mapped_column(ForeignKey("teams.teamId"))

team: Mapped["Team"] = relationship("Team", back_populates="moderators") # type: ignore
user: Mapped["User"] = relationship("User", back_populates="teams_moderation") # type: ignore

class TeamMember(Base):
__tablename__ = "team_members"
Expand Down Expand Up @@ -76,6 +85,8 @@ class Team(Base):
members: Mapped[list["TeamMember"]] = relationship("TeamMember", back_populates="team") # type: ignore
projects: Mapped[list["TeamProject"]] = relationship("TeamProject", back_populates="team") # type: ignore

moderators: Mapped[list["TeamModerator"]] = relationship("TeamModerator", back_populates="team") # type: ignore

class UserProfile(Base):
__tablename__ = "user_profiles"
userProfileId: Mapped[UUID] = mapped_column(UUID, primary_key=True, default=uuid.uuid4)
Expand Down Expand Up @@ -118,6 +129,8 @@ class User(Base):

verification: Mapped["EmailVerification"] = relationship("EmailVerification", back_populates="user")

teams_moderation: Mapped["TeamModerator"] = relationship("TeamModerator", back_populates="user")

class TeamInvitation(Base):
__tablename__ = "team_invitations"
teamInvitationId: Mapped[UUID] = mapped_column(UUID, primary_key=True, default=uuid.uuid4)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,27 @@ class ProjectRepository(AbstractRepository):


async def create_project(self, binded_teamId: str, title: str, description: str) -> Result[Project]:

team_project_repository = TeamProjectRepository(self._session)

team_project_repo = TeamProjectRepository(self._session)
team_projects = await team_project_repo.get_by_filter_all(teamId=binded_teamId)

project_query = (
select(self.model)
.where(
and_(
self.model.title == title,
self.model.description == description
)
self.model.projectId.in_([team_project.projectId for team_project in team_projects])
)
)

project_result = await self._session.execute(project_query)
projects = project_result.scalars().all()
project = await self._session.execute(project_query)
project = project.scalars().one_or_none()

if project:
return err("Проект с таким названием уже существует.")

team_projects_query = (
select(team_project_repository.model)
.where(
team_project_repository.model.teamId == binded_teamId
)
)
project = await self.create(title=title, description=description)

created = await team_project_repo.create(teamId=binded_teamId, projectId=project.projectId)
return success(created)

team_projects_result = await self._session.execute(team_projects_query)
team_projects = team_projects_result.scalars().all()



Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from app.database.abstract.abc_repo import AbstractRepository
from app.database.models.models import TeamModerator

class TeamModerationRepository(AbstractRepository):

model = TeamModerator

Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,25 @@
from app.utils.result import Result, err, success
from app.utils.logger.telegram_bot.telegram_logger import logger

from app.database.models.models import User
from app.database.models.models import User, TeamModerator
from app.database.abstract.abc_repo import AbstractRepository

from sqlalchemy import delete, select, update, insert

class UserRepository(AbstractRepository):
model = User

async def get_moderation_teams(self, userId: str):
query = (
select(self.model)
.where(self.model.userId == userId)
.join(TeamModerator, TeamModerator.userId == self.model.userId)
)

result = await self._session.execute(query)
user = result.mappings().all()
return user

async def create(self, **kwargs):
query = insert(self.model).values(**kwargs).returning(self.model)
result = await self._session.execute(query)
Expand Down
3 changes: 2 additions & 1 deletion SpeedSolverBackend/SpeedSolverAPI/app/routing/main_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
from app.routing.email_router import email_router
from app.routing.verification_router import verification_router
from app.routing.project_router import project_router

from app.routing.test_router import test_router
main_router = APIRouter (
prefix = "/v1"
)

main_router.include_router(test_router)
main_router.include_router(auth_router)
main_router.include_router(verification_router)
main_router.include_router(email_router)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@

project_router = APIRouter(
prefix="/projects",
tags=["Projects Management"]
)

@project_router.post("/create")
async def create_project(create_project: CreateProject, user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session)):
creating = await ProjectService(session).create_project(create_project.for_team, create_project.title, create_project.description)
creating = await ProjectService(session).create_project(user.userId, create_project.for_team, create_project.title, create_project.description)
if not creating.success:
raise HTTPException(status_code=400, detail=creating.error)

return creating.value
13 changes: 13 additions & 0 deletions SpeedSolverBackend/SpeedSolverAPI/app/routing/test_router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from fastapi import APIRouter, Depends

from sqlalchemy.ext.asyncio import AsyncSession

from app.database.database import get_session
from app.database.models.models import User
from app.security.jwtmanager import get_current_user
from app.services.project_service import ProjectService
from app.services.user_service import UserService

test_router = APIRouter(
prefix="/test"
)
13 changes: 10 additions & 3 deletions SpeedSolverBackend/SpeedSolverAPI/app/services/project_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,15 @@ def __init__(self, session: AsyncSession):
self._repo: ProjectRepository = ProjectRepository(session)


async def create_project(self, team_id: str, title: str, description: str) -> Result[Project]:
team = await TeamService(self._session).is_team_exists(team_id)
async def create_project(self, user_sender: str, team_id: str, title: str, description: str) -> Result[Project]:
team = await TeamService(self._session).is_team_exists(team_id=team_id)
if not team:
return err("Команда не найдена.")

is_user_moderator = await TeamService(self._session).is_user_moderator(user_sender, team_id=team_id)

return err("Такой команды не найдено.") if not team else success("Команда есть")
if not is_user_moderator:
return err("Вы не являетесь модератором данной команды.")

return await self._repo.create_project(binded_teamId=team_id, title=title, description=description)

21 changes: 19 additions & 2 deletions SpeedSolverBackend/SpeedSolverAPI/app/services/team_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,40 @@
from typing import Optional
from sqlalchemy.ext.asyncio import AsyncSession

from app.database.models.models import User
from app.database.models.models import Team, User
from app.database.repo.team_moderation_repository import TeamModerationRepository
from app.database.repo.team_repository import TeamRepository

from app.security.jwtmanager import JWTManager

from app.schema.request.team.create_team import CreateTeam
from app.schema.request.team.update_team import UpdateTeam
from app.services.user_service import UserService

class TeamService:
def __init__(self, session: AsyncSession):
self._session = session
self._repo = TeamRepository(session)


async def is_user_moderator(self, user_id: str, team_id: str):
team_moderation_repo = TeamModerationRepository(self._session)
team: Team = await self._repo.get_by_filter_one(teamId=team_id)
if not team:
return False

if team.leaderId == user_id:
return True


team_mod = await team_moderation_repo.get_by_filter_one(userId=user_id, teamId=team_id)
return True if team_mod else False

async def is_team_exists(self, team_id: str) -> bool:

team = await self._repo.get_by_filter_one(teamId=team_id)

return False if team else False
return True if team else False

async def delete_team(self, team_id: str, leaderId: str):
return await self._repo.delete_team(teamId=team_id, leaderId=leaderId)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

from typing import List, Sequence
from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession

Expand Down Expand Up @@ -27,6 +28,11 @@ def __init__(self, session: AsyncSession):
self._session = session
self._repo: UserRepository = UserRepository(session)



async def get_moderation_teams(self, userId: str):
return await self._repo.get_moderation_teams(userId)

async def delete_account(self, userId: str):
return await self._repo.delete_by_id(userId)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def send_log(self, template: str) -> Result[None]:
def info(self, message: str):
template = f"❗**INFO**❗\n{message}\nDate: {datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S")}"

sending = self.send_log(template)
self.send_log(template)


def error(self, message: str, traceback: Optional[str] = None) -> str:
Expand All @@ -36,16 +36,20 @@ def error(self, message: str, traceback: Optional[str] = None) -> str:
else:
template = f"🚨 **ERROR** 🚨\n{message}\nDate: {datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S")}"

sending = self.send_log(template)
self.send_log(template)

def warning(self, message: str) -> str:
template = f"⚠️ **WARNING** ⚠️\n{message}\nDate: {datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S")}"

sending = self.send_log(template)
self.send_log(template)

def fatal(self, message: str) -> str:
template = f"🆘 **FATAL** 🆘\n{message}\nDate: {datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S")}"
def fatal(self, message: str, traceback: str = None) -> str:

sending = self.send_log(template)
if traceback:
template = f"🆘 **FATAL** 🆘\n{message}\n```python\n{traceback}```\nDate: {datetime.datetime.now().strftime('%d/%m/%Y %H:%M:%S')}"
else:
template = f"🆘 **FATAL** 🆘\n{message}\nDate: {datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S")}"

self.send_log(template)

logger: TelegramLogger = TelegramLogger()
10 changes: 8 additions & 2 deletions SpeedSolverBackend/SpeedSolverAPI/main.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
from contextlib import asynccontextmanager
from fastapi import Depends, FastAPI, HTTPException
from fastapi.responses import JSONResponse
from pydantic import BaseModel

from app.cfg.settings import settings
from app.database.database import get_session
from app.database.database import get_session, create_tables
from app.exc.bad_email import BadEmail
from app.routing.main_router import main_router
from app.services.user_service import UserService
from app.utils.email_service.email_service import EmailService

from starlette.middleware.cors import CORSMiddleware

from typing import Annotated
from sqlalchemy.orm import Session
from sqlalchemy.ext.asyncio import AsyncSession
from alembic.config import Config
from alembic import command

Expand Down Expand Up @@ -45,3 +47,7 @@ async def bad_email(request, exc: BadEmail):
detail=exc.message
)


@api.on_event("startup")
async def startup_event():
await create_tables()

0 comments on commit 85af529

Please sign in to comment.