ffbb-data-client 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. ffbb_api_client_v3/__init__.py +25 -0
  2. ffbb_data_client/__init__.py +175 -0
  3. ffbb_data_client/clients/__init__.py +13 -0
  4. ffbb_data_client/clients/api_ffbb_app_client.py +2475 -0
  5. ffbb_data_client/clients/ffbb_data_client.py +2789 -0
  6. ffbb_data_client/clients/meilisearch_client.py +218 -0
  7. ffbb_data_client/clients/meilisearch_ffbb_client.py +647 -0
  8. ffbb_data_client/config.py +153 -0
  9. ffbb_data_client/data/__init__.py +25 -0
  10. ffbb_data_client/data/collections.json +1364 -0
  11. ffbb_data_client/data/endpoint_discovery.json +1875 -0
  12. ffbb_data_client/data/indexes.json +501 -0
  13. ffbb_data_client/data/openapi.json +35713 -0
  14. ffbb_data_client/data/openapi_full.json +37622 -0
  15. ffbb_data_client/helpers/__init__.py +27 -0
  16. ffbb_data_client/helpers/http_requests_helper.py +73 -0
  17. ffbb_data_client/helpers/http_requests_utils.py +502 -0
  18. ffbb_data_client/helpers/meilisearch_client_extension.py +153 -0
  19. ffbb_data_client/helpers/multi_search_query_helper.py +35 -0
  20. ffbb_data_client/models/__init__.py +241 -0
  21. ffbb_data_client/models/affiche.py +45 -0
  22. ffbb_data_client/models/cartographie.py +82 -0
  23. ffbb_data_client/models/categorie.py +55 -0
  24. ffbb_data_client/models/categorie_type.py +42 -0
  25. ffbb_data_client/models/clock.py +38 -0
  26. ffbb_data_client/models/club_contacts.py +77 -0
  27. ffbb_data_client/models/code.py +7 -0
  28. ffbb_data_client/models/commune.py +66 -0
  29. ffbb_data_client/models/competition_fields.py +309 -0
  30. ffbb_data_client/models/competition_id.py +116 -0
  31. ffbb_data_client/models/competition_id_categorie.py +31 -0
  32. ffbb_data_client/models/competition_id_sexe.py +31 -0
  33. ffbb_data_client/models/competition_id_type_competition.py +27 -0
  34. ffbb_data_client/models/competition_id_type_competition_generique.py +24 -0
  35. ffbb_data_client/models/competition_origine.py +69 -0
  36. ffbb_data_client/models/competition_origine_categorie.py +23 -0
  37. ffbb_data_client/models/competition_origine_type_competition.py +14 -0
  38. ffbb_data_client/models/competition_origine_type_competition_generique.py +24 -0
  39. ffbb_data_client/models/competition_type.py +6 -0
  40. ffbb_data_client/models/competitions_facet_distribution.py +65 -0
  41. ffbb_data_client/models/competitions_facet_stats.py +14 -0
  42. ffbb_data_client/models/competitions_hit.py +232 -0
  43. ffbb_data_client/models/competitions_multi_search_query.py +40 -0
  44. ffbb_data_client/models/competitions_query.py +11 -0
  45. ffbb_data_client/models/configuration_models.py +5 -0
  46. ffbb_data_client/models/contact_info.py +18 -0
  47. ffbb_data_client/models/content_multi_search_query.py +93 -0
  48. ffbb_data_client/models/coordonnees.py +27 -0
  49. ffbb_data_client/models/coordonnees_type.py +5 -0
  50. ffbb_data_client/models/document_flyer.py +205 -0
  51. ffbb_data_client/models/document_flyer_type.py +6 -0
  52. ffbb_data_client/models/engagement_contacts.py +97 -0
  53. ffbb_data_client/models/engagements_facet_distribution.py +59 -0
  54. ffbb_data_client/models/engagements_facet_stats.py +14 -0
  55. ffbb_data_client/models/engagements_hit.py +192 -0
  56. ffbb_data_client/models/engagements_multi_search_query.py +41 -0
  57. ffbb_data_client/models/etat.py +6 -0
  58. ffbb_data_client/models/external_competition_id.py +42 -0
  59. ffbb_data_client/models/external_id.py +72 -0
  60. ffbb_data_client/models/facet_distribution.py +13 -0
  61. ffbb_data_client/models/facet_stats.py +13 -0
  62. ffbb_data_client/models/field_set.py +10 -0
  63. ffbb_data_client/models/folder.py +35 -0
  64. ffbb_data_client/models/formation_session.py +60 -0
  65. ffbb_data_client/models/formations_facet_distribution.py +61 -0
  66. ffbb_data_client/models/formations_facet_stats.py +14 -0
  67. ffbb_data_client/models/formations_hit.py +277 -0
  68. ffbb_data_client/models/formations_multi_search_query.py +41 -0
  69. ffbb_data_client/models/game_stats_model.py +57 -0
  70. ffbb_data_client/models/game_stats_models.py +5 -0
  71. ffbb_data_client/models/generic_search.py +92 -0
  72. ffbb_data_client/models/geo.py +27 -0
  73. ffbb_data_client/models/geo_sort_order.py +6 -0
  74. ffbb_data_client/models/get_commune_response.py +18 -0
  75. ffbb_data_client/models/get_competition_response.py +523 -0
  76. ffbb_data_client/models/get_configuration_response.py +45 -0
  77. ffbb_data_client/models/get_engagement_response.py +23 -0
  78. ffbb_data_client/models/get_entraineur_response.py +18 -0
  79. ffbb_data_client/models/get_formation_response.py +28 -0
  80. ffbb_data_client/models/get_officiel_response.py +18 -0
  81. ffbb_data_client/models/get_organisme_response.py +476 -0
  82. ffbb_data_client/models/get_poule_response.py +68 -0
  83. ffbb_data_client/models/get_pratique_response.py +18 -0
  84. ffbb_data_client/models/get_rencontre_response.py +93 -0
  85. ffbb_data_client/models/get_saisons_response.py +56 -0
  86. ffbb_data_client/models/get_salle_response.py +20 -0
  87. ffbb_data_client/models/get_terrain_response.py +16 -0
  88. ffbb_data_client/models/get_tournoi_response.py +16 -0
  89. ffbb_data_client/models/gradient_color.py +27 -0
  90. ffbb_data_client/models/hit.py +16 -0
  91. ffbb_data_client/models/id_engagement_equipe.py +32 -0
  92. ffbb_data_client/models/id_organisme_equipe.py +51 -0
  93. ffbb_data_client/models/id_organisme_equipe1_logo.py +28 -0
  94. ffbb_data_client/models/id_poule.py +27 -0
  95. ffbb_data_client/models/jour.py +11 -0
  96. ffbb_data_client/models/label.py +15 -0
  97. ffbb_data_client/models/labellisation.py +30 -0
  98. ffbb_data_client/models/live.py +192 -0
  99. ffbb_data_client/models/lives.py +6 -0
  100. ffbb_data_client/models/logo.py +28 -0
  101. ffbb_data_client/models/multi_search_queries.py +24 -0
  102. ffbb_data_client/models/multi_search_query.py +96 -0
  103. ffbb_data_client/models/multi_search_result_competitions.py +14 -0
  104. ffbb_data_client/models/multi_search_result_engagements.py +14 -0
  105. ffbb_data_client/models/multi_search_result_formations.py +12 -0
  106. ffbb_data_client/models/multi_search_result_organismes.py +12 -0
  107. ffbb_data_client/models/multi_search_result_pratiques.py +12 -0
  108. ffbb_data_client/models/multi_search_result_rencontres.py +12 -0
  109. ffbb_data_client/models/multi_search_result_salles.py +12 -0
  110. ffbb_data_client/models/multi_search_result_terrains.py +12 -0
  111. ffbb_data_client/models/multi_search_result_tournois.py +12 -0
  112. ffbb_data_client/models/multi_search_results.py +103 -0
  113. ffbb_data_client/models/multi_search_results_class.py +96 -0
  114. ffbb_data_client/models/nature_sol.py +57 -0
  115. ffbb_data_client/models/niveau.py +10 -0
  116. ffbb_data_client/models/niveau_class.py +27 -0
  117. ffbb_data_client/models/niveau_extractor.py +214 -0
  118. ffbb_data_client/models/niveau_info.py +64 -0
  119. ffbb_data_client/models/niveau_models.py +14 -0
  120. ffbb_data_client/models/niveau_type.py +10 -0
  121. ffbb_data_client/models/objectif.py +7 -0
  122. ffbb_data_client/models/organisateur.py +197 -0
  123. ffbb_data_client/models/organisateur_type.py +6 -0
  124. ffbb_data_client/models/organisme_fields.py +327 -0
  125. ffbb_data_client/models/organisme_id_pere.py +177 -0
  126. ffbb_data_client/models/organismes_facet_distribution.py +46 -0
  127. ffbb_data_client/models/organismes_facet_stats.py +14 -0
  128. ffbb_data_client/models/organismes_hit.py +196 -0
  129. ffbb_data_client/models/organismes_multi_search_query.py +41 -0
  130. ffbb_data_client/models/organismes_query.py +8 -0
  131. ffbb_data_client/models/phase_code.py +23 -0
  132. ffbb_data_client/models/poule.py +35 -0
  133. ffbb_data_client/models/poule_fields.py +261 -0
  134. ffbb_data_client/models/poule_rencontre_item_model.py +69 -0
  135. ffbb_data_client/models/poules_models.py +6 -0
  136. ffbb_data_client/models/poules_query.py +9 -0
  137. ffbb_data_client/models/pratique.py +7 -0
  138. ffbb_data_client/models/pratiques_facet_distribution.py +29 -0
  139. ffbb_data_client/models/pratiques_facet_stats.py +14 -0
  140. ffbb_data_client/models/pratiques_hit.py +310 -0
  141. ffbb_data_client/models/pratiques_hit_type.py +9 -0
  142. ffbb_data_client/models/pratiques_multi_search_query.py +41 -0
  143. ffbb_data_client/models/pratiques_type_class.py +45 -0
  144. ffbb_data_client/models/publication_internet.py +6 -0
  145. ffbb_data_client/models/purple_logo.py +24 -0
  146. ffbb_data_client/models/query_fields_manager.py +75 -0
  147. ffbb_data_client/models/ranking_engagement.py +41 -0
  148. ffbb_data_client/models/rankings_models.py +6 -0
  149. ffbb_data_client/models/rencontres_engagement.py +23 -0
  150. ffbb_data_client/models/rencontres_facet_distribution.py +65 -0
  151. ffbb_data_client/models/rencontres_facet_stats.py +14 -0
  152. ffbb_data_client/models/rencontres_hit.py +271 -0
  153. ffbb_data_client/models/rencontres_multi_search_query.py +41 -0
  154. ffbb_data_client/models/saison.py +23 -0
  155. ffbb_data_client/models/saison_fields.py +36 -0
  156. ffbb_data_client/models/saisons_models.py +6 -0
  157. ffbb_data_client/models/saisons_query.py +9 -0
  158. ffbb_data_client/models/salle.py +56 -0
  159. ffbb_data_client/models/salles_facet_distribution.py +14 -0
  160. ffbb_data_client/models/salles_facet_stats.py +14 -0
  161. ffbb_data_client/models/salles_hit.py +153 -0
  162. ffbb_data_client/models/salles_multi_search_query.py +40 -0
  163. ffbb_data_client/models/sexe.py +9 -0
  164. ffbb_data_client/models/sexe_class.py +31 -0
  165. ffbb_data_client/models/source.py +5 -0
  166. ffbb_data_client/models/status.py +5 -0
  167. ffbb_data_client/models/team_engagement.py +56 -0
  168. ffbb_data_client/models/team_ranking.py +108 -0
  169. ffbb_data_client/models/terrains_categorie_championnat_3x3_libelle.py +5 -0
  170. ffbb_data_client/models/terrains_facet_distribution.py +41 -0
  171. ffbb_data_client/models/terrains_facet_stats.py +14 -0
  172. ffbb_data_client/models/terrains_hit.py +223 -0
  173. ffbb_data_client/models/terrains_multi_search_query.py +42 -0
  174. ffbb_data_client/models/terrains_name.py +5 -0
  175. ffbb_data_client/models/terrains_sexe_enum.py +7 -0
  176. ffbb_data_client/models/terrains_storage.py +5 -0
  177. ffbb_data_client/models/tournoi_type_class.py +35 -0
  178. ffbb_data_client/models/tournoi_type_enum.py +7 -0
  179. ffbb_data_client/models/tournoi_types_3x3.py +43 -0
  180. ffbb_data_client/models/tournoi_types_3x3_libelle.py +60 -0
  181. ffbb_data_client/models/tournoi_types_3x3_libelle_enum.py +10 -0
  182. ffbb_data_client/models/tournois_facet_distribution.py +41 -0
  183. ffbb_data_client/models/tournois_facet_stats.py +14 -0
  184. ffbb_data_client/models/tournois_hit.py +132 -0
  185. ffbb_data_client/models/tournois_hit_type.py +5 -0
  186. ffbb_data_client/models/tournois_libelle.py +7 -0
  187. ffbb_data_client/models/tournois_multi_search_query.py +40 -0
  188. ffbb_data_client/models/type_association.py +23 -0
  189. ffbb_data_client/models/type_association_libelle.py +30 -0
  190. ffbb_data_client/models/type_class.py +23 -0
  191. ffbb_data_client/models/type_competition.py +8 -0
  192. ffbb_data_client/models/type_competition_generique.py +31 -0
  193. ffbb_data_client/models/type_enum.py +7 -0
  194. ffbb_data_client/models/type_league.py +6 -0
  195. ffbb_data_client/py.typed +0 -0
  196. ffbb_data_client/utils/__init__.py +27 -0
  197. ffbb_data_client/utils/cache_manager.py +393 -0
  198. ffbb_data_client/utils/converter_utils.py +329 -0
  199. ffbb_data_client/utils/input_validation.py +360 -0
  200. ffbb_data_client/utils/retry_utils.py +478 -0
  201. ffbb_data_client/utils/secure_logging.py +153 -0
  202. ffbb_data_client/utils/token_manager.py +115 -0
  203. ffbb_data_client-2.0.0.dist-info/METADATA +339 -0
  204. ffbb_data_client-2.0.0.dist-info/RECORD +207 -0
  205. ffbb_data_client-2.0.0.dist-info/WHEEL +5 -0
  206. ffbb_data_client-2.0.0.dist-info/licenses/LICENSE.txt +201 -0
  207. ffbb_data_client-2.0.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,205 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from datetime import datetime
