Skip to content

Commit

Permalink
Merge pull request #24 from wafflestudio/time_error
Browse files Browse the repository at this point in the history
시간 서울 시간대로 변경 and 투표 생성시 마감 시간이 현재보다 과거일 수 없도록 변경
  • Loading branch information
morecleverer authored Jan 10, 2025
2 parents e3cc61e + 0937209 commit 035267c
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 18 deletions.
14 changes: 11 additions & 3 deletions snuvote/app/vote/dto/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
from typing import Annotated, Callable, TypeVar, List
from pydantic import BaseModel, EmailStr, Field
from pydantic.functional_validators import AfterValidator
from datetime import datetime
from datetime import datetime, timezone, timedelta

from snuvote.app.vote.errors import InvalidFieldFormatError, ChoicesNotProvidedError, ChoiceInvalidFormatError
from snuvote.app.vote.errors import InvalidFieldFormatError, ChoicesNotProvidedError, ChoiceInvalidFormatError, InvalidEndTimeError

KST = timezone(timedelta(hours=9), "KST")

def validate_title(value: str) -> str:
if len(value) < 1 or len(value) > 100:
Expand Down Expand Up @@ -37,6 +38,13 @@ def validate_choices(value: List[str]) -> List[str]:
for content in value:
if len(content) < 1 or len(content) > 200:
raise ChoiceInvalidFormatError()

return value

def validate_end_datetime(value: datetime) -> datetime:
value = value.replace(tzinfo=KST).astimezone(timezone.utc) # offset_naive한 value가 한국 시간대였음을 주입하고 UTC로 변환
if datetime.now(tz=timezone.utc) >= value:
raise InvalidEndTimeError()
return value

T = TypeVar("T")
Expand Down Expand Up @@ -65,7 +73,7 @@ class CreateVoteRequest(BaseModel):
realtime_result: bool
multiple_choice: bool
annonymous_choice: bool
end_datetime: datetime
end_datetime: Annotated[datetime, AfterValidator(validate_end_datetime)] # end_datetime은 offset_naive임
choices: Annotated[List[str], AfterValidator(validate_choices)]


Expand Down
19 changes: 13 additions & 6 deletions snuvote/app/vote/dto/responses.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
from datetime import datetime
from typing import List
from datetime import datetime, timezone, timedelta
from typing import List, Annotated

from snuvote.database.models import Vote, User, Choice, ChoiceParticipation
from pydantic import BaseModel
from pydantic.functional_validators import AfterValidator

KST = timezone(timedelta(hours=9), "KST")

def convert_utc_to_ktc_naive(value: datetime) -> datetime:
value = value.replace(tzinfo=timezone.utc).astimezone(KST).replace(tzinfo=None) # UTC 시간대 주입 후 KST 시간대로 변환한 뒤 offset-naive로 변환
return value


class VotesListInfoResponse(BaseModel):
id: int
title: str
content: str
create_datetime: datetime
end_datetime: datetime
create_datetime: Annotated[datetime, AfterValidator(convert_utc_to_ktc_naive)] # UTC 시간대를 KST 시간대로 변환한 뒤 offset-naive로 변환
end_datetime: Annotated[datetime, AfterValidator(convert_utc_to_ktc_naive)] # UTC 시간대를 KST 시간대로 변환한 뒤 offset-naive로 변환
participated: bool

@staticmethod
Expand Down Expand Up @@ -90,7 +97,7 @@ class VoteDetailResponse(BaseModel):
realtime_result: bool
multiple_choice: bool
annonymous_choice: bool
create_datetime: datetime
end_datetime: datetime
create_datetime: Annotated[datetime, AfterValidator(convert_utc_to_ktc_naive)] # UTC 시간대를 KST 시간대로 변환한 뒤 offset-naive로 변환
end_datetime: Annotated[datetime, AfterValidator(convert_utc_to_ktc_naive)] # UTC 시간대를 KST 시간대로 변환한 뒤 offset-naive로 변환
choices: List[ChoiceDetailResponse]

7 changes: 6 additions & 1 deletion snuvote/app/vote/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,9 @@ def __init__(self) -> None:

class EndedVoteError(HTTPException):
def __init__(self) -> None:
super().__init__(HTTP_403_FORBIDDEN, "Ended vote")
super().__init__(HTTP_403_FORBIDDEN, "Ended vote")


class InvalidEndTimeError(HTTPException):
def __init__(self) -> None:
super().__init__(HTTP_400_BAD_REQUEST, "Invalid end time")
5 changes: 2 additions & 3 deletions snuvote/app/vote/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from snuvote.app.vote.errors import ChoiceNotFoundError, InvalidFieldFormatError, MultipleChoicesError, ParticipationCodeError, ParticipationCodeNotProvidedError, WrongParticipationCodeError, EndedVoteError
from snuvote.app.vote.dto.requests import ParticipateVoteRequest

from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone



