Coverage for apps/kwai-api/src/kwai_api/v1/club/members/presenters.py: 97%
64 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 that defines presenters for the club api."""
3from typing import Self
5from kwai_bc_club.domain.contact import ContactEntity
6from kwai_bc_club.domain.member import MemberEntity
7from kwai_bc_club.domain.person import PersonEntity
8from kwai_bc_club.import_members import (
9 FailureMemberImportResult,
10 MemberImportResult,
11 OkMemberImportResult,
12)
13from kwai_core.domain.presenter import AsyncPresenter, IterableResult, Presenter
14from kwai_core.json_api import (
15 JsonApiPresenter,
16 Meta,
17 Relationship,
18 RelationshipList,
19 ResourceMeta,
20)
22from kwai_api.schemas.resources import CountryResourceIdentifier
23from kwai_api.v1.club.schemas.contact import (
24 ContactAttributes,
25 ContactDocument,
26 ContactRelationships,
27 ContactResource,
28)
29from kwai_api.v1.club.schemas.member import (
30 MemberAttributes,
31 MemberDocument,
32 MemberRelationships,
33 MemberResource,
34 MembersDocument,
35)
36from kwai_api.v1.club.schemas.person import (
37 PersonAttributes,
38 PersonDocument,
39 PersonRelationships,
40 PersonResource,
41)
42from kwai_api.v1.club.schemas.resources import (
43 ContactResourceIdentifier,
44 MemberResourceIdentifier,
45 PersonResourceIdentifier,
46)
47from kwai_api.v1.club.schemas.upload import (
48 MemberUploadAttributes,
49 MemberUploadDocument,
50 MemberUploadError,
51 MemberUploadRelationships,
52 MemberUploadResource,
53 UploadedMemberMeta,
54 UploadedMemberResource,
55)
56from kwai_api.v1.presenters import JsonApiCountryPresenter
59class JsonApiContactPresenter(
60 JsonApiPresenter[ContactDocument], Presenter[ContactEntity]
61):
62 """A presenter that transforms a contact entity into a JSON:API document."""
64 def present(self, contact: ContactEntity) -> Self:
65 country_document = (
66 JsonApiCountryPresenter().present(contact.address.country).get_document()
67 )
68 self._document = ContactDocument(
69 data=ContactResource(
70 id=str(contact.id),
71 attributes=ContactAttributes(
72 emails=[str(email) for email in contact.emails],
73 tel=contact.tel,
74 mobile=contact.mobile,
75 address=contact.address.address,
76 postal_code=contact.address.postal_code,
77 city=contact.address.city,
78 county=contact.address.county,
79 remark=contact.remark,
80 ),
81 relationships=ContactRelationships(
82 country=Relationship[CountryResourceIdentifier](
83 data=CountryResourceIdentifier(id=country_document.data.id)
84 )
85 ),
86 meta=ResourceMeta(
87 created_at=str(contact.traceable_time.created_at),
88 updated_at=str(contact.traceable_time.updated_at),
89 ),
90 ),
91 included={country_document.data},
92 )
93 return self
96class JsonApiPersonPresenter(JsonApiPresenter, Presenter[PersonEntity]):
97 """A presenter that transforms a person entity into a JSON:API document."""
99 def present(self, person: PersonEntity) -> Self:
100 country_document = (
101 JsonApiCountryPresenter().present(person.nationality).get_document()
102 )
103 contact_document = (
104 JsonApiContactPresenter().present(person.contact).get_document()
105 )
107 self._document = PersonDocument(
108 data=PersonResource(
109 id=str(person.id),
110 attributes=PersonAttributes(
111 first_name=person.name.first_name,
112 last_name=person.name.last_name,
113 gender=person.gender.value,
114 birthdate=str(person.birthdate),
115 remark=person.remark,
116 ),
117 relationships=PersonRelationships(
118 nationality=Relationship[CountryResourceIdentifier](
119 data=CountryResourceIdentifier(id=country_document.data.id)
120 ),
121 contact=Relationship[ContactResourceIdentifier](
122 data=ContactResourceIdentifier(id=contact_document.data.id)
123 ),
124 ),
125 meta=ResourceMeta(
126 created_at=str(person.traceable_time.created_at),
127 updated_at=str(person.traceable_time.updated_at),
128 ),
129 ),
130 included={country_document.data, contact_document.data}
131 | contact_document.included,
132 )
133 return self
136class JsonApiMemberPresenter(JsonApiPresenter[MemberDocument], Presenter[MemberEntity]):
137 """A presenter that transform a member entity into a JSON:API document."""
139 def present(self, member: MemberEntity) -> Self:
140 person_document = JsonApiPersonPresenter().present(member.person).get_document()
142 self._document = MemberDocument(
143 data=MemberResource(
144 id=str(member.uuid),
145 attributes=MemberAttributes(
146 license_number=member.license.number,
147 license_end_date=str(member.license.end_date),
148 remark=member.remark,
149 active=member.active,
150 competition=member.competition,
151 ),
152 meta=ResourceMeta(
153 created_at=str(member.traceable_time.created_at),
154 updated_at=str(member.traceable_time.updated_at),
155 ),
156 relationships=MemberRelationships(
157 person=Relationship[PersonResourceIdentifier](
158 data=PersonResourceIdentifier(id=person_document.data.id)
159 )
160 ),
161 ),
162 included={person_document.data} | person_document.included,
163 )
165 return self
168class JsonApiMembersPresenter(
169 JsonApiPresenter[MembersDocument], AsyncPresenter[IterableResult[MemberEntity]]
170):
171 """A presenter that transform an iterator for members into a JSON:API document."""
173 async def present(self, use_case_result: IterableResult[MemberEntity]) -> Self:
174 self._document = MembersDocument(
175 meta=Meta(
176 count=use_case_result.count,
177 offset=use_case_result.offset,
178 limit=use_case_result.limit,
179 ),
180 )
181 async for member in use_case_result:
182 member_document = JsonApiMemberPresenter().present(member).get_document()
183 self._document.data.append(member_document.data)
184 self._document.included |= member_document.included
185 return self
188class JsonApiUploadMemberPresenter(
189 JsonApiPresenter[MemberUploadDocument],
190 AsyncPresenter[IterableResult[MemberImportResult]],
191):
192 """A presenter that transform a file upload of a member into a JSON:API document."""
194 def __init__(self, preview: bool = False):
195 super().__init__()
196 self._preview = preview
198 async def present(
199 self, use_case_result: IterableResult[MemberImportResult]
200 ) -> Self:
201 async for import_result in use_case_result:
202 if self._document is None:
203 self._document = MemberUploadDocument(
204 data=MemberUploadResource(
205 id=str(import_result.file_upload.uuid),
206 attributes=MemberUploadAttributes(
207 filename=import_result.file_upload.filename,
208 preview=self._preview,
209 remark=import_result.file_upload.remark,
210 errors=[],
211 ),
212 relationships=MemberUploadRelationships(
213 members=RelationshipList[MemberResourceIdentifier]()
214 ),
215 )
216 )
218 match import_result:
219 case OkMemberImportResult():
220 member_document = (
221 JsonApiMemberPresenter()
222 .present(import_result.member)
223 .get_document()
224 )
225 self._document.data.relationships.members.data.append(
226 MemberResourceIdentifier(id=member_document.data.id)
227 )
229 upload_meta = UploadedMemberMeta(
230 **member_document.data.meta.model_dump(),
231 row=import_result.row,
232 new=import_result.member.id.is_empty(),
233 )
234 # A new member has related resources that are not saved yet,
235 # so give them temporarily the same id as the member.
236 if (
237 upload_meta.new
238 and member_document.data.relationships.person.data
239 ):
240 member_document.data.relationships.person.data.id = (
241 member_document.data.id
242 )
243 for included in member_document.included:
244 if (
245 included.type == "persons"
246 and included.relationships.contact.data
247 ):
248 included.relationships.contact.data.id = (
249 member_document.data.id
250 )
251 if included.id == "0":
252 included.id = member_document.data.id
253 self._document.included |= member_document.included
254 uploaded_member = UploadedMemberResource(
255 id=member_document.data.id,
256 meta=upload_meta,
257 attributes=member_document.data.attributes,
258 relationships=member_document.data.relationships,
259 )
260 self._document.included.add(uploaded_member)
261 case FailureMemberImportResult():
262 self._document.data.attributes.errors.append(
263 MemberUploadError(
264 row=import_result.row,
265 message=import_result.to_message(),
266 )
267 )
268 return self