Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ jobs:
- uses: isort/isort-action@master
with:
requirementsFiles: "requirements.txt requirements.dev.txt"
- uses: psf/black@stable
- uses: psf/black@23.11.0
- name: Comment if linting failed
if: failure()
uses: thollander/actions-comment-pull-request@v2
Expand Down
4 changes: 4 additions & 0 deletions calendar_backend/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
EventsGroups,
EventsLecturers,
EventsRooms,
EventUser,
EventUserStatus,
Group,
Lecturer,
Room,
Expand All @@ -27,4 +29,6 @@
"EventsRooms",
"ApproveStatuses",
"EventsGroups",
"EventUser",
"EventUserStatus",
]
18 changes: 18 additions & 0 deletions calendar_backend/models/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@
from .base import ApproveStatuses, BaseDbModel


class EventUserStatus(str, Enum):
NO_STATUS: str = "no_status"
GOING: str = "going"
NOT_GOING: str = "not_going"
ATTENDED: str = "attended"


class Credentials(BaseDbModel):
"""User credentials"""

Expand Down Expand Up @@ -211,3 +218,14 @@ class CommentEvent(BaseDbModel):
foreign_keys="CommentEvent.event_id",
primaryjoin="and_(Event.id==CommentEvent.event_id, not_(Event.is_deleted))",
)


class EventUser(BaseDbModel):
id: Mapped[int] = mapped_column(Integer, primary_key=True)
event_id: Mapped[int] = mapped_column(Integer, ForeignKey("event.id"), nullable=False)
user_id: Mapped[int] = mapped_column(Integer, nullable=False)
status: Mapped[EventUserStatus] = mapped_column(DbEnum(EventUserStatus, native_enum=False), nullable=False)
updated_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow
)
is_deleted: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
2 changes: 2 additions & 0 deletions calendar_backend/routes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from .event.comment import router as event_comment_router
from .event.comment_review import router as event_comment_review_router
from .event.event import router as event_router
from .event.user_event import router as user_event_router
from .group.group import router as group_router
from .lecturer.comment import router as lecturer_comment_router
from .lecturer.comment_review import router as lecturer_comment_review_router
Expand Down Expand Up @@ -128,3 +129,4 @@ async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -
app.include_router(event_router)
app.include_router(event_comment_router)
app.include_router(event_comment_review_router)
app.include_router(user_event_router)
2 changes: 1 addition & 1 deletion calendar_backend/routes/event/event.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import logging
from datetime import date, datetime, timedelta
from datetime import date, timedelta
from typing import Literal

from auth_lib.fastapi import UnionAuth
Expand Down
51 changes: 51 additions & 0 deletions calendar_backend/routes/event/user_event.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from auth_lib.fastapi import UnionAuth
from fastapi import APIRouter, Depends, Query
from fastapi_sqlalchemy import db

from calendar_backend.models import Event, EventUser
from calendar_backend.routes.models.visit import VisitResponse


router = APIRouter(prefix="/event", tags=["Event: Visit"])


@router.post("/{event_id}/visit", response_model=VisitResponse)
async def set_event_visit_status(
event_id: int,
auth: dict = Depends(UnionAuth()),
visit: str = Query(enum=["no_status", "going", "not_going"], default="no_status"),
) -> VisitResponse:
"""
Отметить статус посещения мероприятия для текущего пользователя.

Параметры:
event_id - id события, которому будет присвоен статус,
visit - доступные пользователю статусы для присвоения событию (по умолчанию no_status), где:
no_status - событие без пользовательского решения,
going - событие, на которое пользователь решил сходить,
not_going - событие, на которое пользователь решил не ходить.

Ошибки:
ObjectNotFound - нет события с таким event_id
"""
user_id = auth.get('id')

Event.get(event_id, session=db.session)

existing = (
EventUser.get_all(session=db.session)
.filter(EventUser.event_id == event_id, EventUser.user_id == user_id)
.first()
)

if existing:
result = EventUser.update(existing.id, session=db.session, status=visit)
else:
result = EventUser.create(
session=db.session,
event_id=event_id,
user_id=user_id,
status=visit,
)

return VisitResponse.model_validate(result)
2 changes: 2 additions & 0 deletions calendar_backend/routes/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
Photo,
)
from .room import GetListRoom, RoomEvents, RoomPatch, RoomPost
from .visit import VisitResponse


__all__ = (
Expand Down Expand Up @@ -46,4 +47,5 @@
"RoomEvents",
"RoomPatch",
"RoomPost",
"VisitResponse",
)
13 changes: 13 additions & 0 deletions calendar_backend/routes/models/visit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import datetime

from calendar_backend.models import EventUserStatus

from .base import Base


class VisitResponse(Base):
id: int
event_id: int
user_id: int
status: EventUserStatus
updated_at: datetime.datetime
50 changes: 50 additions & 0 deletions migrations/versions/b060027b11b3_eventuser_building.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""EventUser building

Revision ID: b060027b11b3
Revises: 55a049fde8f4
Create Date: 2026-04-20 17:56:39.185374

"""

import sqlalchemy as sa
from alembic import op


# revision identifiers, used by Alembic.
revision = 'b060027b11b3'
down_revision = '55a049fde8f4'
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
'event_user',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('event_id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column(
'status',
sa.Enum('NO_STATUS', 'GOING', 'NOT_GOING', 'ATTENDED', name='eventuserstatus', native_enum=False),
nullable=False,
),
sa.Column('updated_at', sa.DateTime(), nullable=False),
sa.Column('is_deleted', sa.Boolean(), nullable=False),
sa.ForeignKeyConstraint(
['event_id'],
['event.id'],
),
sa.PrimaryKeyConstraint('id'),
)
op.drop_constraint(op.f('lesson_group_id_fkey'), 'event', type_='foreignkey')
op.drop_column('event', 'group_id')
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('event', sa.Column('group_id', sa.INTEGER(), autoincrement=False, nullable=True))
op.create_foreign_key(op.f('lesson_group_id_fkey'), 'event', 'group', ['group_id'], ['id'])
op.drop_table('event_user')
# ### end Alembic commands ###
2 changes: 1 addition & 1 deletion requirements.dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ pytest
pytest-cov
requests
pytest-mock
black
black==23.11.0
isort
autoflake
Loading