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,218 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import json
5
+ import threading
6
+ import time
7
+ from collections.abc import Sequence
8
+ from typing import Any
9
+
10
+ import httpx
11
+ from httpx import Client
12
+
13
+ from ..config import (
14
+ DEFAULT_USER_AGENT,
15
+ MEILISEARCH_BASE_URL,
16
+ MEILISEARCH_ENDPOINT_MULTI_SEARCH,
17
+ )
18
+ from ..helpers.http_requests_helper import catch_result
19
+ from ..helpers.http_requests_utils import (
20
+ http_post_json,
21
+ http_post_json_async,
22
+ )
23
+ from ..models.multi_search_query import MultiSearchQuery
24
+ from ..models.multi_search_results_class import MultiSearchResults
25
+ from ..utils.cache_manager import CacheManager
26
+ from ..utils.retry_utils import (
27
+ RetryConfig,
28
+ TimeoutConfig,
29
+ get_default_retry_config,
30
+ get_default_timeout_config,
31
+ )
32
+ from ..utils.secure_logging import get_secure_logger, mask_token
33
+
34
+ _APP_CACHE: dict[str, tuple[float, Any]] = {}
35
+ _APP_CACHE_LOCK = threading.Lock()
36
+ _APP_CACHE_TTL: int = 300 # secondes, modifiable
37
+
38
+
39
+ def _make_cache_key(queries: Sequence[MultiSearchQuery] | None) -> str:
40
+ payload = [q.to_dict() for q in queries] if queries else []
41
+ raw = json.dumps(payload, sort_keys=True, default=str)
42
+ return hashlib.md5(raw.encode(), usedforsecurity=False).hexdigest()
43
+
44
+
45
+ def _cache_get(key: str) -> Any | None:
46
+ with _APP_CACHE_LOCK:
47
+ entry = _APP_CACHE.get(key)
48
+ if entry is None:
49
+ return None
50
+ ts, value = entry
51
+ if time.monotonic() - ts > _APP_CACHE_TTL:
52
+ with _APP_CACHE_LOCK:
53
+ _APP_CACHE.pop(key, None)
54
+ return None
55
+ return value
56
+
57
+
58
+ def _cache_set(key: str, value: Any) -> None:
59
+ with _APP_CACHE_LOCK:
60
+ _APP_CACHE[key] = (time.monotonic(), value)
61
+
62
+
63
+ def _result_from_cached_payload(payload: Any) -> MultiSearchResults | None:
64
+ return MultiSearchResults.from_dict(payload) if isinstance(payload, dict) else None
65
+
66
+
67
+ def _cache_result_payload(key: str, result: MultiSearchResults) -> None:
68
+ _cache_set(key, result.to_dict())
69
+
70
+
71
+ def clear_meili_app_cache() -> None:
72
+ """Vide le cache applicatif (utile pour les tests)."""
73
+ with _APP_CACHE_LOCK:
74
+ _APP_CACHE.clear()
75
+
76
+
77
+ logger = get_secure_logger(__name__)
78
+
79
+
80
+ class MeilisearchClient:
81
+ url: str = ""
82
+ debug: bool = False
83
+ cached_session: Client | None = None
84
+ async_cached_session: httpx.AsyncClient | None = None
85
+ retry_config: RetryConfig | None = None
86
+ timeout_config: TimeoutConfig | None = None
87
+
88
+ def __init__(
89
+ self,
90
+ bearer_token: str,
91
+ url: str = MEILISEARCH_BASE_URL,
92
+ debug: bool = False,
93
+ cached_session: Client | None = None,
94
+ *,
95
+ async_cached_session: httpx.AsyncClient | None = None,
96
+ retry_config: RetryConfig | None = None,
97
+ timeout_config: TimeoutConfig | None = None,
98
+ ):
99
+ """
100
+ Initializes an instance of the MeilisearchClient class.
101
+
102
+ Args:
103
+ bearer_token (str): The bearer token used for authentication.
104
+ url (str, optional): The base URL.
105
+ Defaults to "https://meilisearch-prod.ffbb.app/".
106
+ debug (bool, optional): Whether to enable debug mode. Defaults to False.
107
+ cached_session (Client, optional): The cached session to use.
108
+ retry_config (RetryConfig, optional): Retry configuration. Defaults to None.
109
+ timeout_config (TimeoutConfig, optional): Timeout configuration.
110
+ Defaults to None.
111
+ """
112
+ if not bearer_token or not bearer_token.strip():
113
+ raise ValueError("bearer_token cannot be None, empty, or whitespace-only")
114
+
115
+ # Store token securely (private attribute)
116
+ self._bearer_token = bearer_token
117
+ self.url = url
118
+ self.debug = debug
119
+ self.cached_session = (
120
+ cached_session if cached_session else CacheManager().session
121
+ )
122
+ self.async_cached_session = (
123
+ async_cached_session
124
+ if async_cached_session
125
+ else CacheManager().async_session
126
+ )
127
+ self.headers = {
128
+ "Authorization": f"Bearer {self._bearer_token}",
129
+ "Content-Type": "application/json",
130
+ "user-agent": DEFAULT_USER_AGENT,
131
+ }
132
+
133
+ # Configure retry and timeout settings
134
+ self.retry_config = retry_config or get_default_retry_config()
135
+ self.timeout_config = timeout_config or get_default_timeout_config()
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(
144
+ f"MeilisearchClient initialized with token: {masked_token}"
145
+ )
146
+ self.logger.info(
147
+ f"Retry config: {self.retry_config.max_attempts} attempts, "
148
+ f"timeout: {self.timeout_config.total_timeout}s"
149
+ )
150
+ else:
151
+ self.logger.info("MeilisearchClient initialized successfully")
152
+
153
+ @property
154
+ def bearer_token(self) -> str:
155
+ """Get the bearer token."""
156
+ return self._bearer_token
157
+
158
+ def multi_search(
159
+ self,
160
+ queries: Sequence[MultiSearchQuery] | None = None,
161
+ cached_session: Client | None = None,
162
+ ) -> MultiSearchResults | None:
163
+ key = _make_cache_key(queries)
164
+ cached = _cache_get(key)
165
+ if cached is not None:
166
+ return _result_from_cached_payload(cached)
167
+
168
+ url = f"{self.url}{MEILISEARCH_ENDPOINT_MULTI_SEARCH}"
169
+ params = {"queries": [query.to_dict() for query in queries] if queries else []}
170
+ raw_data = catch_result(
171
+ lambda: http_post_json(
172
+ url,
173
+ self.headers,
174
+ params,
175
+ debug=self.debug,
176
+ cached_session=cached_session or self.cached_session,
177
+ retry_config=self.retry_config,
178
+ timeout_config=self.timeout_config,
179
+ )
180
+ )
181
+ result = MultiSearchResults.from_dict(raw_data) if raw_data else None
182
+ if result is not None:
183
+ _cache_result_payload(key, result)
184
+ return result
185
+
186
+ async def multi_search_async(
187
+ self,
188
+ queries: Sequence[MultiSearchQuery] | None = None,
189
+ cached_session: httpx.AsyncClient | None = None,
190
+ ) -> MultiSearchResults | None:
191
+ key = _make_cache_key(queries)
192
+ cached = _cache_get(key)
193
+ if cached is not None:
194
+ return _result_from_cached_payload(cached)
195
+
196
+ url = f"{self.url}{MEILISEARCH_ENDPOINT_MULTI_SEARCH}"
197
+ params = {"queries": [query.to_dict() for query in queries] if queries else []}
198
+ try:
199
+ raw_data = await http_post_json_async(
200
+ url,
201
+ self.headers,
202
+ params,
203
+ debug=self.debug,
204
+ cached_session=cached_session or self.async_cached_session,
205
+ retry_config=self.retry_config,
206
+ timeout_config=self.timeout_config,
207
+ )
208
+ result = MultiSearchResults.from_dict(raw_data) if raw_data else None
209
+ except (httpx.HTTPStatusError, httpx.RequestError) as e:
210
+ self.logger.warning("multi_search_async request failed: %s", e)
211
+ result = None
212
+ except Exception as e:
213
+ self.logger.error("multi_search_async unexpected error: %s", e)
214
+ result = None
215
+
216
+ if result is not None:
217
+ _cache_result_payload(key, result)
218
+ return result