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,2475 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ import httpx
6
+ from httpx import Client
7
+ from pydantic import TypeAdapter
8
+
9
+ from ..config import (
10
+ API_FFBB_BASE_URL,
11
+ DEFAULT_USER_AGENT,
12
+ ENDPOINT_COMMUNES,
13
+ ENDPOINT_COMPETITIONS,
14
+ ENDPOINT_CONFIGURATION,
15
+ ENDPOINT_EDF_MATCHES,
16
+ ENDPOINT_EDF_PLAYERS,
17
+ ENDPOINT_EDF_ROSTERS,
18
+ ENDPOINT_EDF_TEAMS,
19
+ ENDPOINT_ENGAGEMENTS,
20
+ ENDPOINT_ENTRAINEURS,
21
+ ENDPOINT_FORMATIONS,
22
+ ENDPOINT_GENIUS_SPORT_MATCHES,
23
+ ENDPOINT_GENIUS_SPORTS_LIVE_LOGS,
24
+ ENDPOINT_LIVES,
25
+ ENDPOINT_OFFICIELS,
26
+ ENDPOINT_OPENAPI,
27
+ ENDPOINT_ORGANISMES,
28
+ ENDPOINT_POULES,
29
+ ENDPOINT_PRATIQUES,
30
+ ENDPOINT_REMATCH_VIDEOS,
31
+ ENDPOINT_RENCONTRES,
32
+ ENDPOINT_SAISONS,
33
+ ENDPOINT_SALLES,
34
+ ENDPOINT_SESSIONS,
35
+ ENDPOINT_TERRAINS,
36
+ ENDPOINT_TOURNOIS,
37
+ )
38
+ from ..helpers.http_requests_helper import catch_result
39
+ from ..helpers.http_requests_utils import (
40
+ http_get_json,
41
+ http_get_json_async,
42
+ url_with_params,
43
+ )
44
+ from ..models.configuration_models import GetConfigurationResponse
45
+ from ..models.field_set import FieldSet
46
+ from ..models.get_commune_response import GetCommuneResponse
47
+ from ..models.get_competition_response import GetCompetitionResponse
48
+ from ..models.get_engagement_response import GetEngagementResponse
49
+ from ..models.get_entraineur_response import GetEntraineurResponse
50
+ from ..models.get_formation_response import GetFormationResponse
51
+ from ..models.get_officiel_response import GetOfficielResponse
52
+ from ..models.get_organisme_response import GetOrganismeResponse
53
+ from ..models.get_pratique_response import GetPratiqueResponse
54
+ from ..models.get_rencontre_response import GetRencontreResponse
55
+ from ..models.get_salle_response import GetSalleResponse
56
+ from ..models.get_terrain_response import GetTerrainResponse
57
+ from ..models.get_tournoi_response import GetTournoiResponse
58
+ from ..models.lives import Live
59
+ from ..models.poules_models import GetPouleResponse
60
+ from ..models.query_fields_manager import QueryFieldsManager
61
+ from ..models.saisons_models import GetSaisonsResponse
62
+ from ..models.team_ranking import TeamRanking
63
+ from ..utils.cache_manager import CacheConfig, CacheManager
64
+ from ..utils.retry_utils import (
65
+ RetryConfig,
66
+ TimeoutConfig,
67
+ get_default_retry_config,
68
+ get_default_timeout_config,
69
+ )
70
+ from ..utils.secure_logging import get_secure_logger, mask_token
71
+
72
+
73
+ class ApiFFBBAppClient:
74
+ url: str = ""
75
+ debug: bool = False
76
+ headers: dict[str, str] = {}
77
+ cached_session: Client | None = None
78
+ async_cached_session: httpx.AsyncClient | None = None
79
+ retry_config: RetryConfig | None = None
80
+ timeout_config: TimeoutConfig | None = None
81
+
82
+ def __init__(
83
+ self,
84
+ bearer_token: str,
85
+ url: str = API_FFBB_BASE_URL,
86
+ debug: bool = False,
87
+ cached_session: Client | None = None,
88
+ async_cached_session: httpx.AsyncClient | None = None,
89
+ *,
90
+ retry_config: RetryConfig | None = None,
91
+ timeout_config: TimeoutConfig | None = None,
92
+ cache_config: CacheConfig | None = None,
93
+ ):
94
+ """
95
+ Initializes an instance of the ApiFFBBAppClient class.
96
+
97
+ Args:
98
+ bearer_token (str): The bearer token used for authentication.
99
+ url (str, optional): The base URL. Defaults to "https://api.ffbb.app/".
100
+ debug (bool, optional): Whether to enable debug mode. Defaults to False.
101
+ cached_session (Client, optional): The cached session to use.
102
+ retry_config (RetryConfig, optional): Retry configuration. Defaults to None.
103
+ timeout_config (TimeoutConfig, optional): Timeout configuration.
104
+ Defaults to None.
105
+ cache_config (CacheConfig, optional): Cache configuration. Defaults to None.
106
+ """
107
+ if not bearer_token or not bearer_token.strip():
108
+ raise ValueError("bearer_token cannot be None, empty, or whitespace-only")
109
+
110
+ # Store token securely (private attribute)
111
+ self._bearer_token = bearer_token
112
+ self.url = url
113
+ self.debug = debug
114
+ self.cached_session = cached_session
115
+ self.headers = {
116
+ "Authorization": f"Bearer {self._bearer_token}",
117
+ "user-agent": DEFAULT_USER_AGENT,
118
+ }
119
+
120
+ # Configure retry and timeout settings
121
+ self.retry_config = retry_config or get_default_retry_config()
122
+ self.timeout_config = timeout_config or get_default_timeout_config()
123
+
124
+ # Configure cache manager
125
+ self.cache_manager = CacheManager(cache_config)
126
+
127
+ if cached_session is None:
128
+ self.cached_session = self.cache_manager.session
129
+ else:
130
+ self.cached_session = cached_session
131
+
132
+ if async_cached_session is None:
133
+ self.async_cached_session = self.cache_manager.async_session
134
+ else:
135
+ self.async_cached_session = async_cached_session
136
+
137
+ # Initialize secure logger
138
+ self.logger = get_secure_logger(f"{self.__class__.__name__}")
139
+
140
+ # Log initialization with masked token
141
+ masked_token = mask_token(self._bearer_token)
142
+ if self.debug:
143
+ self.logger.info(f"ApiFFBBAppClient initialized with token: {masked_token}")
144
+ self.logger.info(
145
+ f"Retry config: {self.retry_config.max_attempts} attempts, "
146
+ f"timeout: {self.timeout_config.total_timeout}s"
147
+ )
148
+ else:
149
+ self.logger.info("ApiFFBBAppClient initialized successfully")
150
+
151
+ @property
152
+ def bearer_token(self) -> str:
153
+ """Get the bearer token."""
154
+ return self._bearer_token
155
+
156
+ def get_organisme_for_search(
157
+ self,
158
+ organisme_id: int,
159
+ cached_session: Client | None = None,
160
+ ) -> GetOrganismeResponse | None:
161
+ """Version allégée de get_organisme() pour les contextes de recherche.
162
+ Retourne 31 champs au lieu de 77 (exclut membres, labellisation, salle).
163
+ """
164
+ return self.get_organisme(
165
+ organisme_id=organisme_id,
166
+ fields=QueryFieldsManager.get_organisme_search_fields(),
167
+ cached_session=cached_session,
168
+ )
169
+
170
+ async def get_organisme_for_search_async(
171
+ self,
172
+ organisme_id: int,
173
+ cached_session: httpx.AsyncClient | None = None,
174
+ ) -> GetOrganismeResponse | None:
175
+ """Version async allégée de get_organisme() pour les contextes de recherche."""
176
+ return await self.get_organisme_async(
177
+ organisme_id=organisme_id,
178
+ fields=QueryFieldsManager.get_organisme_search_fields(),
179
+ cached_session=cached_session,
180
+ )
181
+
182
+ def _get_directus_item(
183
+ self,
184
+ endpoint: str,
185
+ id: str | int,
186
+ fields: list[str] | None = None,
187
+ cached_session: Client | None = None,
188
+ ) -> dict[str, Any] | None:
189
+ url = f"{self.url}{endpoint}/{id}"
190
+ params: dict[str, Any] = {}
191
+ if fields:
192
+ params["fields[]"] = fields
193
+ final_url = url_with_params(url, params) if params else url
194
+ data = catch_result(
195
+ lambda: http_get_json(
196
+ final_url,
197
+ self.headers,
198
+ debug=self.debug,
199
+ cached_session=cached_session or self.cached_session,
200
+ retry_config=self.retry_config,
201
+ timeout_config=self.timeout_config,
202
+ )
203
+ )
204
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
205
+ return actual_data if isinstance(actual_data, dict) else None
206
+
207
+ async def _get_directus_item_async(
208
+ self,
209
+ endpoint: str,
210
+ id: str | int,
211
+ fields: list[str] | None = None,
212
+ cached_session: httpx.AsyncClient | None = None,
213
+ ) -> dict[str, Any] | None:
214
+ url = f"{self.url}{endpoint}/{id}"
215
+ params: dict[str, Any] = {}
216
+ if fields:
217
+ params["fields[]"] = fields
218
+ final_url = url_with_params(url, params) if params else url
219
+ try:
220
+ data = await http_get_json_async(
221
+ final_url,
222
+ self.headers,
223
+ debug=self.debug,
224
+ cached_session=cached_session or self.async_cached_session,
225
+ retry_config=self.retry_config,
226
+ timeout_config=self.timeout_config,
227
+ )
228
+ except Exception as e:
229
+ if self.debug:
230
+ self.logger.error(f"Error in _get_directus_item_async: {e}")
231
+ return None
232
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
233
+ return actual_data if isinstance(actual_data, dict) else None
234
+
235
+ def _list_directus_items(
236
+ self,
237
+ endpoint: str,
238
+ limit: int = 10,
239
+ fields: list[str] | None = None,
240
+ filter_criteria: str | None = None,
241
+ sort: str | list[str] | None = None,
242
+ offset: int | None = None,
243
+ search: str | None = None,
244
+ cached_session: Client | None = None,
245
+ ) -> list[dict[str, Any]]:
246
+ url = f"{self.url}{endpoint}"
247
+ params: dict[str, Any] = {"limit": str(limit)}
248
+ if fields:
249
+ params["fields[]"] = fields
250
+ if filter_criteria:
251
+ params["filter"] = filter_criteria
252
+ if sort:
253
+ params["sort"] = sort
254
+ if offset:
255
+ params["offset"] = str(offset)
256
+ if search:
257
+ params["search"] = search
258
+ final_url = url_with_params(url, params)
259
+ data = catch_result(
260
+ lambda: http_get_json(
261
+ final_url,
262
+ self.headers,
263
+ debug=self.debug,
264
+ cached_session=cached_session or self.cached_session,
265
+ retry_config=self.retry_config,
266
+ timeout_config=self.timeout_config,
267
+ )
268
+ )
269
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
270
+ return actual_data if isinstance(actual_data, list) else []
271
+
272
+ async def _list_directus_items_async(
273
+ self,
274
+ endpoint: str,
275
+ limit: int = 10,
276
+ fields: list[str] | None = None,
277
+ filter_criteria: str | None = None,
278
+ sort: str | list[str] | None = None,
279
+ offset: int | None = None,
280
+ search: str | None = None,
281
+ cached_session: httpx.AsyncClient | None = None,
282
+ ) -> list[dict[str, Any]]:
283
+ url = f"{self.url}{endpoint}"
284
+ params: dict[str, Any] = {"limit": str(limit)}
285
+ if fields:
286
+ params["fields[]"] = fields
287
+ if filter_criteria:
288
+ params["filter"] = filter_criteria
289
+ if sort:
290
+ params["sort"] = sort
291
+ if offset:
292
+ params["offset"] = str(offset)
293
+ if search:
294
+ params["search"] = search
295
+ final_url = url_with_params(url, params)
296
+ try:
297
+ data = await http_get_json_async(
298
+ final_url,
299
+ self.headers,
300
+ debug=self.debug,
301
+ cached_session=cached_session or self.async_cached_session,
302
+ retry_config=self.retry_config,
303
+ timeout_config=self.timeout_config,
304
+ )
305
+ except Exception as e:
306
+ if self.debug:
307
+ self.logger.error(f"Error in _list_directus_items_async: {e}")
308
+ return []
309
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
310
+ return actual_data if isinstance(actual_data, list) else []
311
+
312
+ def get_openapi_spec(
313
+ self, cached_session: Client | None = None
314
+ ) -> dict[str, Any] | None:
315
+ """Retrieves the public Directus OpenAPI specification."""
316
+ url = f"{self.url}{ENDPOINT_OPENAPI}"
317
+ data = catch_result(
318
+ lambda: http_get_json(
319
+ url,
320
+ self.headers,
321
+ debug=self.debug,
322
+ cached_session=cached_session or self.cached_session,
323
+ retry_config=self.retry_config,
324
+ timeout_config=self.timeout_config,
325
+ )
326
+ )
327
+ return data if isinstance(data, dict) else None
328
+
329
+ def get_lives(self, cached_session: Client | None = None) -> list[Live] | None:
330
+ """
331
+ Retrieves a list of live events with retry logic.
332
+
333
+ Args:
334
+ cached_session (Client, optional): The cached session to use
335
+
336
+ Returns:
337
+ List[Live]: A list of Live objects representing the live events.
338
+ """
339
+ url = f"{self.url}{ENDPOINT_LIVES}"
340
+
341
+ raw_data = catch_result(
342
+ lambda: http_get_json(
343
+ url,
344
+ self.headers,
345
+ debug=self.debug,
346
+ cached_session=cached_session or self.cached_session,
347
+ retry_config=self.retry_config,
348
+ timeout_config=self.timeout_config,
349
+ )
350
+ )
351
+ if raw_data:
352
+ # data might be a list or a dict with "lives" key
353
+ if isinstance(raw_data, dict) and "lives" in raw_data:
354
+ raw_data = raw_data["lives"]
355
+
356
+ if not isinstance(raw_data, list):
357
+ return []
358
+
359
+ adapter = TypeAdapter(list[Live])
360
+ return adapter.validate_python(raw_data)
361
+ return None
362
+
363
+ async def get_lives_async(
364
+ self, cached_session: httpx.AsyncClient | None = None
365
+ ) -> list[Live] | None:
366
+ """
367
+ Retrieves a list of live events asynchroniously.
368
+ """
369
+ url = f"{self.url}{ENDPOINT_LIVES}"
370
+
371
+ # Note: catch_result is not async-friendly, but http_get_json_async handles some errors
372
+ # In a real async environment, we might want an async_catch_result
373
+ try:
374
+ raw_data = await http_get_json_async(
375
+ url,
376
+ self.headers,
377
+ debug=self.debug,
378
+ cached_session=cached_session or self.async_cached_session,
379
+ retry_config=self.retry_config,
380
+ timeout_config=self.timeout_config,
381
+ )
382
+ if raw_data is not None:
383
+ # data might be a list or a dict with "lives" key
384
+ if isinstance(raw_data, dict) and "lives" in raw_data:
385
+ raw_data = raw_data["lives"]
386
+
387
+ if not isinstance(raw_data, list):
388
+ return []
389
+
390
+ adapter = TypeAdapter(list[Live])
391
+ return adapter.validate_python(raw_data)
392
+ except Exception as e:
393
+ if self.debug:
394
+ self.logger.error(f"Error in get_lives_async: {e}")
395
+ return None
396
+
397
+ def get_competition(
398
+ self,
399
+ competition_id: int,
400
+ deep_limit: str | None = "1000",
401
+ fields: list[str] | None = None,
402
+ cached_session: Client | None = None,
403
+ ) -> GetCompetitionResponse | None:
404
+ """
405
+ Retrieves detailed information about a competition.
406
+
407
+ Args:
408
+ competition_id (int): The ID of the competition
409
+ deep_limit (str, optional): Limit for nested rencontres.
410
+ Defaults to "1000".
411
+ fields (List[str], optional): List of fields to retrieve.
412
+ If None, uses default fields.
413
+ cached_session (Client, optional): The cached session to use
414
+
415
+ Returns:
416
+ GetCompetitionResponse: Competition data with nested phases,
417
+ poules, and rencontres
418
+ """
419
+ url = f"{self.url}{ENDPOINT_COMPETITIONS}/{competition_id}"
420
+
421
+ params: dict[str, Any] = {}
422
+ if deep_limit:
423
+ params["deep[phases][poules][rencontres][_limit]"] = deep_limit
424
+
425
+ if fields:
426
+ for field in fields:
427
+ if "fields[]" not in params:
428
+ params["fields[]"] = []
429
+ params["fields[]"].append(field)
430
+ else:
431
+ # Use default fields from descriptor when no fields are specified
432
+ params["fields[]"] = QueryFieldsManager.get_competition_fields(
433
+ FieldSet.DEFAULT
434
+ )
435
+
436
+ final_url = url_with_params(url, params)
437
+ data = catch_result(
438
+ lambda: http_get_json(
439
+ final_url,
440
+ self.headers,
441
+ debug=self.debug,
442
+ cached_session=cached_session or self.cached_session,
443
+ )
444
+ )
445
+
446
+ # Extract the actual data from the response wrapper
447
+
448
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
449
+ if actual_data:
450
+ adapter = TypeAdapter(GetCompetitionResponse)
451
+ return adapter.validate_python(actual_data)
452
+ return None
453
+
454
+ async def get_competition_async(
455
+ self,
456
+ competition_id: int,
457
+ deep_limit: str | None = "1000",
458
+ fields: list[str] | None = None,
459
+ cached_session: httpx.AsyncClient | None = None,
460
+ ) -> GetCompetitionResponse | None:
461
+ """
462
+ Retrieves detailed information about a competition asynchroniously.
463
+ """
464
+ url = f"{self.url}{ENDPOINT_COMPETITIONS}/{competition_id}"
465
+
466
+ params: dict[str, Any] = {}
467
+ if deep_limit:
468
+ params["deep[phases][poules][rencontres][_limit]"] = deep_limit
469
+
470
+ if fields:
471
+ for field in fields:
472
+ if "fields[]" not in params:
473
+ params["fields[]"] = []
474
+ params["fields[]"].append(field)
475
+ else:
476
+ params["fields[]"] = QueryFieldsManager.get_competition_fields(
477
+ FieldSet.DEFAULT
478
+ )
479
+
480
+ final_url = url_with_params(url, params)
481
+ try:
482
+ data = await http_get_json_async(
483
+ final_url,
484
+ self.headers,
485
+ debug=self.debug,
486
+ cached_session=cached_session or self.async_cached_session,
487
+ )
488
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
489
+ if actual_data:
490
+ adapter = TypeAdapter(GetCompetitionResponse)
491
+ return adapter.validate_python(actual_data)
492
+ return None
493
+ except Exception as e:
494
+ if self.debug:
495
+ self.logger.error(f"Error in get_competition_async: {e}")
496
+ return None
497
+
498
+ def get_poule(
499
+ self,
500
+ poule_id: int,
501
+ deep_limit: str | None = "1000",
502
+ fields: list[str] | None = None,
503
+ cached_session: Client | None = None,
504
+ ) -> GetPouleResponse | None:
505
+ """
506
+ Retrieves detailed information about a poule.
507
+
508
+ Args:
509
+ poule_id (int): The ID of the poule
510
+ deep_limit (str, optional): Limit for nested rencontres.
511
+ Defaults to "1000".
512
+ fields (List[str], optional): List of fields to retrieve.
513
+ If None, uses default fields.
514
+ cached_session (Client, optional): The cached session to use
515
+
516
+ Returns:
517
+ GetPouleResponse: Poule data with rencontres
518
+ """
519
+ url = f"{self.url}{ENDPOINT_POULES}/{poule_id}"
520
+
521
+ params: dict[str, Any] = {}
522
+ if deep_limit:
523
+ params["deep[rencontres][_limit]"] = deep_limit
524
+ params["deep[classements][_limit]"] = deep_limit
525
+
526
+ if fields:
527
+ params["fields[]"] = fields
528
+ else:
529
+ # Use default fields from descriptor when no fields are specified
530
+ params["fields[]"] = QueryFieldsManager.get_poule_fields(FieldSet.DEFAULT)
531
+
532
+ final_url = url_with_params(url, params)
533
+ data = catch_result(
534
+ lambda: http_get_json(
535
+ final_url,
536
+ self.headers,
537
+ debug=self.debug,
538
+ cached_session=cached_session or self.cached_session,
539
+ )
540
+ )
541
+
542
+ # Extract the actual data from the response wrapper
543
+
544
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
545
+ if actual_data:
546
+ return GetPouleResponse.from_dict(actual_data)
547
+ return None
548
+
549
+ async def get_poule_async(
550
+ self,
551
+ poule_id: int,
552
+ deep_limit: str | None = "1000",
553
+ fields: list[str] | None = None,
554
+ cached_session: httpx.AsyncClient | None = None,
555
+ ) -> GetPouleResponse | None:
556
+ """
557
+ Retrieves detailed information about a poule asynchroniously.
558
+ """
559
+ url = f"{self.url}{ENDPOINT_POULES}/{poule_id}"
560
+
561
+ params: dict[str, Any] = {}
562
+ if deep_limit:
563
+ params["deep[rencontres][_limit]"] = deep_limit
564
+ params["deep[classements][_limit]"] = deep_limit
565
+
566
+ if fields:
567
+ params["fields[]"] = fields
568
+ else:
569
+ params["fields[]"] = QueryFieldsManager.get_poule_fields(FieldSet.DEFAULT)
570
+
571
+ final_url = url_with_params(url, params)
572
+ try:
573
+ data = await http_get_json_async(
574
+ final_url,
575
+ self.headers,
576
+ debug=self.debug,
577
+ cached_session=cached_session or self.async_cached_session,
578
+ )
579
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
580
+ if actual_data:
581
+ return GetPouleResponse.from_dict(actual_data)
582
+ return None
583
+ except Exception as e:
584
+ if self.debug:
585
+ self.logger.error(f"Error in get_poule_async: {e}")
586
+ return None
587
+
588
+ def get_classement(
589
+ self,
590
+ poule_id: int,
591
+ cached_session: Client | None = None,
592
+ ) -> list[TeamRanking] | None:
593
+ """
594
+ Retrieves ONLY the ranking (classement) for a specific poule.
595
+ """
596
+ res = self.get_poule(
597
+ poule_id=poule_id,
598
+ deep_limit="1000",
599
+ fields=QueryFieldsManager.get_classement_fields(),
600
+ cached_session=cached_session,
601
+ )
602
+ return res.classements if res else None
603
+
604
+ async def get_classement_async(
605
+ self,
606
+ poule_id: int,
607
+ cached_session: httpx.AsyncClient | None = None,
608
+ ) -> list[TeamRanking] | None:
609
+ """
610
+ Asynchronously retrieves ONLY the ranking (classement) for a specific poule.
611
+ """
612
+ res = await self.get_poule_async(
613
+ poule_id=poule_id,
614
+ deep_limit="1000",
615
+ fields=QueryFieldsManager.get_classement_fields(),
616
+ cached_session=cached_session,
617
+ )
618
+ return res.classements if res else None
619
+
620
+ def get_saisons(
621
+ self,
622
+ fields: list[str] | None = None,
623
+ filter_criteria: str | None = '{"actif":{"_eq":true}}',
624
+ cached_session: Client | None = None,
625
+ ) -> list[GetSaisonsResponse]:
626
+ """
627
+ Retrieves list of seasons.
628
+
629
+ Args:
630
+ fields (List[str], optional): List of fields to retrieve.
631
+ If None, uses default fields.
632
+ filter_criteria (str, optional): JSON filter criteria.
633
+ Defaults to active seasons.
634
+ cached_session (Client, optional): The cached session to use
635
+
636
+ Returns:
637
+ List[GetSaisonsResponse]: List of season data
638
+ """
639
+ url = f"{self.url}{ENDPOINT_SAISONS}"
640
+
641
+ params: dict[str, Any] = {}
642
+ if fields:
643
+ params["fields[]"] = fields
644
+ else:
645
+ # Use default fields from descriptor when no fields are specified
646
+ params["fields[]"] = QueryFieldsManager.get_saison_fields(FieldSet.DEFAULT)
647
+
648
+ if filter_criteria:
649
+ params["filter"] = filter_criteria
650
+
651
+ final_url = url_with_params(url, params)
652
+ data = catch_result(
653
+ lambda: http_get_json(
654
+ final_url,
655
+ self.headers,
656
+ debug=self.debug,
657
+ cached_session=cached_session or self.cached_session,
658
+ )
659
+ )
660
+
661
+ # Extract the actual data from the response wrapper
662
+
663
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
664
+ if actual_data and isinstance(actual_data, list):
665
+ adapter = TypeAdapter(list[GetSaisonsResponse])
666
+ return adapter.validate_python(actual_data)
667
+ return []
668
+
669
+ async def get_saisons_async(
670
+ self,
671
+ fields: list[str] | None = None,
672
+ filter_criteria: str | None = '{"actif":{"_eq":true}}',
673
+ cached_session: httpx.AsyncClient | None = None,
674
+ ) -> list[GetSaisonsResponse]:
675
+ """
676
+ Retrieves list of seasons asynchroniously.
677
+ """
678
+ url = f"{self.url}{ENDPOINT_SAISONS}"
679
+
680
+ params: dict[str, Any] = {}
681
+ if fields:
682
+ params["fields[]"] = fields
683
+ else:
684
+ params["fields[]"] = QueryFieldsManager.get_saison_fields(FieldSet.DEFAULT)
685
+
686
+ if filter_criteria:
687
+ params["filter"] = filter_criteria
688
+
689
+ final_url = url_with_params(url, params)
690
+ try:
691
+ data = await http_get_json_async(
692
+ final_url,
693
+ self.headers,
694
+ debug=self.debug,
695
+ cached_session=cached_session or self.async_cached_session,
696
+ )
697
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
698
+ if actual_data and isinstance(actual_data, list):
699
+ adapter = TypeAdapter(list[GetSaisonsResponse])
700
+ return adapter.validate_python(actual_data)
701
+ except Exception as e:
702
+ if self.debug:
703
+ self.logger.error(f"Error in get_saisons_async: {e}")
704
+ return []
705
+
706
+ def get_organisme(
707
+ self,
708
+ organisme_id: int,
709
+ fields: list[str] | None = None,
710
+ cached_session: Client | None = None,
711
+ ) -> GetOrganismeResponse | None:
712
+ """
713
+ Retrieves detailed information about an organisme.
714
+
715
+ Args:
716
+ organisme_id (int): The ID of the organisme
717
+ fields (List[str], optional): List of fields to retrieve.
718
+ If None, uses default fields.
719
+ cached_session (Client, optional): The cached session to use
720
+
721
+ Returns:
722
+ GetOrganismeResponse: Organisme data with members, competitions, etc.
723
+ """
724
+ url = f"{self.url}{ENDPOINT_ORGANISMES}/{organisme_id}"
725
+
726
+ params: dict[str, Any] = {}
727
+ if fields:
728
+ params["fields[]"] = fields
729
+ else:
730
+ # Use default fields from descriptor when no fields are specified
731
+ params["fields[]"] = QueryFieldsManager.get_organisme_fields(
732
+ FieldSet.DEFAULT
733
+ )
734
+
735
+ final_url = url_with_params(url, params)
736
+ data = catch_result(
737
+ lambda: http_get_json(
738
+ final_url,
739
+ self.headers,
740
+ debug=self.debug,
741
+ cached_session=cached_session or self.cached_session,
742
+ )
743
+ )
744
+
745
+ # Extract the actual data from the response wrapper
746
+
747
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
748
+ if actual_data:
749
+ return GetOrganismeResponse.from_dict(actual_data)
750
+ return None
751
+
752
+ async def get_organisme_async(
753
+ self,
754
+ organisme_id: int,
755
+ fields: list[str] | None = None,
756
+ cached_session: httpx.AsyncClient | None = None,
757
+ ) -> GetOrganismeResponse | None:
758
+ """
759
+ Retrieves detailed information about an organisme asynchroniously.
760
+ """
761
+ url = f"{self.url}{ENDPOINT_ORGANISMES}/{organisme_id}"
762
+
763
+ params: dict[str, Any] = {}
764
+ if fields:
765
+ params["fields[]"] = fields
766
+ else:
767
+ params["fields[]"] = QueryFieldsManager.get_organisme_fields(
768
+ FieldSet.DEFAULT
769
+ )
770
+
771
+ final_url = url_with_params(url, params)
772
+ try:
773
+ data = await http_get_json_async(
774
+ final_url,
775
+ self.headers,
776
+ debug=self.debug,
777
+ cached_session=cached_session or self.async_cached_session,
778
+ )
779
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
780
+ if actual_data:
781
+ return GetOrganismeResponse.from_dict(actual_data)
782
+ return None
783
+ except Exception as e:
784
+ if self.debug:
785
+ self.logger.error(f"Error in get_organisme_async: {e}")
786
+ return None
787
+
788
+ def get_equipes(
789
+ self,
790
+ organisme_id: int,
791
+ cached_session: Client | None = None,
792
+ ) -> list[GetOrganismeResponse.EngagementsitemModel] | None:
793
+ """
794
+ Retrieves ONLY the team commitments (engagements) for a specific club.
795
+ """
796
+ res = self.get_organisme(
797
+ organisme_id=organisme_id,
798
+ fields=QueryFieldsManager.get_equipes_fields(),
799
+ cached_session=cached_session,
800
+ )
801
+ return res.engagements if res else None
802
+
803
+ async def get_equipes_async(
804
+ self,
805
+ organisme_id: int,
806
+ cached_session: httpx.AsyncClient | None = None,
807
+ ) -> list[GetOrganismeResponse.EngagementsitemModel] | None:
808
+ """
809
+ Asynchronously retrieves ONLY the team commitments (engagements) for a specific club.
810
+ """
811
+ res = await self.get_organisme_async(
812
+ organisme_id=organisme_id,
813
+ fields=QueryFieldsManager.get_equipes_fields(),
814
+ cached_session=cached_session,
815
+ )
816
+ return res.engagements if res else None
817
+
818
+ def list_competitions(
819
+ self,
820
+ limit: int = 10,
821
+ fields: list[str] | None = None,
822
+ cached_session: Client | None = None,
823
+ ) -> list[GetCompetitionResponse | None]:
824
+ """
825
+ Lists competitions with optional field selection.
826
+
827
+ Args:
828
+ limit (int): Maximum number of competitions to return. Defaults to 10.
829
+ fields (List[str], optional): List of fields to retrieve.
830
+ If None, uses basic fields (id, nom).
831
+ cached_session (Client, optional): The cached session to use
832
+
833
+ Returns:
834
+ list[GetCompetitionResponse]: List of competition data
835
+ """
836
+ url = f"{self.url}{ENDPOINT_COMPETITIONS}"
837
+
838
+ params: dict[str, Any] = {"limit": str(limit)}
839
+
840
+ if fields:
841
+ params["fields[]"] = fields
842
+ else:
843
+ params["fields[]"] = ["id", "nom"]
844
+
845
+ final_url = url_with_params(url, params)
846
+ data = catch_result(
847
+ lambda: http_get_json(
848
+ final_url,
849
+ self.headers,
850
+ debug=self.debug,
851
+ cached_session=cached_session or self.cached_session,
852
+ )
853
+ )
854
+
855
+ # Extract the actual data from the response wrapper
856
+
857
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
858
+ if actual_data and isinstance(actual_data, list):
859
+ adapter = TypeAdapter(list[GetCompetitionResponse])
860
+ return adapter.validate_python(actual_data)
861
+ return []
862
+
863
+ async def list_competitions_async(
864
+ self,
865
+ limit: int = 10,
866
+ fields: list[str] | None = None,
867
+ cached_session: httpx.AsyncClient | None = None,
868
+ ) -> list[GetCompetitionResponse | None]:
869
+ """
870
+ Lists competitions with optional field selection asynchroniously.
871
+ """
872
+ url = f"{self.url}{ENDPOINT_COMPETITIONS}"
873
+
874
+ params: dict[str, Any] = {"limit": str(limit)}
875
+
876
+ if fields:
877
+ params["fields[]"] = fields
878
+ else:
879
+ params["fields[]"] = ["id", "nom"]
880
+
881
+ final_url = url_with_params(url, params)
882
+ try:
883
+ data = await http_get_json_async(
884
+ final_url,
885
+ self.headers,
886
+ debug=self.debug,
887
+ cached_session=cached_session or self.async_cached_session,
888
+ )
889
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
890
+ if actual_data and isinstance(actual_data, list):
891
+ adapter = TypeAdapter(list[GetCompetitionResponse])
892
+ return adapter.validate_python(actual_data)
893
+ except Exception as e:
894
+ if self.debug:
895
+ self.logger.error(f"Error in list_competitions_async: {e}")
896
+ return []
897
+
898
+ def get_configuration(
899
+ self,
900
+ cached_session: Client | None = None,
901
+ ) -> GetConfigurationResponse | None:
902
+ """
903
+ Retrieves the API configuration including bearer tokens.
904
+
905
+ This endpoint returns configuration data including:
906
+ - key_dh: The API bearer token for api.ffbb.app
907
+ - key_ms: The Meilisearch bearer token for meilisearch-prod.ffbb.app
908
+
909
+ Args:
910
+ cached_session (Client, optional): The cached session to use
911
+
912
+ Returns:
913
+ GetConfigurationResponse: Configuration data with tokens
914
+ """
915
+ url = f"{self.url}{ENDPOINT_CONFIGURATION}"
916
+ data = catch_result(
917
+ lambda: http_get_json(
918
+ url,
919
+ self.headers,
920
+ debug=self.debug,
921
+ cached_session=cached_session or self.cached_session,
922
+ retry_config=self.retry_config,
923
+ timeout_config=self.timeout_config,
924
+ )
925
+ )
926
+
927
+ # Extract the actual data from the response wrapper
928
+
929
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
930
+ if actual_data:
931
+ adapter = TypeAdapter(GetConfigurationResponse)
932
+ return adapter.validate_python(actual_data)
933
+ return None
934
+
935
+ async def get_configuration_async(
936
+ self,
937
+ cached_session: httpx.AsyncClient | None = None,
938
+ ) -> GetConfigurationResponse | None:
939
+ """
940
+ Retrieves the API configuration including bearer tokens asynchroniously.
941
+ """
942
+ url = f"{self.url}{ENDPOINT_CONFIGURATION}"
943
+ try:
944
+ data = await http_get_json_async(
945
+ url,
946
+ self.headers,
947
+ debug=self.debug,
948
+ cached_session=cached_session or self.async_cached_session,
949
+ retry_config=self.retry_config,
950
+ timeout_config=self.timeout_config,
951
+ )
952
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
953
+ if actual_data:
954
+ adapter = TypeAdapter(GetConfigurationResponse)
955
+ return adapter.validate_python(actual_data)
956
+ return None
957
+ except Exception as e:
958
+ if self.debug:
959
+ self.logger.error(f"Error in get_configuration_async: {e}")
960
+ return None
961
+
962
+ def get_rencontre(
963
+ self, id: str, cached_session: Client | None = None
964
+ ) -> GetRencontreResponse | None:
965
+ """Retrieves detailed information about a rencontre."""
966
+ url = f"{self.url}{ENDPOINT_RENCONTRES}/{id}"
967
+ data = catch_result(
968
+ lambda: http_get_json(
969
+ url,
970
+ self.headers,
971
+ debug=self.debug,
972
+ cached_session=cached_session or self.cached_session,
973
+ )
974
+ )
975
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
976
+ return GetRencontreResponse.from_dict(actual_data) if actual_data else None
977
+
978
+ async def get_rencontre_async(
979
+ self, id: str, cached_session: httpx.AsyncClient | None = None
980
+ ) -> GetRencontreResponse | None:
981
+ """Asynchronously retrieves detailed information about a rencontre."""
982
+ url = f"{self.url}{ENDPOINT_RENCONTRES}/{id}"
983
+ try:
984
+ data = await http_get_json_async(
985
+ url,
986
+ self.headers,
987
+ debug=self.debug,
988
+ cached_session=cached_session or self.async_cached_session,
989
+ )
990
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
991
+ return GetRencontreResponse.from_dict(actual_data) if actual_data else None
992
+ except Exception as e:
993
+ if self.debug:
994
+ self.logger.error(f"Error in get_rencontre_async: {e}")
995
+ return None
996
+
997
+ def get_engagement(
998
+ self, id: str, cached_session: Client | None = None
999
+ ) -> GetEngagementResponse | None:
1000
+ """Retrieves detailed information about an engagement."""
1001
+ url = f"{self.url}{ENDPOINT_ENGAGEMENTS}/{id}"
1002
+ data = catch_result(
1003
+ lambda: http_get_json(
1004
+ url,
1005
+ self.headers,
1006
+ debug=self.debug,
1007
+ cached_session=cached_session or self.cached_session,
1008
+ )
1009
+ )
1010
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
1011
+ return GetEngagementResponse.from_dict(actual_data) if actual_data else None
1012
+
1013
+ async def get_engagement_async(
1014
+ self, id: str, cached_session: httpx.AsyncClient | None = None
1015
+ ) -> GetEngagementResponse | None:
1016
+ """Asynchronously retrieves detailed information about an engagement."""
1017
+ url = f"{self.url}{ENDPOINT_ENGAGEMENTS}/{id}"
1018
+ try:
1019
+ data = await http_get_json_async(
1020
+ url,
1021
+ self.headers,
1022
+ debug=self.debug,
1023
+ cached_session=cached_session or self.async_cached_session,
1024
+ )
1025
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
1026
+ return GetEngagementResponse.from_dict(actual_data) if actual_data else None
1027
+ except Exception as e:
1028
+ if self.debug:
1029
+ self.logger.error(f"Error in get_engagement_async: {e}")
1030
+ return None
1031
+
1032
+ def get_formation(
1033
+ self, id: str, cached_session: Client | None = None
1034
+ ) -> GetFormationResponse | None:
1035
+ """Retrieves detailed information about a formation."""
1036
+ url = f"{self.url}{ENDPOINT_FORMATIONS}/{id}"
1037
+ data = catch_result(
1038
+ lambda: http_get_json(
1039
+ url,
1040
+ self.headers,
1041
+ debug=self.debug,
1042
+ cached_session=cached_session or self.cached_session,
1043
+ )
1044
+ )
1045
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
1046
+ return GetFormationResponse.from_dict(actual_data) if actual_data else None
1047
+
1048
+ async def get_formation_async(
1049
+ self, id: str, cached_session: httpx.AsyncClient | None = None
1050
+ ) -> GetFormationResponse | None:
1051
+ """Asynchronously retrieves detailed information about a formation."""
1052
+ url = f"{self.url}{ENDPOINT_FORMATIONS}/{id}"
1053
+ try:
1054
+ data = await http_get_json_async(
1055
+ url,
1056
+ self.headers,
1057
+ debug=self.debug,
1058
+ cached_session=cached_session or self.async_cached_session,
1059
+ )
1060
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
1061
+ return GetFormationResponse.from_dict(actual_data) if actual_data else None
1062
+ except Exception as e:
1063
+ if self.debug:
1064
+ self.logger.error(f"Error in get_formation_async: {e}")
1065
+ return None
1066
+
1067
+ def get_entraineur(
1068
+ self, id: str, cached_session: Client | None = None
1069
+ ) -> GetEntraineurResponse | None:
1070
+ """Retrieves detailed information about an entraineur."""
1071
+ url = f"{self.url}{ENDPOINT_ENTRAINEURS}/{id}"
1072
+ data = catch_result(
1073
+ lambda: http_get_json(
1074
+ url,
1075
+ self.headers,
1076
+ debug=self.debug,
1077
+ cached_session=cached_session or self.cached_session,
1078
+ )
1079
+ )
1080
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
1081
+ return GetEntraineurResponse.from_dict(actual_data) if actual_data else None
1082
+
1083
+ async def get_entraineur_async(
1084
+ self, id: str, cached_session: httpx.AsyncClient | None = None
1085
+ ) -> GetEntraineurResponse | None:
1086
+ """Asynchronously retrieves detailed information about an entraineur."""
1087
+ url = f"{self.url}{ENDPOINT_ENTRAINEURS}/{id}"
1088
+ try:
1089
+ data = await http_get_json_async(
1090
+ url,
1091
+ self.headers,
1092
+ debug=self.debug,
1093
+ cached_session=cached_session or self.async_cached_session,
1094
+ )
1095
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
1096
+ return GetEntraineurResponse.from_dict(actual_data) if actual_data else None
1097
+ except Exception as e:
1098
+ if self.debug:
1099
+ self.logger.error(f"Error in get_entraineur_async: {e}")
1100
+ return None
1101
+
1102
+ def get_commune(
1103
+ self, id: str, cached_session: Client | None = None
1104
+ ) -> GetCommuneResponse | None:
1105
+ """Retrieves detailed information about a commune."""
1106
+ url = f"{self.url}{ENDPOINT_COMMUNES}/{id}"
1107
+ data = catch_result(
1108
+ lambda: http_get_json(
1109
+ url,
1110
+ self.headers,
1111
+ debug=self.debug,
1112
+ cached_session=cached_session or self.cached_session,
1113
+ )
1114
+ )
1115
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
1116
+ return GetCommuneResponse.from_dict(actual_data) if actual_data else None
1117
+
1118
+ async def get_commune_async(
1119
+ self, id: str, cached_session: httpx.AsyncClient | None = None
1120
+ ) -> GetCommuneResponse | None:
1121
+ """Asynchronously retrieves detailed information about a commune."""
1122
+ url = f"{self.url}{ENDPOINT_COMMUNES}/{id}"
1123
+ try:
1124
+ data = await http_get_json_async(
1125
+ url,
1126
+ self.headers,
1127
+ debug=self.debug,
1128
+ cached_session=cached_session or self.async_cached_session,
1129
+ )
1130
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
1131
+ return GetCommuneResponse.from_dict(actual_data) if actual_data else None
1132
+ except Exception as e:
1133
+ if self.debug:
1134
+ self.logger.error(f"Error in get_commune_async: {e}")
1135
+ return None
1136
+
1137
+ def get_officiel(
1138
+ self, id: str, cached_session: Client | None = None
1139
+ ) -> GetOfficielResponse | None:
1140
+ """Retrieves detailed information about an officiel."""
1141
+ url = f"{self.url}{ENDPOINT_OFFICIELS}/{id}"
1142
+ data = catch_result(
1143
+ lambda: http_get_json(
1144
+ url,
1145
+ self.headers,
1146
+ debug=self.debug,
1147
+ cached_session=cached_session or self.cached_session,
1148
+ )
1149
+ )
1150
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
1151
+ return GetOfficielResponse.from_dict(actual_data) if actual_data else None
1152
+
1153
+ async def get_officiel_async(
1154
+ self, id: str, cached_session: httpx.AsyncClient | None = None
1155
+ ) -> GetOfficielResponse | None:
1156
+ """Asynchronously retrieves detailed information about an officiel."""
1157
+ url = f"{self.url}{ENDPOINT_OFFICIELS}/{id}"
1158
+ try:
1159
+ data = await http_get_json_async(
1160
+ url,
1161
+ self.headers,
1162
+ debug=self.debug,
1163
+ cached_session=cached_session or self.async_cached_session,
1164
+ )
1165
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
1166
+ return GetOfficielResponse.from_dict(actual_data) if actual_data else None
1167
+ except Exception as e:
1168
+ if self.debug:
1169
+ self.logger.error(f"Error in get_officiel_async: {e}")
1170
+ return None
1171
+
1172
+ def get_salle(
1173
+ self, id: str, cached_session: Client | None = None
1174
+ ) -> GetSalleResponse | None:
1175
+ """Retrieves detailed information about a salle."""
1176
+ url = f"{self.url}{ENDPOINT_SALLES}/{id}"
1177
+ data = catch_result(
1178
+ lambda: http_get_json(
1179
+ url,
1180
+ self.headers,
1181
+ debug=self.debug,
1182
+ cached_session=cached_session or self.cached_session,
1183
+ )
1184
+ )
1185
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
1186
+ return GetSalleResponse.from_dict(actual_data) if actual_data else None
1187
+
1188
+ async def get_salle_async(
1189
+ self, id: str, cached_session: httpx.AsyncClient | None = None
1190
+ ) -> GetSalleResponse | None:
1191
+ """Asynchronously retrieves detailed information about a salle."""
1192
+ url = f"{self.url}{ENDPOINT_SALLES}/{id}"
1193
+ try:
1194
+ data = await http_get_json_async(
1195
+ url,
1196
+ self.headers,
1197
+ debug=self.debug,
1198
+ cached_session=cached_session or self.async_cached_session,
1199
+ )
1200
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
1201
+ return GetSalleResponse.from_dict(actual_data) if actual_data else None
1202
+ except Exception as e:
1203
+ if self.debug:
1204
+ self.logger.error(f"Error in get_salle_async: {e}")
1205
+ return None
1206
+
1207
+ def get_terrain(
1208
+ self, id: str, cached_session: Client | None = None
1209
+ ) -> GetTerrainResponse | None:
1210
+ """Retrieves detailed information about a terrain."""
1211
+ url = f"{self.url}{ENDPOINT_TERRAINS}/{id}"
1212
+ data = catch_result(
1213
+ lambda: http_get_json(
1214
+ url,
1215
+ self.headers,
1216
+ debug=self.debug,
1217
+ cached_session=cached_session or self.cached_session,
1218
+ )
1219
+ )
1220
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
1221
+ return GetTerrainResponse.from_dict(actual_data) if actual_data else None
1222
+
1223
+ async def get_terrain_async(
1224
+ self, id: str, cached_session: httpx.AsyncClient | None = None
1225
+ ) -> GetTerrainResponse | None:
1226
+ """Asynchronously retrieves detailed information about a terrain."""
1227
+ url = f"{self.url}{ENDPOINT_TERRAINS}/{id}"
1228
+ try:
1229
+ data = await http_get_json_async(
1230
+ url,
1231
+ self.headers,
1232
+ debug=self.debug,
1233
+ cached_session=cached_session or self.async_cached_session,
1234
+ )
1235
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
1236
+ return GetTerrainResponse.from_dict(actual_data) if actual_data else None
1237
+ except Exception as e:
1238
+ if self.debug:
1239
+ self.logger.error(f"Error in get_terrain_async: {e}")
1240
+ return None
1241
+
1242
+ def get_tournoi(
1243
+ self, id: str, cached_session: Client | None = None
1244
+ ) -> GetTournoiResponse | None:
1245
+ """Retrieves detailed information about a tournoi."""
1246
+ url = f"{self.url}{ENDPOINT_TOURNOIS}/{id}"
1247
+ data = catch_result(
1248
+ lambda: http_get_json(
1249
+ url,
1250
+ self.headers,
1251
+ debug=self.debug,
1252
+ cached_session=cached_session or self.cached_session,
1253
+ )
1254
+ )
1255
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
1256
+ return GetTournoiResponse.from_dict(actual_data) if actual_data else None
1257
+
1258
+ async def get_tournoi_async(
1259
+ self, id: str, cached_session: httpx.AsyncClient | None = None
1260
+ ) -> GetTournoiResponse | None:
1261
+ """Asynchronously retrieves detailed information about a tournoi."""
1262
+ url = f"{self.url}{ENDPOINT_TOURNOIS}/{id}"
1263
+ try:
1264
+ data = await http_get_json_async(
1265
+ url,
1266
+ self.headers,
1267
+ debug=self.debug,
1268
+ cached_session=cached_session or self.async_cached_session,
1269
+ )
1270
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
1271
+ return GetTournoiResponse.from_dict(actual_data) if actual_data else None
1272
+ except Exception as e:
1273
+ if self.debug:
1274
+ self.logger.error(f"Error in get_tournoi_async: {e}")
1275
+ return None
1276
+
1277
+ def get_pratique(
1278
+ self, id: str, cached_session: Client | None = None
1279
+ ) -> GetPratiqueResponse | None:
1280
+ """Retrieves detailed information about a pratique."""
1281
+ url = f"{self.url}{ENDPOINT_PRATIQUES}/{id}"
1282
+ data = catch_result(
1283
+ lambda: http_get_json(
1284
+ url,
1285
+ self.headers,
1286
+ debug=self.debug,
1287
+ cached_session=cached_session or self.cached_session,
1288
+ )
1289
+ )
1290
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
1291
+ return GetPratiqueResponse.from_dict(actual_data) if actual_data else None
1292
+
1293
+ async def get_pratique_async(
1294
+ self, id: str, cached_session: httpx.AsyncClient | None = None
1295
+ ) -> GetPratiqueResponse | None:
1296
+ """Asynchronously retrieves detailed information about a pratique."""
1297
+ url = f"{self.url}{ENDPOINT_PRATIQUES}/{id}"
1298
+ try:
1299
+ data = await http_get_json_async(
1300
+ url,
1301
+ self.headers,
1302
+ debug=self.debug,
1303
+ cached_session=cached_session or self.async_cached_session,
1304
+ )
1305
+ actual_data = data.get("data") if data and isinstance(data, dict) else data
1306
+ return GetPratiqueResponse.from_dict(actual_data) if actual_data else None
1307
+ except Exception as e:
1308
+ if self.debug:
1309
+ self.logger.error(f"Error in get_pratique_async: {e}")
1310
+ return None
1311
+
1312
+ def get_session(
1313
+ self,
1314
+ id: str,
1315
+ fields: list[str] | None = None,
1316
+ cached_session: Client | None = None,
1317
+ ) -> dict[str, Any] | None:
1318
+ """Retrieves detailed information about a formation session."""
1319
+ return self._get_directus_item(
1320
+ ENDPOINT_SESSIONS, id, fields=fields, cached_session=cached_session
1321
+ )
1322
+
1323
+ def list_sessions(
1324
+ self,
1325
+ limit: int = 10,
1326
+ fields: list[str] | None = None,
1327
+ filter_criteria: str | None = None,
1328
+ sort: str | list[str] | None = None,
1329
+ cached_session: Client | None = None,
1330
+ ) -> list[dict[str, Any]]:
1331
+ """Lists formation sessions."""
1332
+ return self._list_directus_items(
1333
+ ENDPOINT_SESSIONS,
1334
+ limit=limit,
1335
+ fields=fields,
1336
+ filter_criteria=filter_criteria,
1337
+ sort=sort,
1338
+ cached_session=cached_session,
1339
+ )
1340
+
1341
+ async def get_session_async(
1342
+ self,
1343
+ id: str,
1344
+ fields: list[str] | None = None,
1345
+ cached_session: httpx.AsyncClient | None = None,
1346
+ ) -> dict[str, Any] | None:
1347
+ """Asynchronously retrieves detailed information about a formation session."""
1348
+ return await self._get_directus_item_async(
1349
+ ENDPOINT_SESSIONS, id, fields=fields, cached_session=cached_session
1350
+ )
1351
+
1352
+ async def list_sessions_async(
1353
+ self,
1354
+ limit: int = 10,
1355
+ fields: list[str] | None = None,
1356
+ filter_criteria: str | None = None,
1357
+ sort: str | list[str] | None = None,
1358
+ cached_session: httpx.AsyncClient | None = None,
1359
+ ) -> list[dict[str, Any]]:
1360
+ """Asynchronously lists formation sessions."""
1361
+ return await self._list_directus_items_async(
1362
+ ENDPOINT_SESSIONS,
1363
+ limit=limit,
1364
+ fields=fields,
1365
+ filter_criteria=filter_criteria,
1366
+ sort=sort,
1367
+ cached_session=cached_session,
1368
+ )
1369
+
1370
+ # ---------- list_rencontres ----------
1371
+ def list_rencontres(
1372
+ self,
1373
+ limit: int = 10,
1374
+ filter_criteria: str | None = None,
1375
+ sort: list[str] | None = None,
1376
+ offset: int | None = None,
1377
+ search: str | None = None,
1378
+ cached_session: Client | None = None,
1379
+ ) -> list[GetRencontreResponse]:
1380
+ raw = self._list_directus_items(
1381
+ ENDPOINT_RENCONTRES,
1382
+ limit=limit,
1383
+ filter_criteria=filter_criteria,
1384
+ sort=sort,
1385
+ offset=offset,
1386
+ search=search,
1387
+ cached_session=cached_session,
1388
+ )
1389
+ return [GetRencontreResponse.from_dict(r) for r in raw if r]
1390
+
1391
+ async def list_rencontres_async(
1392
+ self,
1393
+ limit: int = 10,
1394
+ filter_criteria: str | None = None,
1395
+ sort: list[str] | None = None,
1396
+ offset: int | None = None,
1397
+ search: str | None = None,
1398
+ cached_session: httpx.AsyncClient | None = None,
1399
+ ) -> list[GetRencontreResponse]:
1400
+ raw = await self._list_directus_items_async(
1401
+ ENDPOINT_RENCONTRES,
1402
+ limit=limit,
1403
+ filter_criteria=filter_criteria,
1404
+ sort=sort,
1405
+ offset=offset,
1406
+ search=search,
1407
+ cached_session=cached_session,
1408
+ )
1409
+ return [GetRencontreResponse.from_dict(r) for r in raw if r]
1410
+
1411
+ # ---------- list_salles ----------
1412
+ def list_salles(
1413
+ self,
1414
+ limit: int = 10,
1415
+ filter_criteria: str | None = None,
1416
+ sort: list[str] | None = None,
1417
+ offset: int | None = None,
1418
+ search: str | None = None,
1419
+ cached_session: Client | None = None,
1420
+ ) -> list[GetSalleResponse]:
1421
+ raw = self._list_directus_items(
1422
+ ENDPOINT_SALLES,
1423
+ limit=limit,
1424
+ filter_criteria=filter_criteria,
1425
+ sort=sort,
1426
+ offset=offset,
1427
+ search=search,
1428
+ cached_session=cached_session,
1429
+ )
1430
+ return [GetSalleResponse.from_dict(r) for r in raw if r]
1431
+
1432
+ async def list_salles_async(
1433
+ self,
1434
+ limit: int = 10,
1435
+ filter_criteria: str | None = None,
1436
+ sort: list[str] | None = None,
1437
+ offset: int | None = None,
1438
+ search: str | None = None,
1439
+ cached_session: httpx.AsyncClient | None = None,
1440
+ ) -> list[GetSalleResponse]:
1441
+ raw = await self._list_directus_items_async(
1442
+ ENDPOINT_SALLES,
1443
+ limit=limit,
1444
+ filter_criteria=filter_criteria,
1445
+ sort=sort,
1446
+ offset=offset,
1447
+ search=search,
1448
+ cached_session=cached_session,
1449
+ )
1450
+ return [GetSalleResponse.from_dict(r) for r in raw if r]
1451
+
1452
+ # ---------- list_terrains ----------
1453
+ def list_terrains(
1454
+ self,
1455
+ limit: int = 10,
1456
+ filter_criteria: str | None = None,
1457
+ sort: list[str] | None = None,
1458
+ offset: int | None = None,
1459
+ search: str | None = None,
1460
+ cached_session: Client | None = None,
1461
+ ) -> list[GetTerrainResponse]:
1462
+ raw = self._list_directus_items(
1463
+ ENDPOINT_TERRAINS,
1464
+ limit=limit,
1465
+ filter_criteria=filter_criteria,
1466
+ sort=sort,
1467
+ offset=offset,
1468
+ search=search,
1469
+ cached_session=cached_session,
1470
+ )
1471
+ return [GetTerrainResponse.from_dict(r) for r in raw if r]
1472
+
1473
+ async def list_terrains_async(
1474
+ self,
1475
+ limit: int = 10,
1476
+ filter_criteria: str | None = None,
1477
+ sort: list[str] | None = None,
1478
+ offset: int | None = None,
1479
+ search: str | None = None,
1480
+ cached_session: httpx.AsyncClient | None = None,
1481
+ ) -> list[GetTerrainResponse]:
1482
+ raw = await self._list_directus_items_async(
1483
+ ENDPOINT_TERRAINS,
1484
+ limit=limit,
1485
+ filter_criteria=filter_criteria,
1486
+ sort=sort,
1487
+ offset=offset,
1488
+ search=search,
1489
+ cached_session=cached_session,
1490
+ )
1491
+ return [GetTerrainResponse.from_dict(r) for r in raw if r]
1492
+
1493
+ # ---------- list_tournois ----------
1494
+ def list_tournois(
1495
+ self,
1496
+ limit: int = 10,
1497
+ filter_criteria: str | None = None,
1498
+ sort: list[str] | None = None,
1499
+ offset: int | None = None,
1500
+ search: str | None = None,
1501
+ cached_session: Client | None = None,
1502
+ ) -> list[GetTournoiResponse]:
1503
+ raw = self._list_directus_items(
1504
+ ENDPOINT_TOURNOIS,
1505
+ limit=limit,
1506
+ filter_criteria=filter_criteria,
1507
+ sort=sort,
1508
+ offset=offset,
1509
+ search=search,
1510
+ cached_session=cached_session,
1511
+ )
1512
+ return [GetTournoiResponse.from_dict(r) for r in raw if r]
1513
+
1514
+ async def list_tournois_async(
1515
+ self,
1516
+ limit: int = 10,
1517
+ filter_criteria: str | None = None,
1518
+ sort: list[str] | None = None,
1519
+ offset: int | None = None,
1520
+ search: str | None = None,
1521
+ cached_session: httpx.AsyncClient | None = None,
1522
+ ) -> list[GetTournoiResponse]:
1523
+ raw = await self._list_directus_items_async(
1524
+ ENDPOINT_TOURNOIS,
1525
+ limit=limit,
1526
+ filter_criteria=filter_criteria,
1527
+ sort=sort,
1528
+ offset=offset,
1529
+ search=search,
1530
+ cached_session=cached_session,
1531
+ )
1532
+ return [GetTournoiResponse.from_dict(r) for r in raw if r]
1533
+
1534
+ # ---------- list_engagements ----------
1535
+ def list_engagements(
1536
+ self,
1537
+ limit: int = 10,
1538
+ filter_criteria: str | None = None,
1539
+ sort: list[str] | None = None,
1540
+ offset: int | None = None,
1541
+ search: str | None = None,
1542
+ cached_session: Client | None = None,
1543
+ ) -> list[GetEngagementResponse]:
1544
+ raw = self._list_directus_items(
1545
+ ENDPOINT_ENGAGEMENTS,
1546
+ limit=limit,
1547
+ filter_criteria=filter_criteria,
1548
+ sort=sort,
1549
+ offset=offset,
1550
+ search=search,
1551
+ cached_session=cached_session,
1552
+ )
1553
+ return [GetEngagementResponse.from_dict(r) for r in raw if r]
1554
+
1555
+ async def list_engagements_async(
1556
+ self,
1557
+ limit: int = 10,
1558
+ filter_criteria: str | None = None,
1559
+ sort: list[str] | None = None,
1560
+ offset: int | None = None,
1561
+ search: str | None = None,
1562
+ cached_session: httpx.AsyncClient | None = None,
1563
+ ) -> list[GetEngagementResponse]:
1564
+ raw = await self._list_directus_items_async(
1565
+ ENDPOINT_ENGAGEMENTS,
1566
+ limit=limit,
1567
+ filter_criteria=filter_criteria,
1568
+ sort=sort,
1569
+ offset=offset,
1570
+ search=search,
1571
+ cached_session=cached_session,
1572
+ )
1573
+ return [GetEngagementResponse.from_dict(r) for r in raw if r]
1574
+
1575
+ # ---------- list_formations ----------
1576
+ def list_formations(
1577
+ self,
1578
+ limit: int = 10,
1579
+ filter_criteria: str | None = None,
1580
+ sort: list[str] | None = None,
1581
+ offset: int | None = None,
1582
+ search: str | None = None,
1583
+ cached_session: Client | None = None,
1584
+ ) -> list[GetFormationResponse]:
1585
+ raw = self._list_directus_items(
1586
+ ENDPOINT_FORMATIONS,
1587
+ limit=limit,
1588
+ filter_criteria=filter_criteria,
1589
+ sort=sort,
1590
+ offset=offset,
1591
+ search=search,
1592
+ cached_session=cached_session,
1593
+ )
1594
+ return [GetFormationResponse.from_dict(r) for r in raw if r]
1595
+
1596
+ async def list_formations_async(
1597
+ self,
1598
+ limit: int = 10,
1599
+ filter_criteria: str | None = None,
1600
+ sort: list[str] | None = None,
1601
+ offset: int | None = None,
1602
+ search: str | None = None,
1603
+ cached_session: httpx.AsyncClient | None = None,
1604
+ ) -> list[GetFormationResponse]:
1605
+ raw = await self._list_directus_items_async(
1606
+ ENDPOINT_FORMATIONS,
1607
+ limit=limit,
1608
+ filter_criteria=filter_criteria,
1609
+ sort=sort,
1610
+ offset=offset,
1611
+ search=search,
1612
+ cached_session=cached_session,
1613
+ )
1614
+ return [GetFormationResponse.from_dict(r) for r in raw if r]
1615
+
1616
+ # ---------- list_entraineurs ----------
1617
+ def list_entraineurs(
1618
+ self,
1619
+ limit: int = 10,
1620
+ filter_criteria: str | None = None,
1621
+ sort: list[str] | None = None,
1622
+ offset: int | None = None,
1623
+ search: str | None = None,
1624
+ cached_session: Client | None = None,
1625
+ ) -> list[GetEntraineurResponse]:
1626
+ raw = self._list_directus_items(
1627
+ ENDPOINT_ENTRAINEURS,
1628
+ limit=limit,
1629
+ filter_criteria=filter_criteria,
1630
+ sort=sort,
1631
+ offset=offset,
1632
+ search=search,
1633
+ cached_session=cached_session,
1634
+ )
1635
+ return [GetEntraineurResponse.from_dict(r) for r in raw if r]
1636
+
1637
+ async def list_entraineurs_async(
1638
+ self,
1639
+ limit: int = 10,
1640
+ filter_criteria: str | None = None,
1641
+ sort: list[str] | None = None,
1642
+ offset: int | None = None,
1643
+ search: str | None = None,
1644
+ cached_session: httpx.AsyncClient | None = None,
1645
+ ) -> list[GetEntraineurResponse]:
1646
+ raw = await self._list_directus_items_async(
1647
+ ENDPOINT_ENTRAINEURS,
1648
+ limit=limit,
1649
+ filter_criteria=filter_criteria,
1650
+ sort=sort,
1651
+ offset=offset,
1652
+ search=search,
1653
+ cached_session=cached_session,
1654
+ )
1655
+ return [GetEntraineurResponse.from_dict(r) for r in raw if r]
1656
+
1657
+ # ---------- list_communes ----------
1658
+ def list_communes(
1659
+ self,
1660
+ limit: int = 10,
1661
+ filter_criteria: str | None = None,
1662
+ sort: list[str] | None = None,
1663
+ offset: int | None = None,
1664
+ search: str | None = None,
1665
+ cached_session: Client | None = None,
1666
+ ) -> list[GetCommuneResponse]:
1667
+ raw = self._list_directus_items(
1668
+ ENDPOINT_COMMUNES,
1669
+ limit=limit,
1670
+ filter_criteria=filter_criteria,
1671
+ sort=sort,
1672
+ offset=offset,
1673
+ search=search,
1674
+ cached_session=cached_session,
1675
+ )
1676
+ return [GetCommuneResponse.from_dict(r) for r in raw if r]
1677
+
1678
+ async def list_communes_async(
1679
+ self,
1680
+ limit: int = 10,
1681
+ filter_criteria: str | None = None,
1682
+ sort: list[str] | None = None,
1683
+ offset: int | None = None,
1684
+ search: str | None = None,
1685
+ cached_session: httpx.AsyncClient | None = None,
1686
+ ) -> list[GetCommuneResponse]:
1687
+ raw = await self._list_directus_items_async(
1688
+ ENDPOINT_COMMUNES,
1689
+ limit=limit,
1690
+ filter_criteria=filter_criteria,
1691
+ sort=sort,
1692
+ offset=offset,
1693
+ search=search,
1694
+ cached_session=cached_session,
1695
+ )
1696
+ return [GetCommuneResponse.from_dict(r) for r in raw if r]
1697
+
1698
+ # ---------- list_officiels ----------
1699
+ def list_officiels(
1700
+ self,
1701
+ limit: int = 10,
1702
+ filter_criteria: str | None = None,
1703
+ sort: list[str] | None = None,
1704
+ offset: int | None = None,
1705
+ search: str | None = None,
1706
+ cached_session: Client | None = None,
1707
+ ) -> list[GetOfficielResponse]:
1708
+ raw = self._list_directus_items(
1709
+ ENDPOINT_OFFICIELS,
1710
+ limit=limit,
1711
+ filter_criteria=filter_criteria,
1712
+ sort=sort,
1713
+ offset=offset,
1714
+ search=search,
1715
+ cached_session=cached_session,
1716
+ )
1717
+ return [GetOfficielResponse.from_dict(r) for r in raw if r]
1718
+
1719
+ async def list_officiels_async(
1720
+ self,
1721
+ limit: int = 10,
1722
+ filter_criteria: str | None = None,
1723
+ sort: list[str] | None = None,
1724
+ offset: int | None = None,
1725
+ search: str | None = None,
1726
+ cached_session: httpx.AsyncClient | None = None,
1727
+ ) -> list[GetOfficielResponse]:
1728
+ raw = await self._list_directus_items_async(
1729
+ ENDPOINT_OFFICIELS,
1730
+ limit=limit,
1731
+ filter_criteria=filter_criteria,
1732
+ sort=sort,
1733
+ offset=offset,
1734
+ search=search,
1735
+ cached_session=cached_session,
1736
+ )
1737
+ return [GetOfficielResponse.from_dict(r) for r in raw if r]
1738
+
1739
+ # ---------- list_pratiques ----------
1740
+ def list_pratiques(
1741
+ self,
1742
+ limit: int = 10,
1743
+ filter_criteria: str | None = None,
1744
+ sort: list[str] | None = None,
1745
+ offset: int | None = None,
1746
+ search: str | None = None,
1747
+ cached_session: Client | None = None,
1748
+ ) -> list[GetPratiqueResponse]:
1749
+ raw = self._list_directus_items(
1750
+ ENDPOINT_PRATIQUES,
1751
+ limit=limit,
1752
+ filter_criteria=filter_criteria,
1753
+ sort=sort,
1754
+ offset=offset,
1755
+ search=search,
1756
+ cached_session=cached_session,
1757
+ )
1758
+ return [GetPratiqueResponse.from_dict(r) for r in raw if r]
1759
+
1760
+ async def list_pratiques_async(
1761
+ self,
1762
+ limit: int = 10,
1763
+ filter_criteria: str | None = None,
1764
+ sort: list[str] | None = None,
1765
+ offset: int | None = None,
1766
+ search: str | None = None,
1767
+ cached_session: httpx.AsyncClient | None = None,
1768
+ ) -> list[GetPratiqueResponse]:
1769
+ raw = await self._list_directus_items_async(
1770
+ ENDPOINT_PRATIQUES,
1771
+ limit=limit,
1772
+ filter_criteria=filter_criteria,
1773
+ sort=sort,
1774
+ offset=offset,
1775
+ search=search,
1776
+ cached_session=cached_session,
1777
+ )
1778
+ return [GetPratiqueResponse.from_dict(r) for r in raw if r]
1779
+
1780
+ # ---------- Pagination helper: list all items ----------
1781
+ def _list_all_directus_items(
1782
+ self,
1783
+ endpoint: str,
1784
+ model_cls,
1785
+ filter_criteria: str | None = None,
1786
+ sort: list[str] | None = None,
1787
+ search: str | None = None,
1788
+ page_size: int = 100,
1789
+ max_items: int = 10000,
1790
+ cached_session: Client | None = None,
1791
+ ) -> list:
1792
+ results: list = []
1793
+ offset = 0
1794
+ while len(results) < max_items:
1795
+ batch = self._list_directus_items(
1796
+ endpoint,
1797
+ limit=page_size,
1798
+ filter_criteria=filter_criteria,
1799
+ sort=sort,
1800
+ offset=offset,
1801
+ search=search,
1802
+ cached_session=cached_session,
1803
+ )
1804
+ if not batch:
1805
+ break
1806
+ results.extend([model_cls.from_dict(r) for r in batch if r])
1807
+ if len(batch) < page_size:
1808
+ break
1809
+ offset += page_size
1810
+ return results[:max_items]
1811
+
1812
+ def list_all_rencontres(
1813
+ self,
1814
+ filter_criteria: str | None = None,
1815
+ sort: list[str] | None = None,
1816
+ search: str | None = None,
1817
+ page_size: int = 100,
1818
+ max_items: int = 10000,
1819
+ cached_session: Client | None = None,
1820
+ ) -> list[GetRencontreResponse]:
1821
+ return self._list_all_directus_items(
1822
+ ENDPOINT_RENCONTRES,
1823
+ GetRencontreResponse,
1824
+ filter_criteria=filter_criteria,
1825
+ sort=sort,
1826
+ search=search,
1827
+ page_size=page_size,
1828
+ max_items=max_items,
1829
+ cached_session=cached_session,
1830
+ )
1831
+
1832
+ def list_all_salles(
1833
+ self,
1834
+ filter_criteria: str | None = None,
1835
+ sort: list[str] | None = None,
1836
+ search: str | None = None,
1837
+ page_size: int = 100,
1838
+ max_items: int = 10000,
1839
+ cached_session: Client | None = None,
1840
+ ) -> list[GetSalleResponse]:
1841
+ return self._list_all_directus_items(
1842
+ ENDPOINT_SALLES,
1843
+ GetSalleResponse,
1844
+ filter_criteria=filter_criteria,
1845
+ sort=sort,
1846
+ search=search,
1847
+ page_size=page_size,
1848
+ max_items=max_items,
1849
+ cached_session=cached_session,
1850
+ )
1851
+
1852
+ def list_all_terrains(
1853
+ self,
1854
+ filter_criteria: str | None = None,
1855
+ sort: list[str] | None = None,
1856
+ search: str | None = None,
1857
+ page_size: int = 100,
1858
+ max_items: int = 10000,
1859
+ cached_session: Client | None = None,
1860
+ ) -> list[GetTerrainResponse]:
1861
+ return self._list_all_directus_items(
1862
+ ENDPOINT_TERRAINS,
1863
+ GetTerrainResponse,
1864
+ filter_criteria=filter_criteria,
1865
+ sort=sort,
1866
+ search=search,
1867
+ page_size=page_size,
1868
+ max_items=max_items,
1869
+ cached_session=cached_session,
1870
+ )
1871
+
1872
+ def list_all_tournois(
1873
+ self,
1874
+ filter_criteria: str | None = None,
1875
+ sort: list[str] | None = None,
1876
+ search: str | None = None,
1877
+ page_size: int = 100,
1878
+ max_items: int = 10000,
1879
+ cached_session: Client | None = None,
1880
+ ) -> list[GetTournoiResponse]:
1881
+ return self._list_all_directus_items(
1882
+ ENDPOINT_TOURNOIS,
1883
+ GetTournoiResponse,
1884
+ filter_criteria=filter_criteria,
1885
+ sort=sort,
1886
+ search=search,
1887
+ page_size=page_size,
1888
+ max_items=max_items,
1889
+ cached_session=cached_session,
1890
+ )
1891
+
1892
+ def list_all_engagements(
1893
+ self,
1894
+ filter_criteria: str | None = None,
1895
+ sort: list[str] | None = None,
1896
+ search: str | None = None,
1897
+ page_size: int = 100,
1898
+ max_items: int = 10000,
1899
+ cached_session: Client | None = None,
1900
+ ) -> list[GetEngagementResponse]:
1901
+ return self._list_all_directus_items(
1902
+ ENDPOINT_ENGAGEMENTS,
1903
+ GetEngagementResponse,
1904
+ filter_criteria=filter_criteria,
1905
+ sort=sort,
1906
+ search=search,
1907
+ page_size=page_size,
1908
+ max_items=max_items,
1909
+ cached_session=cached_session,
1910
+ )
1911
+
1912
+ def list_all_formations(
1913
+ self,
1914
+ filter_criteria: str | None = None,
1915
+ sort: list[str] | None = None,
1916
+ search: str | None = None,
1917
+ page_size: int = 100,
1918
+ max_items: int = 10000,
1919
+ cached_session: Client | None = None,
1920
+ ) -> list[GetFormationResponse]:
1921
+ return self._list_all_directus_items(
1922
+ ENDPOINT_FORMATIONS,
1923
+ GetFormationResponse,
1924
+ filter_criteria=filter_criteria,
1925
+ sort=sort,
1926
+ search=search,
1927
+ page_size=page_size,
1928
+ max_items=max_items,
1929
+ cached_session=cached_session,
1930
+ )
1931
+
1932
+ def list_all_entraineurs(
1933
+ self,
1934
+ filter_criteria: str | None = None,
1935
+ sort: list[str] | None = None,
1936
+ search: str | None = None,
1937
+ page_size: int = 100,
1938
+ max_items: int = 10000,
1939
+ cached_session: Client | None = None,
1940
+ ) -> list[GetEntraineurResponse]:
1941
+ return self._list_all_directus_items(
1942
+ ENDPOINT_ENTRAINEURS,
1943
+ GetEntraineurResponse,
1944
+ filter_criteria=filter_criteria,
1945
+ sort=sort,
1946
+ search=search,
1947
+ page_size=page_size,
1948
+ max_items=max_items,
1949
+ cached_session=cached_session,
1950
+ )
1951
+
1952
+ def list_all_communes(
1953
+ self,
1954
+ filter_criteria: str | None = None,
1955
+ sort: list[str] | None = None,
1956
+ search: str | None = None,
1957
+ page_size: int = 100,
1958
+ max_items: int = 10000,
1959
+ cached_session: Client | None = None,
1960
+ ) -> list[GetCommuneResponse]:
1961
+ return self._list_all_directus_items(
1962
+ ENDPOINT_COMMUNES,
1963
+ GetCommuneResponse,
1964
+ filter_criteria=filter_criteria,
1965
+ sort=sort,
1966
+ search=search,
1967
+ page_size=page_size,
1968
+ max_items=max_items,
1969
+ cached_session=cached_session,
1970
+ )
1971
+
1972
+ def list_all_officiels(
1973
+ self,
1974
+ filter_criteria: str | None = None,
1975
+ sort: list[str] | None = None,
1976
+ search: str | None = None,
1977
+ page_size: int = 100,
1978
+ max_items: int = 10000,
1979
+ cached_session: Client | None = None,
1980
+ ) -> list[GetOfficielResponse]:
1981
+ return self._list_all_directus_items(
1982
+ ENDPOINT_OFFICIELS,
1983
+ GetOfficielResponse,
1984
+ filter_criteria=filter_criteria,
1985
+ sort=sort,
1986
+ search=search,
1987
+ page_size=page_size,
1988
+ max_items=max_items,
1989
+ cached_session=cached_session,
1990
+ )
1991
+
1992
+ def list_all_pratiques(
1993
+ self,
1994
+ filter_criteria: str | None = None,
1995
+ sort: list[str] | None = None,
1996
+ search: str | None = None,
1997
+ page_size: int = 100,
1998
+ max_items: int = 10000,
1999
+ cached_session: Client | None = None,
2000
+ ) -> list[GetPratiqueResponse]:
2001
+ return self._list_all_directus_items(
2002
+ ENDPOINT_PRATIQUES,
2003
+ GetPratiqueResponse,
2004
+ filter_criteria=filter_criteria,
2005
+ sort=sort,
2006
+ search=search,
2007
+ page_size=page_size,
2008
+ max_items=max_items,
2009
+ cached_session=cached_session,
2010
+ )
2011
+
2012
+ async def _list_all_directus_items_async(
2013
+ self,
2014
+ endpoint: str,
2015
+ model_cls,
2016
+ filter_criteria: str | None = None,
2017
+ sort: list[str] | None = None,
2018
+ search: str | None = None,
2019
+ page_size: int = 100,
2020
+ max_items: int = 10000,
2021
+ cached_session: httpx.AsyncClient | None = None,
2022
+ ) -> list:
2023
+ results: list = []
2024
+ offset = 0
2025
+ while len(results) < max_items:
2026
+ batch = await self._list_directus_items_async(
2027
+ endpoint,
2028
+ limit=page_size,
2029
+ filter_criteria=filter_criteria,
2030
+ sort=sort,
2031
+ offset=offset,
2032
+ search=search,
2033
+ cached_session=cached_session,
2034
+ )
2035
+ if not batch:
2036
+ break
2037
+ results.extend([model_cls.from_dict(r) for r in batch if r])
2038
+ if len(batch) < page_size:
2039
+ break
2040
+ offset += page_size
2041
+ return results[:max_items]
2042
+
2043
+ async def list_all_rencontres_async(
2044
+ self,
2045
+ filter_criteria: str | None = None,
2046
+ sort: list[str] | None = None,
2047
+ search: str | None = None,
2048
+ page_size: int = 100,
2049
+ max_items: int = 10000,
2050
+ cached_session: httpx.AsyncClient | None = None,
2051
+ ) -> list[GetRencontreResponse]:
2052
+ return await self._list_all_directus_items_async(
2053
+ ENDPOINT_RENCONTRES,
2054
+ GetRencontreResponse,
2055
+ filter_criteria=filter_criteria,
2056
+ sort=sort,
2057
+ search=search,
2058
+ page_size=page_size,
2059
+ max_items=max_items,
2060
+ cached_session=cached_session,
2061
+ )
2062
+
2063
+ async def list_all_salles_async(
2064
+ self,
2065
+ filter_criteria: str | None = None,
2066
+ sort: list[str] | None = None,
2067
+ search: str | None = None,
2068
+ page_size: int = 100,
2069
+ max_items: int = 10000,
2070
+ cached_session: httpx.AsyncClient | None = None,
2071
+ ) -> list[GetSalleResponse]:
2072
+ return await self._list_all_directus_items_async(
2073
+ ENDPOINT_SALLES,
2074
+ GetSalleResponse,
2075
+ filter_criteria=filter_criteria,
2076
+ sort=sort,
2077
+ search=search,
2078
+ page_size=page_size,
2079
+ max_items=max_items,
2080
+ cached_session=cached_session,
2081
+ )
2082
+
2083
+ async def list_all_terrains_async(
2084
+ self,
2085
+ filter_criteria: str | None = None,
2086
+ sort: list[str] | None = None,
2087
+ search: str | None = None,
2088
+ page_size: int = 100,
2089
+ max_items: int = 10000,
2090
+ cached_session: httpx.AsyncClient | None = None,
2091
+ ) -> list[GetTerrainResponse]:
2092
+ return await self._list_all_directus_items_async(
2093
+ ENDPOINT_TERRAINS,
2094
+ GetTerrainResponse,
2095
+ filter_criteria=filter_criteria,
2096
+ sort=sort,
2097
+ search=search,
2098
+ page_size=page_size,
2099
+ max_items=max_items,
2100
+ cached_session=cached_session,
2101
+ )
2102
+
2103
+ async def list_all_tournois_async(
2104
+ self,
2105
+ filter_criteria: str | None = None,
2106
+ sort: list[str] | None = None,
2107
+ search: str | None = None,
2108
+ page_size: int = 100,
2109
+ max_items: int = 10000,
2110
+ cached_session: httpx.AsyncClient | None = None,
2111
+ ) -> list[GetTournoiResponse]:
2112
+ return await self._list_all_directus_items_async(
2113
+ ENDPOINT_TOURNOIS,
2114
+ GetTournoiResponse,
2115
+ filter_criteria=filter_criteria,
2116
+ sort=sort,
2117
+ search=search,
2118
+ page_size=page_size,
2119
+ max_items=max_items,
2120
+ cached_session=cached_session,
2121
+ )
2122
+
2123
+ async def list_all_engagements_async(
2124
+ self,
2125
+ filter_criteria: str | None = None,
2126
+ sort: list[str] | None = None,
2127
+ search: str | None = None,
2128
+ page_size: int = 100,
2129
+ max_items: int = 10000,
2130
+ cached_session: httpx.AsyncClient | None = None,
2131
+ ) -> list[GetEngagementResponse]:
2132
+ return await self._list_all_directus_items_async(
2133
+ ENDPOINT_ENGAGEMENTS,
2134
+ GetEngagementResponse,
2135
+ filter_criteria=filter_criteria,
2136
+ sort=sort,
2137
+ search=search,
2138
+ page_size=page_size,
2139
+ max_items=max_items,
2140
+ cached_session=cached_session,
2141
+ )
2142
+
2143
+ async def list_all_formations_async(
2144
+ self,
2145
+ filter_criteria: str | None = None,
2146
+ sort: list[str] | None = None,
2147
+ search: str | None = None,
2148
+ page_size: int = 100,
2149
+ max_items: int = 10000,
2150
+ cached_session: httpx.AsyncClient | None = None,
2151
+ ) -> list[GetFormationResponse]:
2152
+ return await self._list_all_directus_items_async(
2153
+ ENDPOINT_FORMATIONS,
2154
+ GetFormationResponse,
2155
+ filter_criteria=filter_criteria,
2156
+ sort=sort,
2157
+ search=search,
2158
+ page_size=page_size,
2159
+ max_items=max_items,
2160
+ cached_session=cached_session,
2161
+ )
2162
+
2163
+ async def list_all_entraineurs_async(
2164
+ self,
2165
+ filter_criteria: str | None = None,
2166
+ sort: list[str] | None = None,
2167
+ search: str | None = None,
2168
+ page_size: int = 100,
2169
+ max_items: int = 10000,
2170
+ cached_session: httpx.AsyncClient | None = None,
2171
+ ) -> list[GetEntraineurResponse]:
2172
+ return await self._list_all_directus_items_async(
2173
+ ENDPOINT_ENTRAINEURS,
2174
+ GetEntraineurResponse,
2175
+ filter_criteria=filter_criteria,
2176
+ sort=sort,
2177
+ search=search,
2178
+ page_size=page_size,
2179
+ max_items=max_items,
2180
+ cached_session=cached_session,
2181
+ )
2182
+
2183
+ async def list_all_communes_async(
2184
+ self,
2185
+ filter_criteria: str | None = None,
2186
+ sort: list[str] | None = None,
2187
+ search: str | None = None,
2188
+ page_size: int = 100,
2189
+ max_items: int = 10000,
2190
+ cached_session: httpx.AsyncClient | None = None,
2191
+ ) -> list[GetCommuneResponse]:
2192
+ return await self._list_all_directus_items_async(
2193
+ ENDPOINT_COMMUNES,
2194
+ GetCommuneResponse,
2195
+ filter_criteria=filter_criteria,
2196
+ sort=sort,
2197
+ search=search,
2198
+ page_size=page_size,
2199
+ max_items=max_items,
2200
+ cached_session=cached_session,
2201
+ )
2202
+
2203
+ async def list_all_officiels_async(
2204
+ self,
2205
+ filter_criteria: str | None = None,
2206
+ sort: list[str] | None = None,
2207
+ search: str | None = None,
2208
+ page_size: int = 100,
2209
+ max_items: int = 10000,
2210
+ cached_session: httpx.AsyncClient | None = None,
2211
+ ) -> list[GetOfficielResponse]:
2212
+ return await self._list_all_directus_items_async(
2213
+ ENDPOINT_OFFICIELS,
2214
+ GetOfficielResponse,
2215
+ filter_criteria=filter_criteria,
2216
+ sort=sort,
2217
+ search=search,
2218
+ page_size=page_size,
2219
+ max_items=max_items,
2220
+ cached_session=cached_session,
2221
+ )
2222
+
2223
+ async def list_all_pratiques_async(
2224
+ self,
2225
+ filter_criteria: str | None = None,
2226
+ sort: list[str] | None = None,
2227
+ search: str | None = None,
2228
+ page_size: int = 100,
2229
+ max_items: int = 10000,
2230
+ cached_session: httpx.AsyncClient | None = None,
2231
+ ) -> list[GetPratiqueResponse]:
2232
+ return await self._list_all_directus_items_async(
2233
+ ENDPOINT_PRATIQUES,
2234
+ GetPratiqueResponse,
2235
+ filter_criteria=filter_criteria,
2236
+ sort=sort,
2237
+ search=search,
2238
+ page_size=page_size,
2239
+ max_items=max_items,
2240
+ cached_session=cached_session,
2241
+ )
2242
+
2243
+ def get_genius_sport_match(
2244
+ self,
2245
+ id: str,
2246
+ fields: list[str] | None = None,
2247
+ cached_session: Client | None = None,
2248
+ ) -> dict[str, Any] | None:
2249
+ """Retrieves detailed Genius Sports match statistics."""
2250
+ return self._get_directus_item(
2251
+ ENDPOINT_GENIUS_SPORT_MATCHES,
2252
+ id,
2253
+ fields=fields,
2254
+ cached_session=cached_session,
2255
+ )
2256
+
2257
+ def list_genius_sport_matches(
2258
+ self,
2259
+ limit: int = 10,
2260
+ fields: list[str] | None = None,
2261
+ filter_criteria: str | None = None,
2262
+ sort: str | list[str] | None = None,
2263
+ cached_session: Client | None = None,
2264
+ ) -> list[dict[str, Any]]:
2265
+ """Lists Genius Sports match statistics."""
2266
+ return self._list_directus_items(
2267
+ ENDPOINT_GENIUS_SPORT_MATCHES,
2268
+ limit=limit,
2269
+ fields=fields,
2270
+ filter_criteria=filter_criteria,
2271
+ sort=sort,
2272
+ cached_session=cached_session,
2273
+ )
2274
+
2275
+ async def get_genius_sport_match_async(
2276
+ self,
2277
+ id: str,
2278
+ fields: list[str] | None = None,
2279
+ cached_session: httpx.AsyncClient | None = None,
2280
+ ) -> dict[str, Any] | None:
2281
+ """Asynchronously retrieves detailed Genius Sports match statistics."""
2282
+ return await self._get_directus_item_async(
2283
+ ENDPOINT_GENIUS_SPORT_MATCHES,
2284
+ id,
2285
+ fields=fields,
2286
+ cached_session=cached_session,
2287
+ )
2288
+
2289
+ async def list_genius_sport_matches_async(
2290
+ self,
2291
+ limit: int = 10,
2292
+ fields: list[str] | None = None,
2293
+ filter_criteria: str | None = None,
2294
+ sort: str | list[str] | None = None,
2295
+ cached_session: httpx.AsyncClient | None = None,
2296
+ ) -> list[dict[str, Any]]:
2297
+ """Asynchronously lists Genius Sports match statistics."""
2298
+ return await self._list_directus_items_async(
2299
+ ENDPOINT_GENIUS_SPORT_MATCHES,
2300
+ limit=limit,
2301
+ fields=fields,
2302
+ filter_criteria=filter_criteria,
2303
+ sort=sort,
2304
+ cached_session=cached_session,
2305
+ )
2306
+
2307
+ def list_genius_sports_live_logs(
2308
+ self,
2309
+ limit: int = 10,
2310
+ fields: list[str] | None = None,
2311
+ filter_criteria: str | None = None,
2312
+ sort: str | list[str] | None = None,
2313
+ cached_session: Client | None = None,
2314
+ ) -> list[dict[str, Any]]:
2315
+ """Lists Genius Sports live logs."""
2316
+ return self._list_directus_items(
2317
+ ENDPOINT_GENIUS_SPORTS_LIVE_LOGS,
2318
+ limit=limit,
2319
+ fields=fields,
2320
+ filter_criteria=filter_criteria,
2321
+ sort=sort,
2322
+ cached_session=cached_session,
2323
+ )
2324
+
2325
+ def get_rematch_video(
2326
+ self,
2327
+ id: str,
2328
+ fields: list[str] | None = None,
2329
+ cached_session: Client | None = None,
2330
+ ) -> dict[str, Any] | None:
2331
+ """Retrieves a Rematch video linked to FFBB data."""
2332
+ return self._get_directus_item(
2333
+ ENDPOINT_REMATCH_VIDEOS, id, fields=fields, cached_session=cached_session
2334
+ )
2335
+
2336
+ def list_rematch_videos(
2337
+ self,
2338
+ limit: int = 10,
2339
+ fields: list[str] | None = None,
2340
+ filter_criteria: str | None = None,
2341
+ sort: str | list[str] | None = None,
2342
+ cached_session: Client | None = None,
2343
+ ) -> list[dict[str, Any]]:
2344
+ """Lists Rematch videos linked to FFBB data."""
2345
+ return self._list_directus_items(
2346
+ ENDPOINT_REMATCH_VIDEOS,
2347
+ limit=limit,
2348
+ fields=fields,
2349
+ filter_criteria=filter_criteria,
2350
+ sort=sort,
2351
+ cached_session=cached_session,
2352
+ )
2353
+
2354
+ async def get_rematch_video_async(
2355
+ self,
2356
+ id: str,
2357
+ fields: list[str] | None = None,
2358
+ cached_session: httpx.AsyncClient | None = None,
2359
+ ) -> dict[str, Any] | None:
2360
+ """Asynchronously retrieves a Rematch video linked to FFBB data."""
2361
+ return await self._get_directus_item_async(
2362
+ ENDPOINT_REMATCH_VIDEOS, id, fields=fields, cached_session=cached_session
2363
+ )
2364
+
2365
+ async def list_rematch_videos_async(
2366
+ self,
2367
+ limit: int = 10,
2368
+ fields: list[str] | None = None,
2369
+ filter_criteria: str | None = None,
2370
+ sort: str | list[str] | None = None,
2371
+ cached_session: httpx.AsyncClient | None = None,
2372
+ ) -> list[dict[str, Any]]:
2373
+ """Asynchronously lists Rematch videos linked to FFBB data."""
2374
+ return await self._list_directus_items_async(
2375
+ ENDPOINT_REMATCH_VIDEOS,
2376
+ limit=limit,
2377
+ fields=fields,
2378
+ filter_criteria=filter_criteria,
2379
+ sort=sort,
2380
+ cached_session=cached_session,
2381
+ )
2382
+
2383
+ def get_edf_match(
2384
+ self,
2385
+ id: str | int,
2386
+ fields: list[str] | None = None,
2387
+ cached_session: Client | None = None,
2388
+ ) -> dict[str, Any] | None:
2389
+ """Retrieves an Equipe de France match."""
2390
+ return self._get_directus_item(
2391
+ ENDPOINT_EDF_MATCHES, id, fields=fields, cached_session=cached_session
2392
+ )
2393
+
2394
+ def list_edf_matches(
2395
+ self,
2396
+ limit: int = 10,
2397
+ fields: list[str] | None = None,
2398
+ filter_criteria: str | None = None,
2399
+ sort: str | list[str] | None = None,
2400
+ cached_session: Client | None = None,
2401
+ ) -> list[dict[str, Any]]:
2402
+ """Lists Equipe de France matches."""
2403
+ return self._list_directus_items(
2404
+ ENDPOINT_EDF_MATCHES,
2405
+ limit=limit,
2406
+ fields=fields,
2407
+ filter_criteria=filter_criteria,
2408
+ sort=sort,
2409
+ cached_session=cached_session,
2410
+ )
2411
+
2412
+ def get_edf_player(
2413
+ self,
2414
+ id: str | int,
2415
+ fields: list[str] | None = None,
2416
+ cached_session: Client | None = None,
2417
+ ) -> dict[str, Any] | None:
2418
+ """Retrieves an Equipe de France player."""
2419
+ return self._get_directus_item(
2420
+ ENDPOINT_EDF_PLAYERS, id, fields=fields, cached_session=cached_session
2421
+ )
2422
+
2423
+ def list_edf_players(
2424
+ self,
2425
+ limit: int = 10,
2426
+ fields: list[str] | None = None,
2427
+ filter_criteria: str | None = None,
2428
+ sort: str | list[str] | None = None,
2429
+ cached_session: Client | None = None,
2430
+ ) -> list[dict[str, Any]]:
2431
+ """Lists Equipe de France players."""
2432
+ return self._list_directus_items(
2433
+ ENDPOINT_EDF_PLAYERS,
2434
+ limit=limit,
2435
+ fields=fields,
2436
+ filter_criteria=filter_criteria,
2437
+ sort=sort,
2438
+ cached_session=cached_session,
2439
+ )
2440
+
2441
+ def list_edf_teams(
2442
+ self,
2443
+ limit: int = 10,
2444
+ fields: list[str] | None = None,
2445
+ filter_criteria: str | None = None,
2446
+ sort: str | list[str] | None = None,
2447
+ cached_session: Client | None = None,
2448
+ ) -> list[dict[str, Any]]:
2449
+ """Lists Equipe de France teams."""
2450
+ return self._list_directus_items(
2451
+ ENDPOINT_EDF_TEAMS,
2452
+ limit=limit,
2453
+ fields=fields,
2454
+ filter_criteria=filter_criteria,
2455
+ sort=sort,
2456
+ cached_session=cached_session,
2457
+ )
2458
+
2459
+ def list_edf_rosters(
2460
+ self,
2461
+ limit: int = 10,
2462
+ fields: list[str] | None = None,
2463
+ filter_criteria: str | None = None,
2464
+ sort: str | list[str] | None = None,
2465
+ cached_session: Client | None = None,
2466
+ ) -> list[dict[str, Any]]:
2467
+ """Lists Equipe de France rosters."""
2468
+ return self._list_directus_items(
2469
+ ENDPOINT_EDF_ROSTERS,
2470
+ limit=limit,
2471
+ fields=fields,
2472
+ filter_criteria=filter_criteria,
2473
+ sort=sort,
2474
+ cached_session=cached_session,
2475
+ )