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, result: IterableResult[MemberEntity]) -> Self:
174 self._document = MembersDocument(
175 meta=Meta(count=result.count, offset=result.offset, limit=result.limit),
176 )
177 async for member in result.iterator:
178 member_document = JsonApiMemberPresenter().present(member).get_document()
179 self._document.data.append(member_document.data)
180 self._document.included |= member_document.included
181 return self
184class JsonApiUploadMemberPresenter(
185 JsonApiPresenter[MemberUploadDocument],
186 AsyncPresenter[IterableResult[MemberImportResult]],
187):
188 """A presenter that transform a file upload of a member into a JSON:API document."""
190 def __init__(self, preview: bool = False):
191 super().__init__()
192 self._preview = preview
194 async def present(self, result: IterableResult[MemberImportResult]) -> Self:
195 async for import_result in result.iterator:
196 if self._document is None:
197 self._document = MemberUploadDocument(
198 data=MemberUploadResource(
199 id=str(import_result.file_upload.uuid),
200 attributes=MemberUploadAttributes(
201 filename=import_result.file_upload.filename,
202 preview=self._preview,
203 remark=import_result.file_upload.remark,
204 errors=[],
205 ),
206 relationships=MemberUploadRelationships(
207 members=RelationshipList[MemberResourceIdentifier]()
208 ),
209 )
210 )
212 match import_result:
213 case OkMemberImportResult():
214 member_document = (
215 JsonApiMemberPresenter()
216 .present(import_result.member)
217 .get_document()
218 )
219 self._document.data.relationships.members.data.append(
220 MemberResourceIdentifier(id=member_document.data.id)
221 )
223 upload_meta = UploadedMemberMeta(
224 **member_document.data.meta.model_dump(),
225 row=import_result.row,
226 new=import_result.member.id.is_empty(),
227 )
228 # A new member has related resources that are not saved yet,
229 # so give them temporarily the same id as the member.
230 if (
231 upload_meta.new
232 and member_document.data.relationships.person.data
233 ):
234 member_document.data.relationships.person.data.id = (
235 member_document.data.id
236 )
237 for included in member_document.included:
238 if (
239 included.type == "persons"
240 and included.relationships.contact.data
241 ):
242 included.relationships.contact.data.id = (
243 member_document.data.id
244 )
245 if included.id == "0":
246 included.id = member_document.data.id
247 self._document.included |= member_document.included
248 uploaded_member = UploadedMemberResource(
249 id=member_document.data.id,
250 meta=upload_meta,
251 attributes=member_document.data.attributes,
252 relationships=member_document.data.relationships,
253 )
254 self._document.included.add(uploaded_member)
255 case FailureMemberImportResult():
256 self._document.data.attributes.errors.append(
257 MemberUploadError(
258 row=import_result.row,
259 message=import_result.to_message(),
260 )
261 )
262 return self