Coverage for apps/kwai-api/src/kwai_api/v1/club/members/endpoints.py: 96%
74 statements
« prev ^ index » next coverage.py v7.11.0, created at 2024-01-01 00:00 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2024-01-01 00:00 +0000
1"""Module for defining the endpoints for members of the club API."""
3from pathlib import Path
4from typing import Annotated
6from fastapi import APIRouter, Depends, HTTPException, Query, UploadFile, status
7from fastapi.params import File
8from kwai_bc_club.get_member import GetMember, GetMemberCommand
9from kwai_bc_club.get_members import GetMembers, GetMembersCommand
10from kwai_bc_club.import_members import ImportMembers, ImportMembersCommand
11from kwai_bc_club.repositories.country_db_repository import CountryDbRepository
12from kwai_bc_club.repositories.file_upload_db_repository import FileUploadDbRepository
13from kwai_bc_club.repositories.file_upload_preview_repository import (
14 FileUploadPreviewRepository,
15)
16from kwai_bc_club.repositories.file_upload_repository import (
17 DuplicateMemberUploadedException,
18)
19from kwai_bc_club.repositories.flemish_member_importer import FlemishMemberImporter
20from kwai_bc_club.repositories.member_db_repository import MemberDbRepository
21from kwai_bc_club.repositories.member_repository import MemberNotFoundException
22from kwai_bc_identity.users.user import UserEntity
23from kwai_core.db.database import Database
24from kwai_core.db.uow import UnitOfWork
25from kwai_core.functions import generate_filenames
26from kwai_core.json_api import PaginationModel
27from kwai_core.settings import Settings
28from pydantic import BaseModel, Field
30from kwai_api.dependencies import create_database, get_current_user, get_settings
31from kwai_api.v1.club.members.presenters import (
32 JsonApiMemberPresenter,
33 JsonApiMembersPresenter,
34 JsonApiUploadMemberPresenter,
35)
36from kwai_api.v1.club.schemas.member import MemberDocument, MembersDocument
37from kwai_api.v1.club.schemas.upload import MemberUploadDocument
40router = APIRouter()
43class MembersFilterModel(BaseModel):
44 """Define the JSON:API filter for members."""
46 enabled: bool = Field(Query(default=True, alias="filter[enabled]"))
47 license_end_month: int = Field(Query(default=0, alias="filter[license_end_month]"))
48 license_end_year: int = Field(Query(default=0, alias="filter[license_end_year]"))
49 name: str = Field(Query(default="", alias="filter[name]"))
50 is_coach: bool | None = Field(Query(default=None, alias="filter[is_coach]"))
53@router.get("/members")
54async def get_members(
55 pagination: Annotated[PaginationModel, Depends(PaginationModel)],
56 members_filter: Annotated[MembersFilterModel, Depends(MembersFilterModel)],
57 db: Annotated[Database, Depends(create_database)],
58 user: Annotated[UserEntity, Depends(get_current_user)],
59) -> MembersDocument:
60 """Get members."""
61 command = GetMembersCommand(
62 offset=pagination.offset or 0,
63 limit=pagination.limit or 0,
64 active=members_filter.enabled,
65 license_end_year=members_filter.license_end_year,
66 license_end_month=members_filter.license_end_month,
67 name=members_filter.name,
68 is_coach=members_filter.is_coach,
69 )
70 presenter = JsonApiMembersPresenter()
71 await GetMembers(MemberDbRepository(db), presenter).execute(command)
73 return presenter.get_document()
76@router.get("/members/{uuid}")
77async def get_member(
78 uuid: str,
79 db=Depends(create_database),
80 user: UserEntity = Depends(get_current_user),
81) -> MemberDocument:
82 """Get a member with the given unique id."""
83 command = GetMemberCommand(uuid=uuid)
85 presenter = JsonApiMemberPresenter()
86 try:
87 await GetMember(MemberDbRepository(db), presenter).execute(command)
88 except MemberNotFoundException as ex:
89 raise HTTPException(
90 status_code=status.HTTP_404_NOT_FOUND, detail=str(ex)
91 ) from ex
93 return presenter.get_document()
96@router.post("/members/upload")
97async def upload(
98 member_file: Annotated[
99 UploadFile, File(description="A file with members to upload into kwai")
100 ],
101 settings: Annotated[Settings, Depends(get_settings)],
102 database: Annotated[Database, Depends(create_database)],
103 user: Annotated[UserEntity, Depends(get_current_user)],
104 preview: Annotated[bool, Query(description="Whether or not to preview")] = True,
105) -> MemberUploadDocument:
106 """Upload a members csv file."""
107 if member_file.filename is None:
108 raise HTTPException(
109 status_code=status.HTTP_400_BAD_REQUEST,
110 detail="There is no filename available for the uploaded file.",
111 )
113 try:
114 member_filename = await upload_file(member_file, settings.files.path)
115 except Exception as ex:
116 raise HTTPException(
117 status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
118 detail=f"Failed to upload members file: {ex}",
119 ) from ex
121 presenter = JsonApiUploadMemberPresenter(preview=preview)
122 async with UnitOfWork(database):
123 try:
124 await ImportMembers(
125 FlemishMemberImporter(
126 str(member_filename),
127 user.create_owner(),
128 CountryDbRepository(database),
129 ),
130 (
131 FileUploadPreviewRepository()
132 if preview
133 else FileUploadDbRepository(database)
134 ),
135 MemberDbRepository(database),
136 presenter,
137 ).execute(ImportMembersCommand(preview=preview))
138 except DuplicateMemberUploadedException as ex:
139 raise HTTPException(
140 status_code=status.HTTP_400_BAD_REQUEST,
141 detail=f"Failed to upload members file: {ex}",
142 ) from ex
144 return presenter.get_document()
147async def upload_file(uploaded_file, path: str):
148 """Creates a unique file for the uploaded file."""
149 file_path = Path(path)
150 file_path.mkdir(parents=True, exist_ok=True)
151 member_file_path = file_path / uploaded_file.filename
152 member_filename_generator = generate_filenames(
153 member_file_path.stem + "_", member_file_path.suffix
154 )
155 while True:
156 member_filename = file_path / next(member_filename_generator)
157 if not member_filename.exists():
158 break
160 with open(member_filename, "wb") as fh:
161 fh.write(await uploaded_file.read())
163 return member_filename