diff --git a/351004/Barsukov/.gitignore b/351004/Barsukov/.gitignore new file mode 100644 index 000000000..0cb5582c1 --- /dev/null +++ b/351004/Barsukov/.gitignore @@ -0,0 +1,219 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[codz] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py.cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +# Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +# poetry.lock +# poetry.toml + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python. +# https://pdm-project.org/en/latest/usage/project/#working-with-version-control +# pdm.lock +# pdm.toml +.pdm-python +.pdm-build/ + +# pixi +# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. +# pixi.lock +# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one +# in the .venv directory. It is recommended not to include this directory in version control. +.pixi + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# Redis +*.rdb +*.aof +*.pid + +# RabbitMQ +mnesia/ +rabbitmq/ +rabbitmq-data/ + +# ActiveMQ +activemq-data/ + +# SageMath parsed files +*.sage.py + +# Environments +.env +.envrc +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +# .idea/ + +# Abstra +# Abstra is an AI-powered process automation framework. +# Ignore directories containing user credentials, local state, and settings. +# Learn more at https://abstra.io/docs +.abstra/ + +# Visual Studio Code +# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore +# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +# and can be added to the global gitignore or merged into this file. However, if you prefer, +# you could uncomment the following to ignore the entire vscode folder +# .vscode/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc + +# Marimo +marimo/_static/ +marimo/_lsp/ +__marimo__/ + +# Streamlit +.streamlit/secrets.toml + +# Pytest +pytest.ini \ No newline at end of file diff --git a/351004/Barsukov/app/__init__.py b/351004/Barsukov/app/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/351004/Barsukov/app/api/v1/endpoints/authors.py b/351004/Barsukov/app/api/v1/endpoints/authors.py new file mode 100644 index 000000000..ab7329656 --- /dev/null +++ b/351004/Barsukov/app/api/v1/endpoints/authors.py @@ -0,0 +1,22 @@ +from fastapi import APIRouter, status, Depends, HTTPException +from sqlalchemy.orm import Session +from database import get_db +from app.schemas.author import AuthorRequestTo, AuthorResponseTo +from app.services.author_service import AuthorService +from typing import List + +router = APIRouter() +service = AuthorService() + +@router.post("", response_model=AuthorResponseTo, status_code=status.HTTP_201_CREATED) +async def create(dto: AuthorRequestTo, db: Session = Depends(get_db)): + return service.create(db, dto) + +@router.get("", response_model=List[AuthorResponseTo]) +async def get_all(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)): + return service.get_all(db, skip, limit) + +@router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT) +async def delete(id: int, db: Session = Depends(get_db)): + if not service.delete(db, id): + raise HTTPException(status_code=404) \ No newline at end of file diff --git a/351004/Barsukov/app/api/v1/endpoints/issues.py b/351004/Barsukov/app/api/v1/endpoints/issues.py new file mode 100644 index 000000000..21998f5b1 --- /dev/null +++ b/351004/Barsukov/app/api/v1/endpoints/issues.py @@ -0,0 +1,51 @@ +from fastapi import APIRouter, status, Body, Depends, HTTPException +from sqlalchemy.orm import Session +from database import get_db +from app.schemas.issue import IssueRequestTo, IssueResponseTo +from app.services.issue_service import IssueService +from typing import List + +router = APIRouter() +service = IssueService() + +@router.post("", response_model=IssueResponseTo, status_code=status.HTTP_201_CREATED) +async def create(dto: IssueRequestTo = Body(...), db: Session = Depends(get_db)): + res = service.create(db, dto) + # Мапим author_id -> authorId для схемы + return IssueResponseTo( + id=res.id, authorId=res.author_id, title=res.title, + content=res.content, created=str(res.created), modified=str(res.modified) + ) + +@router.get("", response_model=List[IssueResponseTo]) +async def get_all(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)): + items = service.get_all(db, skip, limit) + return [ + IssueResponseTo( + id=i.id, authorId=i.author_id, title=i.title, + content=i.content, created=str(i.created), modified=str(i.modified) + ) for i in items + ] + +@router.get("/{id}", response_model=IssueResponseTo) +async def get_by_id(id: int, db: Session = Depends(get_db)): + i = service.get_by_id(db, id) + if not i: raise HTTPException(404, "Issue not found") + return IssueResponseTo( + id=i.id, authorId=i.author_id, title=i.title, + content=i.content, created=str(i.created), modified=str(i.modified) + ) + +@router.put("/{id}", response_model=IssueResponseTo) +async def update(id: int, dto: IssueRequestTo = Body(...), db: Session = Depends(get_db)): + res = service.update(db, id, dto) + if not res: raise HTTPException(404, "Issue not found") + return IssueResponseTo( + id=res.id, authorId=res.author_id, title=res.title, + content=res.content, created=str(res.created), modified=str(res.modified) + ) + +@router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT) +async def delete(id: int, db: Session = Depends(get_db)): + if not service.delete(db, id): + raise HTTPException(404, "Issue not found") \ No newline at end of file diff --git a/351004/Barsukov/app/api/v1/endpoints/notes.py b/351004/Barsukov/app/api/v1/endpoints/notes.py new file mode 100644 index 000000000..75cfaa197 --- /dev/null +++ b/351004/Barsukov/app/api/v1/endpoints/notes.py @@ -0,0 +1,36 @@ +from fastapi import APIRouter, status, Body, Depends, HTTPException +from sqlalchemy.orm import Session +from database import get_db +from app.schemas.note import NoteRequestTo, NoteResponseTo +from app.services.note_service import NoteService +from typing import List + +router = APIRouter() +service = NoteService() + +@router.post("", response_model=NoteResponseTo, status_code=status.HTTP_201_CREATED) +async def create(dto: NoteRequestTo = Body(...), db: Session = Depends(get_db)): + res = service.create(db, dto) + return NoteResponseTo(id=res.id, issueId=res.issue_id, content=res.content) + +@router.get("", response_model=List[NoteResponseTo]) +async def get_all(db: Session = Depends(get_db)): + items = service.get_all(db) + return [NoteResponseTo(id=i.id, issueId=i.issue_id, content=i.content) for i in items] + +@router.get("/{id}", response_model=NoteResponseTo) +async def get_by_id(id: int, db: Session = Depends(get_db)): + res = service.get_by_id(db, id) + if not res: raise HTTPException(404, "Note not found") + return NoteResponseTo(id=res.id, issueId=res.issue_id, content=res.content) + +@router.put("/{id}", response_model=NoteResponseTo) +async def update(id: int, dto: NoteRequestTo = Body(...), db: Session = Depends(get_db)): + res = service.update(db, id, dto) + if not res: raise HTTPException(404, "Note not found") + return NoteResponseTo(id=res.id, issueId=res.issue_id, content=res.content) + +@router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT) +async def delete(id: int, db: Session = Depends(get_db)): + if not service.delete(db, id): + raise HTTPException(404, "Note not found") \ No newline at end of file diff --git a/351004/Barsukov/app/api/v1/endpoints/stickers.py b/351004/Barsukov/app/api/v1/endpoints/stickers.py new file mode 100644 index 000000000..4a098d7d7 --- /dev/null +++ b/351004/Barsukov/app/api/v1/endpoints/stickers.py @@ -0,0 +1,34 @@ +from fastapi import APIRouter, status, Body, Depends, HTTPException +from sqlalchemy.orm import Session +from database import get_db +from app.schemas.sticker import StickerRequestTo, StickerResponseTo +from app.services.sticker_service import StickerService +from typing import List + +router = APIRouter() +service = StickerService() + +@router.post("", response_model=StickerResponseTo, status_code=status.HTTP_201_CREATED) +async def create(dto: StickerRequestTo = Body(...), db: Session = Depends(get_db)): + return service.create(db, dto) + +@router.get("", response_model=List[StickerResponseTo]) +async def get_all(db: Session = Depends(get_db)): + return service.get_all(db) + +@router.get("/{id}", response_model=StickerResponseTo) +async def get_by_id(id: int, db: Session = Depends(get_db)): + res = service.get_by_id(db, id) + if not res: raise HTTPException(404, "Sticker not found") + return res + +@router.put("/{id}", response_model=StickerResponseTo) +async def update(id: int, dto: StickerRequestTo = Body(...), db: Session = Depends(get_db)): + res = service.update(db, id, dto) + if not res: raise HTTPException(404, "Sticker not found") + return res + +@router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT) +async def delete(id: int, db: Session = Depends(get_db)): + if not service.delete(db, id): + raise HTTPException(404, "Sticker not found") \ No newline at end of file diff --git a/351004/Barsukov/app/api/v1/router.py b/351004/Barsukov/app/api/v1/router.py new file mode 100644 index 000000000..77dab1e94 --- /dev/null +++ b/351004/Barsukov/app/api/v1/router.py @@ -0,0 +1,10 @@ +from fastapi import APIRouter + +from app.api.v1.endpoints import authors, issues, stickers, notes + +api_router = APIRouter() + +api_router.include_router(authors.router, prefix="/authors", tags=["Authors"]) +api_router.include_router(issues.router, prefix="/issues", tags=["Issues"]) +api_router.include_router(stickers.router, prefix="/stickers", tags=["Stickers"]) +api_router.include_router(notes.router, prefix="/notes", tags=["Notes"]) diff --git a/351004/Barsukov/app/core/exceptions.py b/351004/Barsukov/app/core/exceptions.py new file mode 100644 index 000000000..65e61f49d --- /dev/null +++ b/351004/Barsukov/app/core/exceptions.py @@ -0,0 +1,21 @@ +from fastapi import Request +from fastapi.responses import JSONResponse +from fastapi.exceptions import RequestValidationError + +class AppException(Exception): + def __init__(self, status_code: int, message: str, sub_code: int): + self.status_code = status_code + self.message = message + self.error_code = f"{status_code}{sub_code:02d}" + +async def app_exception_handler(request: Request, exc: AppException): + return JSONResponse( + status_code=exc.status_code, + content={"errorMessage": exc.message, "errorCode": exc.error_code} + ) + +async def validation_exception_handler(request: Request, exc: RequestValidationError): + return JSONResponse( + status_code=400, + content={"errorMessage": "Validation failed", "errorCode": "40000"} + ) \ No newline at end of file diff --git a/351004/Barsukov/app/database.py b/351004/Barsukov/app/database.py new file mode 100644 index 000000000..89a5a08ee --- /dev/null +++ b/351004/Barsukov/app/database.py @@ -0,0 +1,16 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + +SQLALCHEMY_DATABASE_URL = "postgresql://postgres:1234@localhost:5432/distcomp?options=-csearch_path%3Ddistcomp" + +engine = create_engine(SQLALCHEMY_DATABASE_URL) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) +Base = declarative_base() + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() \ No newline at end of file diff --git a/351004/Barsukov/app/models.py b/351004/Barsukov/app/models.py new file mode 100644 index 000000000..3ccc35fad --- /dev/null +++ b/351004/Barsukov/app/models.py @@ -0,0 +1,53 @@ +from sqlalchemy import Column, Integer, String, ForeignKey, Table, Text, DateTime +from sqlalchemy.orm import relationship +from sqlalchemy.sql import func +from database import Base + +# Таблица связи Многие-ко-Многим (Issue <-> Sticker) +# Название: tbl_issue_sticker +issue_sticker = Table( + 'tbl_issue_sticker', Base.metadata, + Column('issue_id', Integer, ForeignKey('tbl_issue.id', ondelete="CASCADE"), primary_key=True), + Column('sticker_id', Integer, ForeignKey('tbl_sticker.id', ondelete="CASCADE"), primary_key=True) +) + + +class Author(Base): + __tablename__ = "tbl_author" + id = Column(Integer, primary_key=True, index=True) + login = Column(String(64), unique=True, nullable=False) + password = Column(String(128), nullable=False) + firstname = Column(String(64), nullable=False) + lastname = Column(String(64), nullable=False) + + issues = relationship("Issue", back_populates="author") + + +class Issue(Base): + __tablename__ = "tbl_issue" + id = Column(Integer, primary_key=True, index=True) + author_id = Column(Integer, ForeignKey("tbl_author.id", ondelete="CASCADE"), nullable=False) + title = Column(String(64), nullable=False) + content = Column(String(2048), nullable=False) + created = Column(DateTime, server_default=func.now()) + modified = Column(DateTime, server_default=func.now(), onupdate=func.now()) + + author = relationship("Author", back_populates="issues") + notes = relationship("Note", back_populates="issue") + # Связь многие-ко-многим через таблицу tbl_issue_sticker + stickers = relationship("Sticker", secondary=issue_sticker) + + +class Sticker(Base): + __tablename__ = "tbl_sticker" + id = Column(Integer, primary_key=True, index=True) + name = Column(String(32), unique=True, nullable=False) + + +class Note(Base): + __tablename__ = "tbl_note" + id = Column(Integer, primary_key=True, index=True) + issue_id = Column(Integer, ForeignKey("tbl_issue.id", ondelete="CASCADE"), nullable=False) + content = Column(String(2048), nullable=False) + + issue = relationship("Issue", back_populates="notes") \ No newline at end of file diff --git a/351004/Barsukov/app/repository.py b/351004/Barsukov/app/repository.py new file mode 100644 index 000000000..ddca7098e --- /dev/null +++ b/351004/Barsukov/app/repository.py @@ -0,0 +1,49 @@ +from sqlalchemy.orm import Session +import models + +class BaseRepository: + def __init__(self, model): + self.model = model + + def get_all(self, db: Session, skip: int = 0, limit: int = 10, sort_by: str = "id", **filters): + query = db.query(self.model) + for attr, value in filters.items(): + if value is not None and hasattr(self.model, attr): + column = getattr(self.model, attr) + query = query.filter(column.ilike(f"%{value}%") if isinstance(value, str) else column == value) + return query.order_by(getattr(self.model, sort_by)).offset(skip).limit(limit).all() + + def get_by_id(self, db: Session, obj_id: int): + return db.query(self.model).filter(self.model.id == obj_id).first() + + def create(self, db: Session, data: dict): + obj = self.model(**data) + db.add(obj) + db.commit() + db.refresh(obj) + return obj + + def update(self, db: Session, obj_id: int, data: dict): + obj = self.get_by_id(db, obj_id) + if obj: + for key, val in data.items(): setattr(obj, key, val) + db.commit() + db.refresh(obj) + return obj + + def delete(self, db: Session, obj_id: int): + obj = self.get_by_id(db, obj_id) + if obj: + db.delete(obj) + db.commit() + return True + return False + +class AuthorRepo(BaseRepository): + def __init__(self): super().__init__(models.Author) +class IssueRepo(BaseRepository): + def __init__(self): super().__init__(models.Issue) +class NoteRepo(BaseRepository): + def __init__(self): super().__init__(models.Note) +class StickerRepo(BaseRepository): + def __init__(self): super().__init__(models.Sticker) \ No newline at end of file diff --git a/351004/Barsukov/app/schemas/__init__.py b/351004/Barsukov/app/schemas/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/351004/Barsukov/app/schemas/author.py b/351004/Barsukov/app/schemas/author.py new file mode 100644 index 000000000..157dd9f10 --- /dev/null +++ b/351004/Barsukov/app/schemas/author.py @@ -0,0 +1,15 @@ +from pydantic import BaseModel, Field +from typing import Optional + +class AuthorRequestTo(BaseModel): + id: Optional[int] = None + login: str = Field(..., min_length=2, max_length=64) + password: str = Field(..., min_length=8, max_length=128) + firstname: str = Field(..., min_length=2, max_length=64) + lastname: str = Field(..., min_length=2, max_length=64) + +class AuthorResponseTo(BaseModel): + id: int + login: str + firstname: str + lastname: str \ No newline at end of file diff --git a/351004/Barsukov/app/schemas/issue.py b/351004/Barsukov/app/schemas/issue.py new file mode 100644 index 000000000..9ff6cc311 --- /dev/null +++ b/351004/Barsukov/app/schemas/issue.py @@ -0,0 +1,17 @@ +from pydantic import BaseModel, Field +from typing import List, Optional + +class IssueRequestTo(BaseModel): + id: Optional[int] = None + authorId: int + title: str = Field(..., min_length=2, max_length=64) + content: str = Field(..., min_length=4, max_length=2048) + stickerIds: List[int] = [] + +class IssueResponseTo(BaseModel): + id: int + authorId: int + title: str + content: str + created: str + modified: str diff --git a/351004/Barsukov/app/schemas/note.py b/351004/Barsukov/app/schemas/note.py new file mode 100644 index 000000000..740c7a031 --- /dev/null +++ b/351004/Barsukov/app/schemas/note.py @@ -0,0 +1,12 @@ +from pydantic import BaseModel, Field +from typing import Optional + +class NoteRequestTo(BaseModel): + id: Optional[int] = None + issueId: int + content: str = Field(..., min_length=2, max_length=2048) + +class NoteResponseTo(BaseModel): + id: int + issueId: int + content: str \ No newline at end of file diff --git a/351004/Barsukov/app/schemas/sticker.py b/351004/Barsukov/app/schemas/sticker.py new file mode 100644 index 000000000..dcc989d24 --- /dev/null +++ b/351004/Barsukov/app/schemas/sticker.py @@ -0,0 +1,10 @@ +from pydantic import BaseModel, Field +from typing import Optional + +class StickerRequestTo(BaseModel): + id: Optional[int] = None + name: str = Field(..., min_length=2, max_length=32) + +class StickerResponseTo(BaseModel): + id: int + name: str \ No newline at end of file diff --git a/351004/Barsukov/app/services/author_service.py b/351004/Barsukov/app/services/author_service.py new file mode 100644 index 000000000..cc0dae407 --- /dev/null +++ b/351004/Barsukov/app/services/author_service.py @@ -0,0 +1,22 @@ +from sqlalchemy.orm import Session +from repository import AuthorRepo +from app.schemas.author import AuthorRequestTo + +class AuthorService: + def __init__(self): + self.repo = AuthorRepo() + + def create(self, db: Session, dto: AuthorRequestTo): + return self.repo.create(db, dto.model_dump(exclude_none=True)) + + def get_all(self, db: Session, skip=0, limit=10, sort="id", name=None): + return self.repo.get_all(db, skip=skip, limit=limit, sort_by=sort, firstname=name) + + def get_by_id(self, db: Session, id: int): + return self.repo.get_by_id(db, id) + + def update(self, db: Session, id: int, dto: AuthorRequestTo): + return self.repo.update(db, id, dto.model_dump(exclude_none=True)) + + def delete(self, db: Session, id: int): + return self.repo.delete(db, id) \ No newline at end of file diff --git a/351004/Barsukov/app/services/issue_service.py b/351004/Barsukov/app/services/issue_service.py new file mode 100644 index 000000000..8b8356d3a --- /dev/null +++ b/351004/Barsukov/app/services/issue_service.py @@ -0,0 +1,28 @@ +from sqlalchemy.orm import Session +from repository import IssueRepo +from app.schemas.issue import IssueRequestTo + +class IssueService: + def __init__(self): + self.repo = IssueRepo() + + def create(self, db: Session, dto: IssueRequestTo): + data = dto.model_dump(exclude_none=True) + # Маппинг CamelCase -> snake_case + if "authorId" in data: data["author_id"] = data.pop("authorId") + if "stickerIds" in data: data.pop("stickerIds") + return self.repo.create(db, data) + + def get_all(self, db: Session, skip=0, limit=10): + return self.repo.get_all(db, skip=skip, limit=limit) + + def get_by_id(self, db: Session, id: int): + return self.repo.get_by_id(db, id) + + def update(self, db: Session, id: int, dto: IssueRequestTo): + data = dto.model_dump(exclude_none=True) + if "authorId" in data: data["author_id"] = data.pop("authorId") + return self.repo.update(db, id, data) + + def delete(self, db: Session, id: int): + return self.repo.delete(db, id) \ No newline at end of file diff --git a/351004/Barsukov/app/services/note_service.py b/351004/Barsukov/app/services/note_service.py new file mode 100644 index 000000000..6c781f843 --- /dev/null +++ b/351004/Barsukov/app/services/note_service.py @@ -0,0 +1,26 @@ +from sqlalchemy.orm import Session +from repository import NoteRepo +from app.schemas.note import NoteRequestTo + +class NoteService: + def __init__(self): + self.repo = NoteRepo() + + def create(self, db: Session, dto: NoteRequestTo): + data = dto.model_dump(exclude_none=True) + if "issueId" in data: data["issue_id"] = data.pop("issueId") + return self.repo.create(db, data) + + def get_all(self, db: Session): + return self.repo.get_all(db) + + def get_by_id(self, db: Session, id: int): + return self.repo.get_by_id(db, id) + + def update(self, db: Session, id: int, dto: NoteRequestTo): + data = dto.model_dump(exclude_none=True) + if "issueId" in data: data["issue_id"] = data.pop("issueId") + return self.repo.update(db, id, data) + + def delete(self, db: Session, id: int): + return self.repo.delete(db, id) \ No newline at end of file diff --git a/351004/Barsukov/app/services/sticker_service.py b/351004/Barsukov/app/services/sticker_service.py new file mode 100644 index 000000000..e0dd26a7d --- /dev/null +++ b/351004/Barsukov/app/services/sticker_service.py @@ -0,0 +1,22 @@ +from sqlalchemy.orm import Session +from repository import StickerRepo +from app.schemas.sticker import StickerRequestTo + +class StickerService: + def __init__(self): + self.repo = StickerRepo() + + def create(self, db: Session, dto: StickerRequestTo): + return self.repo.create(db, dto.model_dump(exclude_none=True)) + + def get_all(self, db: Session): + return self.repo.get_all(db) + + def get_by_id(self, db: Session, id: int): + return self.repo.get_by_id(db, id) + + def update(self, db: Session, id: int, dto: StickerRequestTo): + return self.repo.update(db, id, dto.model_dump(exclude_none=True)) + + def delete(self, db: Session, id: int): + return self.repo.delete(db, id) \ No newline at end of file diff --git a/351004/Barsukov/main.py b/351004/Barsukov/main.py new file mode 100644 index 000000000..16e564e44 --- /dev/null +++ b/351004/Barsukov/main.py @@ -0,0 +1,10 @@ +import uvicorn +from fastapi import FastAPI +from app.api.v1.router import api_router + +app = FastAPI() + +app.include_router(api_router, prefix="/api/v1.0") + +if __name__ == "__main__": + uvicorn.run(app, host="127.0.0.1", port=24110) \ No newline at end of file