Skip to content

Commit 68e69a0

Browse files
committed
✨ feat: create jmy data
1 parent b654c55 commit 68e69a0

File tree

10 files changed

+304
-2
lines changed

10 files changed

+304
-2
lines changed

alembic/env.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from alembic import context
77
from app.core.configs import configs
88
from app.core.database import database
9-
from app.models.base import BaseModel
9+
from app.models import BaseModel
1010

1111
# this is the Alembic Config object, which provides
1212
# access to the values within the .ini file in use.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""feat: jmy
2+
3+
Revision ID: 7d6aa6eaf640
4+
Revises: 0bdf3abd5213
5+
Create Date: 2025-03-05 00:15:09.139612
6+
7+
"""
8+
from typing import Sequence, Union
9+
10+
from alembic import op
11+
import sqlalchemy as sa
12+
13+
14+
# revision identifiers, used by Alembic.
15+
revision: str = '7d6aa6eaf640'
16+
down_revision: Union[str, None] = '0bdf3abd5213'
17+
branch_labels: Union[str, Sequence[str], None] = None
18+
depends_on: Union[str, Sequence[str], None] = None
19+
20+
21+
def upgrade() -> None:
22+
# ### commands auto generated by Alembic - please adjust! ###
23+
op.create_table('jmy_company',
24+
sa.Column('name', sa.String(length=255), nullable=False),
25+
sa.Column('year', sa.Integer(), nullable=False),
26+
sa.Column('location', sa.String(length=255), nullable=False),
27+
sa.Column('address', sa.String(length=255), nullable=False),
28+
sa.Column('type', sa.String(length=255), nullable=False),
29+
sa.Column('size', sa.String(length=255), nullable=False),
30+
sa.Column('research', sa.String(length=255), nullable=True),
31+
sa.Column('id', sa.Integer(), nullable=False),
32+
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
33+
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
34+
sa.PrimaryKeyConstraint('id')
35+
)
36+
op.create_table('jmy_time_series',
37+
sa.Column('company_id', sa.Integer(), nullable=False),
38+
sa.Column('date', sa.DateTime(timezone=True), nullable=False),
39+
sa.Column('b_assigned', sa.Integer(), nullable=False),
40+
sa.Column('b_new', sa.Integer(), nullable=False),
41+
sa.Column('b_old', sa.Integer(), nullable=False),
42+
sa.Column('a_assigned', sa.Integer(), nullable=False),
43+
sa.Column('a_new', sa.Integer(), nullable=False),
44+
sa.Column('a_old', sa.Integer(), nullable=False),
45+
sa.Column('id', sa.Integer(), nullable=False),
46+
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
47+
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
48+
sa.ForeignKeyConstraint(['company_id'], ['jmy_company.id'], ),
49+
sa.PrimaryKeyConstraint('id')
50+
)
51+
# ### end Alembic commands ###
52+
53+
54+
def downgrade() -> None:
55+
# ### commands auto generated by Alembic - please adjust! ###
56+
op.drop_table('jmy_time_series')
57+
op.drop_table('jmy_company')
58+
# ### end Alembic commands ###

app/api/v1/endpoints/admin/jmy.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from dependency_injector.wiring import Provide, inject
2+
from fastapi import Depends, status
3+
from fastapi.responses import ORJSONResponse
4+
5+
from app.core.auth import (
6+
AdminAuthDeps,
7+
GitHubOAuthDeps,
8+
GoogleOAuthDeps,
9+
PasswordOAuthDeps,
10+
)
11+
from app.core.container import Container
12+
from app.core.router import CoreAPIRouter
13+
from app.schemas.jmy import JmyCompanyOut, JmyCompanyRequest
14+
from app.services.jmy import JmyService
15+
16+
router = CoreAPIRouter(
17+
prefix="/jmy",
18+
tags=["admin"],
19+
dependencies=[AdminAuthDeps, PasswordOAuthDeps, GoogleOAuthDeps, GitHubOAuthDeps],
20+
)
21+
22+
23+
@router.post(
24+
"",
25+
response_model=JmyCompanyOut,
26+
response_class=ORJSONResponse,
27+
status_code=status.HTTP_200_OK,
28+
summary="",
29+
description="",
30+
)
31+
@inject
32+
async def create_jmy(
33+
schema: JmyCompanyRequest,
34+
service: JmyService = Depends(Provide[Container.jmy_service]),
35+
):
36+
return await service.create(schema)
37+
38+
39+
# TODO: 회사 명을 통해 데이터 조회
40+
# @router.get(
41+
# "",
42+
# response_model=JmyCompanyOut,
43+
# response_class=ORJSONResponse,
44+
# status_code=status.HTTP_200_OK,
45+
# summary="",
46+
# description="",
47+
# )
48+
# @inject
49+
# async def get_jmy(
50+
# name: str,
51+
# service: JmyService = Depends(Provide[Container.jmy_service]),
52+
# ):
53+
# return await service.get_by_id(name)

app/api/v1/routers.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
from fastapi import APIRouter
22

33
from app.api.v1.endpoints import auth, shields, users
4+
from app.api.v1.endpoints.admin import jmy as admin_jmy
45
from app.api.v1.endpoints.admin import users as admin_users
56

67
routers = APIRouter(prefix="/v1", tags=["v1"])
7-
_routers = [auth.router, users.router, shields.router] + [admin_users.router]
8+
_routers = [auth.router, users.router, shields.router] + [
9+
admin_users.router,
10+
admin_jmy.router,
11+
]
812

913
for _router in _routers:
1014
routers.include_router(_router)

app/core/container.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
from dependency_injector.providers import Factory
33

44
from app.repositories.auth import AuthRepository
5+
from app.repositories.jmy import JmyRepository
56
from app.repositories.users import UserRepository
67
from app.services.auth import AuthService, JwtService
8+
from app.services.jmy import JmyService
79
from app.services.security import CryptService
810
from app.services.users import UserService
911

@@ -15,11 +17,13 @@ class Container(DeclarativeContainer):
1517
"app.api.v1.endpoints.users",
1618
"app.api.v1.endpoints.auth",
1719
"app.api.v1.endpoints.admin.users",
20+
"app.api.v1.endpoints.admin.jmy",
1821
]
1922
)
2023

2124
user_repository = Factory(UserRepository)
2225
auth_repository = Factory(AuthRepository)
26+
jmy_repository = Factory(JmyRepository)
2327

2428
jwt_service = Factory(JwtService)
2529
crypt_service = Factory(CryptService)
@@ -31,3 +35,4 @@ class Container(DeclarativeContainer):
3135
jwt_service=jwt_service,
3236
crypt_service=crypt_service,
3337
)
38+
jmy_service = Factory(JmyService, jmy_repository=jmy_repository)

app/models/__init__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from app.models.auth import OAuth
2+
from app.models.base import BaseModel
3+
from app.models.enums import OAuthProvider, Role
4+
from app.models.jmy import JmyCompany, JmyTimeSeries
5+
from app.models.users import User
6+
7+
__all__ = [
8+
"BaseModel",
9+
"User",
10+
"OAuth",
11+
"Role",
12+
"OAuthProvider",
13+
"JmyCompany",
14+
"JmyTimeSeries",
15+
]

app/models/jmy.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from datetime import datetime
2+
3+
from sqlalchemy import (
4+
DateTime,
5+
ForeignKey,
6+
Integer,
7+
String,
8+
)
9+
from sqlalchemy.orm import Mapped, mapped_column, relationship
10+
11+
from app.models.base import BaseModel
12+
13+
14+
class JmyCompany(BaseModel):
15+
__tablename__ = "jmy_company"
16+
17+
# 업체명
18+
name: Mapped[str] = mapped_column(String(255), nullable=False)
19+
# 선정년도
20+
year: Mapped[int] = mapped_column(Integer, nullable=False)
21+
# 지방청
22+
location: Mapped[str] = mapped_column(String(255), nullable=False)
23+
# 주소
24+
address: Mapped[str] = mapped_column(String(255), nullable=False)
25+
# 업종
26+
type_: Mapped[str] = mapped_column("type", String(255), nullable=False)
27+
# 기업규모
28+
size: Mapped[str] = mapped_column(String(255), nullable=False)
29+
# 연구분야
30+
research: Mapped[str] = mapped_column(String(255), nullable=True)
31+
32+
time_series = relationship(
33+
"JmyTimeSeries",
34+
back_populates="company",
35+
cascade="all, delete-orphan",
36+
lazy="selectin",
37+
)
38+
39+
eagers = ["time_series"]
40+
41+
42+
class JmyTimeSeries(BaseModel):
43+
__tablename__ = "jmy_time_series"
44+
45+
company_id: Mapped[int] = mapped_column(
46+
ForeignKey("jmy_company.id"), nullable=False
47+
)
48+
49+
date: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
50+
# 보충역 배정인원
51+
b_assigned: Mapped[int] = mapped_column(Integer, nullable=False)
52+
# 보충역 편입인원
53+
b_new: Mapped[int] = mapped_column(Integer, nullable=False)
54+
# 보충역 복무인원
55+
b_old: Mapped[int] = mapped_column(Integer, nullable=False)
56+
# 현역 배정인원
57+
a_assigned: Mapped[int] = mapped_column(Integer, nullable=False)
58+
# 현역 편입인원
59+
a_new: Mapped[int] = mapped_column(Integer, nullable=False)
60+
# 현역 복무인원
61+
a_old: Mapped[int] = mapped_column(Integer, nullable=False)
62+
63+
company = relationship("JmyCompany", back_populates="time_series", lazy="noload")
64+
65+
eagers = ["company"]

app/repositories/jmy.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from sqlalchemy import select
2+
3+
from app.core.database import database
4+
from app.models.jmy import JmyCompany
5+
from app.repositories.base import BaseRepository
6+
7+
8+
class JmyRepository(BaseRepository[JmyCompany]):
9+
def __init__(self):
10+
super().__init__(model=JmyCompany)
11+
12+
async def read_by_name(self, name: str, eager: bool = False) -> JmyCompany:
13+
stmt = select(self.model)
14+
if eager:
15+
stmt = self._eager(stmt=stmt)
16+
stmt = stmt.where(self.model.name == name)
17+
session = database.scoped_session()
18+
result = await session.execute(stmt)
19+
entity = result.scalar_one_or_none()
20+
return entity

app/schemas/jmy.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from datetime import datetime
2+
3+
from app.schemas.base import BaseRequest, BaseResponse
4+
5+
6+
class JmyCompanyRequest(BaseRequest):
7+
name: str
8+
year: int
9+
location: str
10+
address: str
11+
type_: str
12+
size: str
13+
research: str
14+
date: datetime
15+
b_assigned: int
16+
b_new: int
17+
b_old: int
18+
a_assigned: int
19+
a_new: int
20+
a_old: int
21+
22+
23+
class JmyCompanyResponse(BaseResponse):
24+
name: str
25+
year: int
26+
location: str
27+
address: str
28+
type_: str
29+
size: str
30+
research: str
31+
32+
33+
class JmyTimeSeriesOut(BaseResponse):
34+
date: datetime
35+
b_assigned: int
36+
b_new: int
37+
b_old: int
38+
a_assigned: int
39+
a_new: int
40+
a_old: int
41+
42+
43+
class JmyCompanyOut(JmyCompanyResponse):
44+
time_series: list[JmyTimeSeriesOut]

app/services/jmy.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from app.core.database import database
2+
from app.models.jmy import JmyCompany, JmyTimeSeries
3+
from app.repositories.jmy import JmyRepository
4+
from app.schemas.jmy import JmyCompanyOut, JmyCompanyRequest
5+
from app.services.base import BaseService
6+
7+
8+
class JmyService(BaseService[JmyCompany, JmyCompanyRequest, JmyCompanyOut]):
9+
def __init__(self, jmy_repository: JmyRepository):
10+
super().__init__(repository=jmy_repository, schema=JmyCompanyOut)
11+
self.repository: jmy_repository
12+
13+
@database.transactional
14+
async def create(self, schema: JmyCompanyRequest) -> JmyCompanyOut:
15+
jmy_company = await self.repository.read_by_name(name=schema.name)
16+
if jmy_company is None:
17+
jmy_company = JmyCompany(
18+
name=schema.name,
19+
year=schema.year,
20+
location=schema.location,
21+
address=schema.address,
22+
type_=schema.type_,
23+
size=schema.size,
24+
research=schema.research,
25+
)
26+
jmy_company.time_series.append(
27+
JmyTimeSeries(
28+
date=schema.date,
29+
b_assigned=schema.b_assigned,
30+
b_new=schema.b_new,
31+
b_old=schema.b_old,
32+
a_assigned=schema.a_assigned,
33+
a_new=schema.a_new,
34+
a_old=schema.a_old,
35+
)
36+
)
37+
entity = await self.repository.create(entity=jmy_company)
38+
return self.mapper(entity)

0 commit comments

Comments
 (0)