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

1"""Module for defining the endpoints for members of the club API.""" 

2 

3from pathlib import Path 

4from typing import Annotated 

5 

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 

29 

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 

38 

39 

40router = APIRouter() 

41 

42 

43class MembersFilterModel(BaseModel): 

44 """Define the JSON:API filter for members.""" 

45 

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]")) 

51 

52 

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) 

72 

73 return presenter.get_document() 

74 

75 

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) 

84 

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 

92 

93 return presenter.get_document() 

94 

95 

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 ) 

112 

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 

120 

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 

143 

144 return presenter.get_document() 

145 

146 

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 

159 

160 with open(member_filename, "wb") as fh: 

161 fh.write(await uploaded_file.read()) 

162 

163 return member_filename