Expand Down Expand Up @@ -51,9 +51,8 @@ def get_vote_by_vote_id(self, vote_id: int) -> Vote:
return self.vote_store.get_vote_by_vote_id(vote_id=vote_id)

def participate_vote(self, vote: Vote, user: User, participate_vote_request: ParticipateVoteRequest) -> None:

# 종료 시간 이후인 경우
if datetime.now() > vote.end_datetime:
if datetime.now(tz=timezone.utc) > vote.end_datetime.replace(tzinfo=timezone.utc):
raise EndedVoteError()

# 참여코드가 필요한 투표글인 경우
Expand Down
8 changes: 5 additions & 3 deletions snuvote/app/vote/store.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from functools import cache
from typing import Annotated, List
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone

from fastapi import Depends
from snuvote.database.models import Vote, Choice, ChoiceParticipation
Expand All @@ -9,6 +9,8 @@
from sqlalchemy import select, delete
from sqlalchemy.orm import Session

KST = timezone(timedelta(hours=9), "KST")

class VoteStore:
def __init__(self, session: Annotated[Session, Depends(get_db_session)]) -> None:
self.session = session
Expand All @@ -27,7 +29,7 @@ def add_vote(self,
end_datetime:datetime,
choices: List[str]) -> Vote:

create_datetime = datetime.now()
create_datetime = datetime.now(tz=timezone.utc)


vote = Vote(writer_id=writer_id,
Expand Down Expand Up @@ -55,7 +57,7 @@ def add_vote(self,

# 진행 중인 투표 리스트 조회
def get_ongoing_list(self) -> List[Vote]:
return self.session.execute(select(Vote).where(Vote.end_datetime > datetime.now())).scalars().all()
return self.session.execute(select(Vote).where(Vote.end_datetime > datetime.now(timezone.utc))).scalars().all()

# 투표글 상세 내용 조회
def get_vote_by_vote_id(self, vote_id: int) -> Vote:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"""Vote의 create, end_datetime를 Timestamp로 변경
Revision ID: 0a5a02cfb40b
Revises: ac9dd8c234ab
Create Date: 2025-01-10 18:32:23.324963
"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = '0a5a02cfb40b'
down_revision: Union[str, None] = 'ac9dd8c234ab'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
op.execute(f"""
UPDATE vote
SET create_datetime = CONVERT_TZ(create_datetime, 'Asia/Seoul', 'UTC');
""")
# DATETIME을 TIMESTAMP로 변경
op.alter_column(
'vote',
'create_datetime',
type_=sa.TIMESTAMP(timezone=False), # TIMESTAMP로 변경
existing_type=sa.DATETIME(),
existing_nullable=False # 기존 NULL 허용 여부를 유지 # 기존 기본값 유지
)
op.execute(f"""
UPDATE vote
SET create_datetime = CONVERT_TZ(create_datetime, 'Asia/Seoul', 'UTC');
""")
# DATETIME을 TIMESTAMP로 변경
op.alter_column(
'vote',
'end_datetime',
type_=sa.TIMESTAMP(timezone=False), # TIMESTAMP로 변경
existing_type=sa.DATETIME(),
existing_nullable=False # 기존 NULL 허용 여부를 유지 # 기존 기본값 유지
)
op.execute(f"""
UPDATE vote
SET end_datetime = CONVERT_TZ(end_datetime, 'Asia/Seoul', 'UTC');
""")



def downgrade() -> None:
op.alter_column(
'vote',
'end_datetime',
type_=sa.DATETIME(),
existing_type=sa.TIMESTAMP(timezone=False),
existing_nullable=True
)
op.execute(f"""
UPDATE vote
SET end_datetime = CONVERT_TZ(end_datetime, 'UTC', 'Asia/Seoul');
""")
op.alter_column(
'vote',
'create_datetime',
type_=sa.DATETIME(),
existing_type=sa.TIMESTAMP(timezone=False),
existing_nullable=True
)
op.execute(f"""
UPDATE vote
SET create_datetime = CONVERT_TZ(create_datetime, 'UTC', 'Asia/Seoul');
""")
pass
4 changes: 2 additions & 2 deletions snuvote/database/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ class Vote(Base):
writer_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("user.id"))
writer: Mapped["User"] = relationship("User", back_populates="votes")

create_datetime: Mapped[datetime] = mapped_column(DateTime, nullable=False)
create_datetime: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
title: Mapped[str] = mapped_column(String(100), nullable=False)
content: Mapped[str] = mapped_column(Text, nullable=False)
end_datetime: Mapped[datetime] = mapped_column(DateTime, nullable=False)
end_datetime: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
participation_code_required: Mapped[bool] = mapped_column(Boolean, nullable=False)
participation_code: Mapped[str] = mapped_column(String(20), nullable=True)
realtime_result: Mapped[bool] = mapped_column(Boolean, nullable=False)
Expand Down

0 comments on commit 035267c

Please sign in to comment.