Coverage for src/kwai/modules/club/import_members.py: 93%
73 statements
« prev ^ index » next coverage.py v7.6.10, created at 2024-01-01 00:00 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2024-01-01 00:00 +0000
1"""Module that implements a use case for importing members."""
3from abc import ABC
4from dataclasses import dataclass
6from kwai.core.domain.entity import Entity
7from kwai.core.domain.presenter import Presenter
8from kwai.core.domain.use_case import UseCaseResult
9from kwai.modules.club.domain.file_upload import FileUploadEntity
10from kwai.modules.club.domain.member import MemberEntity
11from kwai.modules.club.repositories import member_importer
12from kwai.modules.club.repositories.file_upload_repository import FileUploadRepository
13from kwai.modules.club.repositories.member_repository import (
14 MemberNotFoundException,
15 MemberRepository,
16)
19@dataclass(kw_only=True, slots=True, frozen=True)
20class MemberImportResult(UseCaseResult, ABC):
21 """The result of the use case ImportMembers."""
23 file_upload: FileUploadEntity
24 row: int
27@dataclass(kw_only=True, slots=True, frozen=True)
28class OkMemberImportResult(MemberImportResult):
29 """A successful import of a member."""
31 member: MemberEntity
33 def to_message(self) -> str:
34 return f"Member {self.member.id}(row={self.row}) imported successfully."
37@dataclass(kw_only=True, slots=True, frozen=True)
38class FailureMemberImportResult(MemberImportResult):
39 """An import of a member failed."""
41 message: str
43 def to_message(self) -> str:
44 return self.message
47@dataclass(kw_only=True, slots=True, frozen=True)
48class ImportMembersCommand:
49 """Input for the use case "ImportMembers"."""
51 preview: bool = True
54class ImportMembers:
55 """Use case for importing members."""
57 def __init__(
58 self,
59 importer: member_importer.MemberImporter,
60 file_upload_repo: FileUploadRepository,
61 member_repo: MemberRepository,
62 presenter: Presenter,
63 ):
64 """Initialize the use case.
66 Args:
67 importer: A class that is responsible for importing members from a resource.
68 file_upload_repo: A repository for storing the file upload information.
69 member_repo: A repository for managing members.
70 presenter: A presenter
71 """
72 self._importer = importer
73 self._file_upload_repo = file_upload_repo
74 self._member_repo = member_repo
75 self._presenter = presenter
77 async def execute(self, command: ImportMembersCommand):
78 """Execute the use case."""
79 file_upload_entity = await self._file_upload_repo.create(
80 self._importer.create_file_upload_entity(command.preview)
81 )
82 async for import_result in self._importer.import_():
83 match import_result:
84 case member_importer.OkResult():
85 member = await self._save_member(
86 file_upload_entity, import_result.member, command.preview
87 )
88 self._presenter.present(
89 OkMemberImportResult(
90 file_upload=file_upload_entity,
91 row=import_result.row,
92 member=member,
93 )
94 )
95 case member_importer.FailureResult():
96 self._presenter.present(
97 FailureMemberImportResult(
98 file_upload=file_upload_entity,
99 row=import_result.row,
100 message=import_result.message,
101 )
102 )
103 if not command.preview:
104 await self._activate_members(file_upload_entity)
106 async def _save_member(
107 self, file_upload: FileUploadEntity, member: MemberEntity, preview: bool
108 ) -> MemberEntity:
109 """Create or update the member."""
110 existing_member = await self._get_member(member)
111 if existing_member is not None:
112 updated_member = self._update_member(existing_member, member)
113 await self._file_upload_repo.save_member(file_upload, updated_member)
114 if not preview:
115 await self._member_repo.update(updated_member)
116 return updated_member
118 if not preview:
119 member = await self._member_repo.create(member)
120 await self._file_upload_repo.save_member(file_upload, member)
122 return member
124 @classmethod
125 def _update_member(
126 cls, old_member: MemberEntity, new_member: MemberEntity
127 ) -> MemberEntity:
128 """Update an existing member with the new imported data."""
129 updated_contact = Entity.replace(
130 old_member.person.contact,
131 id_=old_member.person.contact.id,
132 traceable_time=old_member.person.contact.traceable_time.mark_for_update(),
133 )
134 updated_person = Entity.replace(
135 old_member.person,
136 id_=old_member.person.id,
137 contact=updated_contact,
138 traceable_time=old_member.person.traceable_time.mark_for_update(),
139 )
140 updated_member = Entity.replace(
141 new_member,
142 id_=old_member.id,
143 uuid=old_member.uuid,
144 remark=old_member.remark,
145 competition=old_member.is_competitive,
146 active=old_member.is_active,
147 person=updated_person,
148 traceable_time=old_member.traceable_time.mark_for_update(),
149 )
150 return updated_member
152 async def _get_member(self, member: MemberEntity) -> MemberEntity | None:
153 """Return the member.
155 Returns:
156 If found the member is returned, otherwise None is returned.
157 """
158 member_query = self._member_repo.create_query()
159 member_query.filter_by_license(member.license.number)
161 try:
162 member = await self._member_repo.get(member_query)
163 except MemberNotFoundException:
164 return None
166 return member
168 async def _activate_members(self, upload_entity: FileUploadEntity):
169 """Activate members.
171 Members that are part of the upload will be activated.
172 Members not part of the upload will be deactivated.
173 """
174 await self._member_repo.activate_members(upload_entity)
175 await self._member_repo.deactivate_members(upload_entity)