5
+ from typing import Any
6
+ from uuid import UUID
7
+
8
+ from ..utils.converter_utils import (
9
+ from_datetime,
10
+ from_enum,
11
+ from_float,
12
+ from_int,
13
+ from_list,
14
+ from_obj,
15
+ from_str,
16
+ from_uuid,
17
+ )
18
+ from .document_flyer_type import DocumentFlyerType
19
+ from .facet_stats import FacetStats
20
+ from .folder import Folder
21
+ from .source import Source
22
+
23
+
24
+ @dataclass
25
+ class DocumentFlyer:
26
+ id: UUID | None = None
27
+ storage: str | None = None
28
+ filename_disk: str | None = None
29
+ filename_download: str | None = None
30
+ title: str | None = None
31
+ type: DocumentFlyerType | None = None
32
+ uploaded_on: datetime | None = None
33
+ modified_on: datetime | None = None
34
+ charset: str | None = None
35
+ filesize: int | None = None
36
+ width: int | None = None
37
+ height: int | None = None
38
+ duration: int | None = None
39
+ embed: str | None = None
40
+ description: str | None = None
41
+ location: str | None = None
42
+ tags: str | None = None
43
+ metadata: FacetStats | None = None
44
+ source: Source | None = None
45
+ credits: str | None = None
46
+ gradient_color: str | None = None
47
+ md5: str | None = None
48
+ newsbridge_media_id: str | None = None
49
+ newsbridge_metadatas: str | None = None
50
+ newsbridge_name: str | None = None
51
+ newsbridge_recorded_at: datetime | None = None
52
+ focal_point_x: float | None = None
53
+ focal_point_y: float | None = None
54
+ newsbridge_labels: list[Any] | None = None
55
+ newsbridge_persons: list[Any] | None = None
56
+ folder: Folder | None = None
57
+ uploaded_by: UUID | None = None
58
+ modified_by: UUID | None = None
59
+ newsbridge_mission: str | None = None
60
+
61
+ @staticmethod
62
+ def from_dict(obj: Any) -> DocumentFlyer:
63
+ assert isinstance(obj, dict)
64
+ id = from_uuid(obj, "id")
65
+ storage = from_str(obj, "storage")
66
+ filename_disk = from_str(obj, "filename_disk")
67
+ filename_download = from_str(obj, "filename_download")
68
+ title = from_str(obj, "title")
69
+ type = from_enum(DocumentFlyerType, obj, "type")
70
+ uploaded_on = from_datetime(obj, "uploaded_on")
71
+ modified_on = from_datetime(obj, "modified_on")
72
+ charset = from_str(obj, "charset")
73
+ filesize = from_int(obj, "filesize")
74
+ width = from_int(obj, "width")
75
+ height = from_int(obj, "height")
76
+ duration = from_int(obj, "duration")
77
+ embed = from_str(obj, "embed")
78
+ description = from_str(obj, "description")
79
+ location = from_str(obj, "location")
80
+ tags = from_str(obj, "tags")
81
+ metadata = from_obj(FacetStats.from_dict, obj, "metadata")
82
+ source = from_enum(Source, obj, "source")
83
+ credits = from_str(obj, "credits")
84
+ gradient_color = from_str(obj, "gradient_color")
85
+ md5 = from_str(obj, "md5")
86
+ newsbridge_media_id = from_str(obj, "newsbridge_media_id")
87
+ newsbridge_metadatas = from_str(obj, "newsbridge_metadatas")
88
+ newsbridge_name = from_str(obj, "newsbridge_name")
89
+ newsbridge_recorded_at = from_datetime(obj, "newsbridge_recorded_at")
90
+ focal_point_x = from_float(obj, "focal_point_x")
91
+ focal_point_y = from_float(obj, "focal_point_y")
92
+ newsbridge_labels = from_list(lambda x: x, obj, "newsbridge_labels")
93
+ newsbridge_persons = from_list(lambda x: x, obj, "newsbridge_persons")
94
+ folder = from_obj(Folder.from_dict, obj, "folder")
95
+ uploaded_by = from_uuid(obj, "uploaded_by")
96
+ modified_by = from_uuid(obj, "modified_by")
97
+ newsbridge_mission = from_str(obj, "newsbridge_mission")
98
+ return DocumentFlyer(
99
+ id=id,
100
+ storage=storage,
101
+ filename_disk=filename_disk,
102
+ filename_download=filename_download,
103
+ title=title,
104
+ type=type,
105
+ uploaded_on=uploaded_on,
106
+ modified_on=modified_on,
107
+ charset=charset,
108
+ filesize=filesize,
109
+ width=width,
110
+ height=height,
111
+ duration=duration,
112
+ embed=embed,
113
+ description=description,
114
+ location=location,
115
+ tags=tags,
116
+ metadata=metadata,
117
+ source=source,
118
+ credits=credits,
119
+ gradient_color=gradient_color,
120
+ md5=md5,
121
+ newsbridge_media_id=newsbridge_media_id,
122
+ newsbridge_metadatas=newsbridge_metadatas,
123
+ newsbridge_name=newsbridge_name,
124
+ newsbridge_recorded_at=newsbridge_recorded_at,
125
+ focal_point_x=focal_point_x,
126
+ focal_point_y=focal_point_y,
127
+ newsbridge_labels=newsbridge_labels,
128
+ newsbridge_persons=newsbridge_persons,
129
+ folder=folder,
130
+ uploaded_by=uploaded_by,
131
+ modified_by=modified_by,
132
+ newsbridge_mission=newsbridge_mission,
133
+ )
134
+
135
+ def to_dict(self) -> dict:
136
+ result: dict = {}
137
+ if self.id is not None:
138
+ result["id"] = str(self.id)
139
+ if self.storage is not None:
140
+ result["storage"] = self.storage
141
+ if self.filename_disk is not None:
142
+ result["filename_disk"] = self.filename_disk
143
+ if self.filename_download is not None:
144
+ result["filename_download"] = self.filename_download
145
+ if self.title is not None:
146
+ result["title"] = self.title
147
+ if self.type is not None:
148
+ result["type"] = self.type.value
149
+ if self.uploaded_on is not None:
150
+ result["uploaded_on"] = self.uploaded_on.isoformat()
151
+ if self.modified_on is not None:
152
+ result["modified_on"] = self.modified_on.isoformat()
153
+ if self.filesize is not None:
154
+ result["filesize"] = str(self.filesize)
155
+ if self.width is not None:
156
+ result["width"] = self.width
157
+ if self.height is not None:
158
+ result["height"] = self.height
159
+ if self.metadata is not None:
160
+ result["metadata"] = self.metadata.to_dict()
161
+ if self.source is not None:
162
+ result["source"] = self.source.value
163
+ if self.gradient_color is not None:
164
+ result["gradient_color"] = self.gradient_color
165
+ if self.md5 is not None:
166
+ result["md5"] = self.md5
167
+ if self.newsbridge_labels is not None:
168
+ result["newsbridge_labels"] = self.newsbridge_labels
169
+ if self.newsbridge_persons is not None:
170
+ result["newsbridge_persons"] = self.newsbridge_persons
171
+ if self.folder is not None:
172
+ result["folder"] = self.folder.to_dict()
173
+ if self.charset is not None:
174
+ result["charset"] = self.charset
175
+ if self.duration is not None:
176
+ result["duration"] = self.duration
177
+ if self.embed is not None:
178
+ result["embed"] = self.embed
179
+ if self.description is not None:
180
+ result["description"] = self.description
181
+ if self.location is not None:
182
+ result["location"] = self.location
183
+ if self.tags is not None:
184
+ result["tags"] = self.tags
185
+ if self.credits is not None:
186
+ result["credits"] = self.credits
187
+ if self.newsbridge_media_id is not None:
188
+ result["newsbridge_media_id"] = self.newsbridge_media_id
189
+ if self.newsbridge_metadatas is not None:
190
+ result["newsbridge_metadatas"] = self.newsbridge_metadatas
191
+ if self.newsbridge_name is not None:
192
+ result["newsbridge_name"] = self.newsbridge_name
193
+ if self.newsbridge_recorded_at is not None:
194
+ result["newsbridge_recorded_at"] = self.newsbridge_recorded_at.isoformat()
195
+ if self.focal_point_x is not None:
196
+ result["focal_point_x"] = self.focal_point_x
197
+ if self.focal_point_y is not None:
198
+ result["focal_point_y"] = self.focal_point_y
199
+ if self.uploaded_by is not None:
200
+ result["uploaded_by"] = str(self.uploaded_by)
201
+ if self.modified_by is not None:
202
+ result["modified_by"] = str(self.modified_by)
203
+ if self.newsbridge_mission is not None:
204
+ result["newsbridge_mission"] = self.newsbridge_mission
205
+ return result
@@ -0,0 +1,6 @@
1
+ from enum import Enum
2
+
3
+
4
+ class DocumentFlyerType(Enum):
5
+ IMAGE_JPEG = "image/jpeg"
6
+ IMAGE_PNG = "image/png"
@@ -0,0 +1,97 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from .contact_info import ContactInfo
6
+ from .get_engagement_response import GetEngagementResponse
7
+ from .get_entraineur_response import GetEntraineurResponse
8
+
9
+
10
+ def _normalize_phone(raw: str | None) -> str | None:
11
+ if not raw:
12
+ return None
13
+ return str(raw).strip()
14
+
15
+
16
+ def _sanitize_name(name: str | None) -> str | None:
17
+ return name.strip().title() if name else None
18
+
19
+
20
+ @dataclass
21
+ class EngagementContacts:
22
+ """Contacts for an engagement (team): correspondant + coaches."""
23
+
24
+ engagement: GetEngagementResponse
25
+ correspondant: ContactInfo | None
26
+ entraineur: ContactInfo | None
27
+ entraineur_adjoint: ContactInfo | None
28
+
29
+
30
+ def extract_correspondant(engagement: GetEngagementResponse) -> ContactInfo | None:
31
+ """Extract correspondant contact from an engagement.
32
+
33
+ This is defensive: the engagement model in V3 may or may not include
34
+ correspondent phone/email fields. We try common attribute names and return
35
+ None if nothing usable is found.
36
+ """
37
+ phone = _normalize_phone(
38
+ getattr(engagement, "telephonePortableCorrespondantEquipe", None)
39
+ or getattr(engagement, "telephone_portable_correspondant_equipe", None)
40
+ or getattr(engagement, "telephoneFixeCorrespondantEquipe", None)
41
+ or getattr(engagement, "telephone_fixe_correspondant_equipe", None)
42
+ or getattr(engagement, "telephone", None)
43
+ )
44
+ email = (
45
+ getattr(engagement, "emailCorrespondantEquipe", None)
46
+ or getattr(engagement, "email_correspondant_equipe", None)
47
+ or getattr(engagement, "email", None)
48
+ )
49
+ if not phone and not email:
50
+ return None
51
+ return ContactInfo(
52
+ nom=_sanitize_name(
53
+ getattr(engagement, "nomCorrespondantEquipe", None)
54
+ or getattr(engagement, "nom", None)
55
+ ),
56
+ prenom=_sanitize_name(
57
+ getattr(engagement, "prenomCorrespondantEquipe", None)
58
+ or getattr(engagement, "prenom", None)
59
+ ),
60
+ telephone=phone,
61
+ email=email,
62
+ role="correspondant",
63
+ )
64
+
65
+
66
+ def extract_entraineur_contact(
67
+ entraineur: GetEntraineurResponse | None, titre: str | None
68
+ ) -> ContactInfo | None:
69
+ """Extract contact info from an entraineur object.
70
+
71
+ If phone/email are missing we still return a name-only ContactInfo when
72
+ at least a name is present so callers can display a best-effort contact.
73
+ """
74
+ if not entraineur:
75
+ return None
76
+ phone = _normalize_phone(
77
+ getattr(entraineur, "telephonePortable", None)
78
+ or getattr(entraineur, "telephone_portable", None)
79
+ or getattr(entraineur, "telephoneDomicile", None)
80
+ or getattr(entraineur, "telephone_travail", None)
81
+ or getattr(entraineur, "telephone", None)
82
+ )
83
+ email = getattr(entraineur, "email", None) or None
84
+ if (
85
+ not phone
86
+ and not email
87
+ and not getattr(entraineur, "nom", None)
88
+ and not getattr(entraineur, "prenom", None)
89
+ ):
90
+ return None
91
+ return ContactInfo(
92
+ nom=_sanitize_name(getattr(entraineur, "nom", None)),
93
+ prenom=_sanitize_name(getattr(entraineur, "prenom", None)),
94
+ telephone=phone,
95
+ email=email,
96
+ role=titre,
97
+ )
@@ -0,0 +1,59 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any
5
+
6
+ from .facet_distribution import FacetDistribution
7
+
8
+
9
+ @dataclass
10
+ class EngagementsFacetDistribution(FacetDistribution):
11
+ club_pro: dict[str, int] | None = None
12
+ id_competition_categorie_code: dict[str, int] | None = None
13
+ id_competition_categorie_libelle: dict[str, int] | None = None
14
+ id_competition_code: dict[str, int] | None = None
15
+ id_competition_nom: dict[str, int] | None = None
16
+ id_competition_sexe: dict[str, int] | None = None
17
+ id_poule_nom: dict[str, int] | None = None
18
+ niveau_code: dict[str, int] | None = None
19
+ niveau_libelle: dict[str, int] | None = None
20
+
21
+ @staticmethod
22
+ def from_dict(obj: Any) -> EngagementsFacetDistribution:
23
+ if not isinstance(obj, dict):
24
+ raise TypeError(f"Expected dict, got {obj.__class__.__name__}")
25
+ return EngagementsFacetDistribution(
26
+ club_pro=obj.get("clubPro"),
27
+ id_competition_categorie_code=obj.get("idCompetition.categorie.code"),
28
+ id_competition_categorie_libelle=obj.get("idCompetition.categorie.libelle"),
29
+ id_competition_code=obj.get("idCompetition.code"),
30
+ id_competition_nom=obj.get("idCompetition.nom"),
31
+ id_competition_sexe=obj.get("idCompetition.sexe"),
32
+ id_poule_nom=obj.get("idPoule.nom"),
33
+ niveau_code=obj.get("niveau.code"),
34
+ niveau_libelle=obj.get("niveau.libelle"),
35
+ )
36
+
37
+ def to_dict(self) -> dict:
38
+ result: dict = {}
39
+ if self.club_pro is not None:
40
+ result["clubPro"] = self.club_pro
41
+ if self.id_competition_categorie_code is not None:
42
+ result["idCompetition.categorie.code"] = self.id_competition_categorie_code
43
+ if self.id_competition_categorie_libelle is not None:
44
+ result["idCompetition.categorie.libelle"] = (
45
+ self.id_competition_categorie_libelle
46
+ )
47
+ if self.id_competition_code is not None:
48
+ result["idCompetition.code"] = self.id_competition_code
49
+ if self.id_competition_nom is not None:
50
+ result["idCompetition.nom"] = self.id_competition_nom
51
+ if self.id_competition_sexe is not None:
52
+ result["idCompetition.sexe"] = self.id_competition_sexe
53
+ if self.id_poule_nom is not None:
54
+ result["idPoule.nom"] = self.id_poule_nom
55
+ if self.niveau_code is not None:
56
+ result["niveau.code"] = self.niveau_code
57
+ if self.niveau_libelle is not None:
58
+ result["niveau.libelle"] = self.niveau_libelle
59
+ return result
@@ -0,0 +1,14 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from .facet_stats import FacetStats
6
+
7
+
8
+ class EngagementsFacetStats(FacetStats):
9
+ @staticmethod
10
+ def from_dict(obj: Any) -> EngagementsFacetStats:
11
+ return EngagementsFacetStats()
12
+
13
+ def to_dict(self) -> dict:
14
+ return super().to_dict()
@@ -0,0 +1,192 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any
5
+
6
+ from ..utils.converter_utils import (
7
+ from_bool,
8
+ from_obj,
9
+ from_str,
10
+ )
11
+ from .categorie import Categorie
12
+ from .competition_origine import CompetitionOrigine
13
+ from .geo import Geo
14
+ from .hit import Hit
15
+ from .poule import Poule
16
+
17
+
18
+ @dataclass
19
+ class EngagementsHit(Hit):
20
+ id: str | None = None
21
+ nom: str | None = None
22
+ age: str | None = None
23
+ sexe: str | None = None
24
+ club_pro: bool | None = None
25
+ code_abrege: str | None = None
26
+ code_club: str | None = None
27
+ code_comite: str | None = None
28
+ code_ligue: str | None = None
29
+ competitions_url: str | None = None
30
+ id_competition: CompetitionOrigine | None = None
31
+ id_poule: Poule | None = None
32
+ logo: str | None = None
33
+ niveau: Categorie | None = None
34
+ categorie: Categorie | None = None
35
+ nom_club: str | None = None
36
+ nom_club_pro: str | None = None
37
+ nom_comite: str | None = None
38
+ nom_ctc: str | None = None
39
+ nom_equipe: str | None = None
40
+ nom_ligue: str | None = None
41
+ nom_officiel: str | None = None
42
+ nom_organisme: str | None = None
43
+ nom_usuel: str | None = None
44
+ numero_equipe: str | None = None
45
+ thumbnail: str | None = None
46
+ gradient_color: str | None = None
47
+ geo: Geo | None = None
48
+
49
+ lower_nom: str | None = field(init=False, default=None, repr=False)
50
+ lower_nom_club: str | None = field(init=False, default=None, repr=False)
51
+ lower_nom_equipe: str | None = field(init=False, default=None, repr=False)
52
+ lower_nom_organisme: str | None = field(init=False, default=None, repr=False)
53
+
54
+ def __post_init__(self) -> None:
55
+ self.lower_nom = self.nom.lower() if self.nom else None
56
+ self.lower_nom_club = self.nom_club.lower() if self.nom_club else None
57
+ self.lower_nom_equipe = self.nom_equipe.lower() if self.nom_equipe else None
58
+ self.lower_nom_organisme = (
59
+ self.nom_organisme.lower() if self.nom_organisme else None
60
+ )
61
+
62
+ @staticmethod
63
+ def from_dict(obj: Any) -> EngagementsHit:
64
+ if not isinstance(obj, dict):
65
+ raise TypeError(f"Expected dict, got {obj.__class__.__name__}")
66
+ id = from_str(obj, "id")
67
+ nom = from_str(obj, "nom")
68
+ age = from_str(obj, "age")
69
+ sexe = from_str(obj, "sexe")
70
+ club_pro = from_bool(obj, "clubPro")
71
+ code_abrege = from_str(obj, "codeAbrege")
72
+ code_club = from_str(obj, "codeClub")
73
+ code_comite = from_str(obj, "codeComite")
74
+ code_ligue = from_str(obj, "codeLigue")
75
+ competitions_url = from_str(obj, "competitionsUrl")
76
+ id_competition = from_obj(CompetitionOrigine.from_dict, obj, "idCompetition")
77
+ id_poule = from_obj(Poule.from_dict, obj, "idPoule")
78
+ logo = from_str(obj, "logo")
79
+ niveau = from_obj(Categorie.from_dict, obj, "niveau")
80
+ categorie = from_obj(Categorie.from_dict, obj, "categorie")
81
+ nom_club = from_str(obj, "nomClub")
82
+ nom_club_pro = from_str(obj, "nomClubPro")
83
+ nom_comite = from_str(obj, "nomComite")
84
+ nom_ctc = from_str(obj, "nomCtc")
85
+ nom_equipe = from_str(obj, "nomEquipe")
86
+ nom_ligue = from_str(obj, "nomLigue")
87
+ nom_officiel = from_str(obj, "nomOfficiel")
88
+ nom_organisme = from_str(obj, "nomOrganisme")
89
+ nom_usuel = from_str(obj, "nomUsuel")
90
+ numero_equipe = from_str(obj, "numeroEquipe")
91
+ thumbnail = from_str(obj, "thumbnail")
92
+ gradient_color = from_str(obj, "gradient_color")
93
+ geo = from_obj(Geo.from_dict, obj, "_geo")
94
+ return EngagementsHit(
95
+ id=id,
96
+ nom=nom,
97
+ age=age,
98
+ sexe=sexe,
99
+ club_pro=club_pro,
100
+ code_abrege=code_abrege,
101
+ code_club=code_club,
102
+ code_comite=code_comite,
103
+ code_ligue=code_ligue,
104
+ competitions_url=competitions_url,
105
+ id_competition=id_competition,
106
+ id_poule=id_poule,
107
+ logo=logo,
108
+ niveau=niveau,
109
+ categorie=categorie,
110
+ nom_club=nom_club,
111
+ nom_club_pro=nom_club_pro,
112
+ nom_comite=nom_comite,
113
+ nom_ctc=nom_ctc,
114
+ nom_equipe=nom_equipe,
115
+ nom_ligue=nom_ligue,
116
+ nom_officiel=nom_officiel,
117
+ nom_organisme=nom_organisme,
118
+ nom_usuel=nom_usuel,
119
+ numero_equipe=numero_equipe,
120
+ thumbnail=thumbnail,
121
+ gradient_color=gradient_color,
122
+ geo=geo,
123
+ )
124
+
125
+ def to_dict(self) -> dict:
126
+ result: dict = {}
127
+ if self.id is not None:
128
+ result["id"] = self.id
129
+ if self.nom is not None:
130
+ result["nom"] = self.nom
131
+ if self.age is not None:
132
+ result["age"] = self.age
133
+ if self.sexe is not None:
134
+ result["sexe"] = self.sexe
135
+ if self.club_pro is not None:
136
+ result["clubPro"] = self.club_pro
137
+ if self.code_abrege is not None:
138
+ result["codeAbrege"] = self.code_abrege
139
+ if self.code_club is not None:
140
+ result["codeClub"] = self.code_club
141
+ if self.code_comite is not None:
142
+ result["codeComite"] = self.code_comite
143
+ if self.code_ligue is not None:
144
+ result["codeLigue"] = self.code_ligue
145
+ if self.competitions_url is not None:
146
+ result["competitionsUrl"] = self.competitions_url
147
+ if self.id_competition is not None:
148
+ result["idCompetition"] = self.id_competition.to_dict()
149
+ if self.id_poule is not None:
150
+ result["idPoule"] = self.id_poule.to_dict()
151
+ if self.logo is not None:
152
+ result["logo"] = self.logo
153
+ if self.niveau is not None:
154
+ result["niveau"] = self.niveau.to_dict()
155
+ if self.categorie is not None:
156
+ result["categorie"] = self.categorie.to_dict()
157
+ if self.nom_club is not None:
158
+ result["nomClub"] = self.nom_club
159
+ if self.nom_club_pro is not None:
160
+ result["nomClubPro"] = self.nom_club_pro
161
+ if self.nom_comite is not None:
162
+ result["nomComite"] = self.nom_comite
163
+ if self.nom_ctc is not None:
164
+ result["nomCtc"] = self.nom_ctc
165
+ if self.nom_equipe is not None:
166
+ result["nomEquipe"] = self.nom_equipe
167
+ if self.nom_ligue is not None:
168
+ result["nomLigue"] = self.nom_ligue
169
+ if self.nom_officiel is not None:
170
+ result["nomOfficiel"] = self.nom_officiel
171
+ if self.nom_organisme is not None:
172
+ result["nomOrganisme"] = self.nom_organisme
173
+ if self.nom_usuel is not None:
174
+ result["nomUsuel"] = self.nom_usuel
175
+ if self.numero_equipe is not None:
176
+ result["numeroEquipe"] = str(self.numero_equipe)
177
+ if self.thumbnail is not None:
178
+ result["thumbnail"] = self.thumbnail
179
+ if self.gradient_color is not None:
180
+ result["gradient_color"] = self.gradient_color
181
+ if self.geo is not None:
182
+ result["_geo"] = self.geo.to_dict()
183
+ return result
184
+
185
+ def is_valid_for_query(self, query: str) -> bool:
186
+ return bool(
187
+ not query
188
+ or (self.lower_nom and query in self.lower_nom)
189
+ or (self.lower_nom_club and query in self.lower_nom_club)
190
+ or (self.lower_nom_equipe and query in self.lower_nom_equipe)
191
+ or (self.lower_nom_organisme and query in self.lower_nom_organisme)
192
+ )
@@ -0,0 +1,41 @@
1
+ from __future__ import annotations
2
+
3
+ from ..config import MEILISEARCH_FACETS_ENGAGEMENTS, MEILISEARCH_INDEX_ENGAGEMENTS
4
+ from .engagements_facet_distribution import EngagementsFacetDistribution
5
+ from .engagements_facet_stats import EngagementsFacetStats
6
+ from .multi_search_query import MultiSearchQuery
7
+ from .multi_search_result_engagements import EngagementsMultiSearchResult
8
+ from .multi_search_results import MultiSearchResult
9
+
10
+
11
+ class EngagementsMultiSearchQuery(MultiSearchQuery):
12
+ def __init__(
13
+ self,
14
+ q: str | None,
15
+ limit: int | None = 10,
16
+ offset: int | None = 0,
17
+ filter: list[str] | None = None,
18
+ sort: list[str] | None = None,
19
+ ):
20
+ super().__init__(
21
+ index_uid=MEILISEARCH_INDEX_ENGAGEMENTS,
22
+ q=q,
23
+ facets=MEILISEARCH_FACETS_ENGAGEMENTS,
24
+ limit=limit,
25
+ offset=offset,
26
+ filter=filter,
27
+ sort=sort,
28
+ )
29
+
30
+ def is_valid_result(self, result: MultiSearchResult):
31
+ return result and (
32
+ isinstance(result, EngagementsMultiSearchResult)
33
+ and (
34
+ result.facet_distribution is None
35
+ or isinstance(result.facet_distribution, EngagementsFacetDistribution)
36
+ )
37
+ and (
38
+ result.facet_stats is None
39
+ or isinstance(result.facet_stats, EngagementsFacetStats)
40
+ )
41
+ )
@@ -0,0 +1,6 @@
1
+ from enum import Enum
2
+
3
+
4
+ class Etat(Enum):
5
+ A = "A"
6
+ D = "D"
@@ -0,0 +1,42 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any
5
+
6
+ from ..utils.converter_utils import (
7
+ from_str,
8
+ )
9
+
10
+
11
+ @dataclass
12
+ class ExternalCompetitionID:
13
+ code: str | None = None
14
+ nom: str | None = None
15
+ sexe: str | None = None
16
+ type_competition: str | None = None
17
+
18
+ @staticmethod
19
+ def from_dict(obj: Any) -> ExternalCompetitionID:
20
+ assert isinstance(obj, dict)
21
+ code = from_str(obj, "code")
22
+ nom = from_str(obj, "nom")
23
+ sexe = from_str(obj, "sexe")
24
+ type_competition = from_str(obj, "typeCompetition")
25
+ return ExternalCompetitionID(
26
+ code=code,
27
+ nom=nom,
28
+ sexe=sexe,
29
+ type_competition=type_competition,
30
+ )
31
+
32
+ def to_dict(self) -> dict:
33
+ result: dict = {}
34
+ if self.code is not None:
35
+ result["code"] = self.code
36
+ if self.nom is not None:
37
+ result["nom"] = self.nom
38
+ if self.sexe is not None:
39
+ result["sexe"] = self.sexe
40
+ if self.type_competition is not None:
41
+ result["typeCompetition"] = self.type_competition
42
+ return result