Skip to content

Commit 71ac91d

Browse files
authored
feat: history api 구현 (#80)
* History 관련 api를 구현했습니다. * History table을 수정하고 마이그레이션 파일을 추가했습니다.
1 parent 7917594 commit 71ac91d

9 files changed

Lines changed: 212 additions & 5 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .views import v3_router as router
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from wacruit.src.apps.common.exceptions import WacruitException
2+
3+
4+
class HistoryEmptyException(WacruitException):
5+
def __init__(self):
6+
super().__init__(status_code=404, detail="History 테이블이 비어 있습니다.")

wacruit/src/apps/history/models.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
from datetime import datetime
2-
31
from sqlalchemy.orm import Mapped
42

53
from wacruit.src.database.base import DeclarativeBase
64
from wacruit.src.database.base import intpk
5+
from wacruit.src.database.base import str50
76

87

98
class History(DeclarativeBase):
109
__tablename__ = "histroy"
1110

1211
id: Mapped[intpk]
13-
total_projects: Mapped[int]
14-
total_users: Mapped[int]
15-
total_members: Mapped[int]
12+
history_key: Mapped[str50]
13+
history_value: Mapped[str50]
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from datetime import datetime
2+
3+
from fastapi import Depends
4+
from sqlalchemy import select
5+
from sqlalchemy.orm import Session
6+
7+
from wacruit.src.apps.history.models import History
8+
from wacruit.src.database.connection import get_db_session
9+
from wacruit.src.database.connection import Transaction
10+
11+
12+
class HistoryRepository:
13+
def __init__(
14+
self,
15+
session: Session = Depends(get_db_session),
16+
transaction: Transaction = Depends(),
17+
):
18+
self.session = session
19+
self.transaction = transaction
20+
21+
def get_history(self) -> list[History]:
22+
query = select(History)
23+
return list(self.session.execute(query).scalars().all())
24+
25+
def update_history(self, history: History) -> History:
26+
history_key = history.history_key
27+
history_value = history.history_value
28+
29+
with self.transaction:
30+
query = select(History).where(History.history_key == history_key)
31+
result = self.session.execute(query).scalar_one_or_none()
32+
if result:
33+
result.history_value = history_value
34+
else:
35+
result = History(history_key=history_key, history_value=history_value)
36+
self.session.add(result)
37+
38+
return history
39+
40+
def get_start_date(self) -> datetime | None:
41+
query = select(History).where(History.history_key == "start_date")
42+
result = self.session.execute(query).scalar_one_or_none()
43+
44+
if result is None:
45+
return None
46+
return datetime.strptime(result.history_value, "%Y-%m-%d")
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from pydantic import BaseModel
2+
3+
4+
class UpdateHistoryRequest(BaseModel):
5+
class Config:
6+
extra = "allow"
7+
8+
9+
class HistoryResponse(BaseModel):
10+
__root__: dict[str, str]
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from datetime import datetime
2+
3+
from fastapi import Depends
4+
5+
from wacruit.src.apps.history.exceptions import HistoryEmptyException
6+
from wacruit.src.apps.history.models import History
7+
from wacruit.src.apps.history.repositories import HistoryRepository
8+
from wacruit.src.apps.history.schemas import UpdateHistoryRequest
9+
10+
11+
class HistoryService:
12+
def __init__(self, history_repository: HistoryRepository = Depends()):
13+
self.history_repository = history_repository
14+
15+
def get_history(self) -> list[History]:
16+
history_list = self.history_repository.get_history()
17+
operation_period = self.calculate_operation_period()
18+
19+
if operation_period is not None:
20+
history_list.append(
21+
History(
22+
history_key="operation_period", history_value=str(operation_period)
23+
)
24+
)
25+
26+
if not history_list:
27+
raise HistoryEmptyException
28+
return history_list
29+
30+
def update_history(self, update_request: UpdateHistoryRequest):
31+
req = update_request.dict()
32+
33+
for k, v in req.items():
34+
to_update = History(history_key=k, history_value=v)
35+
self.history_repository.update_history(to_update)
36+
37+
return self.get_history()
38+
39+
def calculate_operation_period(self) -> int | None:
40+
start_date = self.history_repository.get_start_date()
41+
now = datetime.now()
42+
43+
if start_date is None:
44+
return None
45+
return now.year - start_date.year

wacruit/src/apps/history/views.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from typing import Annotated
2+
3+
from fastapi import APIRouter
4+
from fastapi import Depends
5+
6+
from wacruit.src.apps.history.schemas import HistoryResponse
7+
from wacruit.src.apps.history.schemas import UpdateHistoryRequest
8+
from wacruit.src.apps.history.services import HistoryService
9+
from wacruit.src.apps.user.dependencies import AdminUser
10+
11+
v3_router = APIRouter(prefix="/v3/history", tags=["history"])
12+
13+
14+
@v3_router.patch("/")
15+
def update_history(
16+
admin_user: AdminUser,
17+
history_service: Annotated[HistoryService, Depends()],
18+
update_request: UpdateHistoryRequest,
19+
):
20+
updated_history = history_service.update_history(update_request)
21+
response_dict = {}
22+
for h in updated_history:
23+
response_dict[h.history_key] = h.history_value
24+
25+
return HistoryResponse(__root__=response_dict)
26+
27+
28+
@v3_router.get("/")
29+
def get_history(history_service: Annotated[HistoryService, Depends()]):
30+
history_list = history_service.get_history()
31+
response_dict = {}
32+
for h in history_list:
33+
response_dict[h.history_key] = h.history_value
34+
35+
return HistoryResponse(__root__=response_dict)

wacruit/src/apps/router.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from wacruit.src.apps import announcement
44
from wacruit.src.apps import dummy
5+
from wacruit.src.apps import history
56
from wacruit.src.apps import member
67
from wacruit.src.apps import portfolio
78
from wacruit.src.apps import problem
@@ -26,4 +27,5 @@
2627
api_router.include_router(portfolio.v2_router)
2728
api_router.include_router(project.router)
2829
api_router.include_router(seminar.router)
30+
api_router.include_router(history.router)
2931
api_router.include_router(review.router)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"""fix history table
2+
3+
Revision ID: d7157419824c
4+
Revises: 22f4d37c8e9f
5+
Create Date: 2025-07-12 23:06:01.005810
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
from sqlalchemy.dialects import mysql
11+
12+
# revision identifiers, used by Alembic.
13+
revision = "d7157419824c"
14+
down_revision = "22f4d37c8e9f"
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade() -> None:
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
op.add_column(
22+
"histroy", sa.Column("history_key", sa.String(length=50), nullable=False)
23+
)
24+
op.add_column(
25+
"histroy", sa.Column("history_value", sa.String(length=50), nullable=False)
26+
)
27+
op.drop_column("histroy", "total_projects")
28+
op.drop_column("histroy", "total_users")
29+
op.drop_column("histroy", "total_members")
30+
# ### end Alembic commands ###
31+
32+
33+
def downgrade() -> None:
34+
# ### commands auto generated by Alembic - please adjust! ###
35+
op.add_column(
36+
"histroy",
37+
sa.Column(
38+
"total_members",
39+
mysql.INTEGER(display_width=11),
40+
autoincrement=False,
41+
nullable=False,
42+
),
43+
)
44+
op.add_column(
45+
"histroy",
46+
sa.Column(
47+
"total_users",
48+
mysql.INTEGER(display_width=11),
49+
autoincrement=False,
50+
nullable=False,
51+
),
52+
)
53+
op.add_column(
54+
"histroy",
55+
sa.Column(
56+
"total_projects",
57+
mysql.INTEGER(display_width=11),
58+
autoincrement=False,
59+
nullable=False,
60+
),
61+
)
62+
op.drop_column("histroy", "history_value")
63+
op.drop_column("histroy", "history_key")
64+
# ### end Alembic commands ###

0 commit comments

Comments
 (0)