Coverage for src/kwai/api/v1/club/endpoints/upload_members.py: 94%

53 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2024-01-01 00:00 +0000

1"""Module that defines the members API.""" 

2 

3from pathlib import Path 

4from typing import Annotated 

5 

6from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile, status 

7from pydantic import BaseModel, Field 

8 

9from kwai.api.dependencies import create_database, get_current_user 

10from kwai.api.v1.club.presenters import JsonApiUploadMemberPresenter 

11from kwai.api.v1.club.schemas.member import MemberDocument 

12from kwai.core.db.database import Database 

13from kwai.core.db.uow import UnitOfWork 

14from kwai.core.functions import generate_filenames 

15from kwai.core.settings import Settings, get_settings 

16from kwai.modules.club.import_members import ( 

17 ImportMembers, 

18 ImportMembersCommand, 

19) 

20from kwai.modules.club.repositories.country_db_repository import CountryDbRepository 

21from kwai.modules.club.repositories.file_upload_db_repository import ( 

22 FileUploadDbRepository, 

23) 

24from kwai.modules.club.repositories.file_upload_preview_repository import ( 

25 FileUploadPreviewRepository, 

26) 

27from kwai.modules.club.repositories.file_upload_repository import ( 

28 DuplicateMemberUploadedException, 

29) 

30from kwai.modules.club.repositories.flemish_member_importer import FlemishMemberImporter 

31from kwai.modules.club.repositories.member_db_repository import MemberDbRepository 

32from kwai.modules.identity.users.user import UserEntity 

33 

34 

35router = APIRouter() 

36 

37 

38class UploadMemberModel(BaseModel): 

39 """Model containing information about a row. 

40 

41 The id will be set, when a member is successfully imported. When a member was 

42 not imported, the message will contain a description about the problem. 

43 """ 

44 

45 row: int 

46 id: int | None = None 

47 message: str = "" 

48 

49 

50class UploadMembersModel(BaseModel): 

51 """Model that contains the information about all rows.""" 

52 

53 members: list[UploadMemberModel] = Field(default_factory=list) 

54 

55 

56@router.post("/members/upload") 

57async def upload( 

58 member_file: Annotated[ 

59 UploadFile, File(description="A file with members to upload into kwai") 

60 ], 

61 settings: Annotated[Settings, Depends(get_settings)], 

62 database: Annotated[Database, Depends(create_database)], 

63 user: Annotated[UserEntity, Depends(get_current_user)], 

64 preview: Annotated[bool, Query(description="Whether or not to preview")] = True, 

65) -> MemberDocument: 

66 """Upload a members csv file.""" 

67 if member_file.filename is None: 

68 raise HTTPException( 

69 status_code=status.HTTP_400_BAD_REQUEST, 

70 detail="There is no filename available for the uploaded file.", 

71 ) 

72 

73 try: 

74 member_filename = await upload_file(member_file, settings.files.path) 

75 except Exception as ex: 

76 raise HTTPException( 

77 status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, 

78 detail=f"Failed to upload members file: {ex}", 

79 ) from ex 

80 

81 presenter = JsonApiUploadMemberPresenter() 

82 async with UnitOfWork(database): 

83 try: 

84 await ImportMembers( 

85 FlemishMemberImporter( 

86 str(member_filename), 

87 user.create_owner(), 

88 CountryDbRepository(database), 

89 ), 

90 ( 

91 FileUploadPreviewRepository() 

92 if preview 

93 else FileUploadDbRepository(database) 

94 ), 

95 MemberDbRepository(database), 

96 presenter, 

97 ).execute(ImportMembersCommand(preview=preview)) 

98 except DuplicateMemberUploadedException as ex: 

99 raise HTTPException( 

100 status_code=status.HTTP_400_BAD_REQUEST, 

101 detail=f"Failed to upload members file: {ex}", 

102 ) from ex 

103 

104 return presenter.get_document() 

105 

106 

107async def upload_file(uploaded_file, path: str): 

108 """Creates a unique file for the uploaded file.""" 

109 file_path = Path(path) 

110 file_path.mkdir(parents=True, exist_ok=True) 

111 member_file_path = file_path / uploaded_file.filename 

112 member_filename_generator = generate_filenames( 

113 member_file_path.stem + "_", member_file_path.suffix 

114 ) 

115 while True: 

116 member_filename = file_path / next(member_filename_generator) 

117 if not member_filename.exists(): 

118 break 

119 

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

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

122 

123 return member_filename