1010from birdxplorer_api .openapi_doc import (
1111 V1DataNotesDocs ,
1212 V1DataPostsDocs ,
13+ V1DataSearchDocs ,
1314 V1DataTopicsDocs ,
1415 V1DataUserEnrollmentsDocs ,
1516)
2223 ParticipantId ,
2324 Post ,
2425 PostId ,
26+ SummaryString ,
2527 Topic ,
2628 TopicId ,
2729 TwitterTimestamp ,
5456 ),
5557]
5658
59+ SearchPaginationMetaWithExamples : TypeAlias = Annotated [
60+ PaginationMeta ,
61+ PydanticField (
62+ description = "ページネーション用情報。 リクエスト時に指定した offset / limit の値に応じて、次のページや前のページのリクエスト用 URL が設定される。" ,
63+ json_schema_extra = {
64+ "examples" : [
65+ {"next" : "http://birdxplorer.onrender.com/api/v1/data/search?offset=100&limit=100" , "prev" : "null" }
66+ ]
67+ },
68+ ),
69+ ]
70+
5771TopicListWithExamples : TypeAlias = Annotated [
5872 List [Topic ],
5973 PydanticField (
141155]
142156
143157
158+ class SearchedNote (BaseModel ):
159+ noteId : NoteId
160+ summary : Annotated [SummaryString , PydanticField (description = "コミュニティノートの本文" )]
161+ language : Annotated [LanguageIdentifier , PydanticField (description = "コミュニティノートの言語" )]
162+ topics : Annotated [List [Topic ], PydanticField (description = "コミュニティノートに関連付けられたトピックのリスト" )]
163+ postId : PostId
164+ current_status : Annotated [
165+ Annotated [
166+ str ,
167+ PydanticField (
168+ json_schema_extra = {
169+ "enum" : ["NEEDS_MORE_RATINGS" , "CURRENTLY_RATED_HELPFUL" , "CURRENTLY_RATED_NOT_HELPFUL" ]
170+ },
171+ ),
172+ ]
173+ | None ,
174+ PydanticField (
175+ description = "コミュニティノートの現在の評価状態" ,
176+ ),
177+ ]
178+ created_at : Annotated [
179+ TwitterTimestamp , PydanticField (description = "コミュニティノートの作成日時 (ミリ秒単位の UNIX EPOCH TIMESTAMP)" )
180+ ]
181+ post : Annotated [Post , PydanticField (description = "コミュニティノートに関連付けられた Post の情報" )]
182+
183+
184+ SearchWithExamples : TypeAlias = Annotated [
185+ List [SearchedNote ],
186+ PydanticField (
187+ description = "検索結果のノートのリスト" ,
188+ json_schema_extra = {
189+ "examples" : [
190+ {
191+ "noteId" : "1845672983001710655" ,
192+ "language" : "ja" ,
193+ "topics" : [
194+ {
195+ "topicId" : 26 ,
196+ "label" : {"ja" : "セキュリティ上の脅威" , "en" : "security threat" },
197+ "referenceCount" : 0 ,
198+ },
199+ {"topicId" : 47 , "label" : {"ja" : "検閲" , "en" : "Censorship" }, "referenceCount" : 0 },
200+ {"topicId" : 51 , "label" : {"ja" : "テクノロジー" , "en" : "technology" }, "referenceCount" : 0 },
201+ ],
202+ "summary" : "Content Security Policyは情報の持ち出しを防止する仕組みではありません。コンテンツインジェクションの脆弱性のリスクを軽減する仕組みです。適切なContent Security Policyがレスポンスヘッダーに設定されている場合でも、外部への通信をブロックできない点に注意が必要です。 Content Security Policy Level 3 https://w3c.github.io/webappsec-csp/" , # noqa: E501
203+ "currentStatus" : "NEEDS_MORE_RATINGS" ,
204+ "createdAt" : 1728877704750 ,
205+ "post" : {
206+ "postId" : "1846718284369912064" ,
207+ "xUserId" : "90954365" ,
208+ "xUser" : {
209+ "userId" : "90954365" ,
210+ "name" : "earthquakejapan" ,
211+ "profileImage" : "https://pbs.twimg.com/profile_images/1638600342/japan_rel96_normal.jpg" ,
212+ "followersCount" : 162934 ,
213+ "followingCount" : 6 ,
214+ },
215+ "text" : "今後48時間以内に日本ではマグニチュード6.0の地震が発生する可能性があります。地図をご覧ください。(10月17日~10月18日) - https://t.co/nuyiVdM4FW https://t.co/Xd6U9XkpbL" , # noqa: E501
216+ "mediaDetails" : [
217+ {
218+ "mediaKey" : "3_1846718279236177920-1846718284369912064" ,
219+ "type" : "photo" ,
220+ "url" : "https://pbs.twimg.com/media/GaDcfZoX0AAko2-.jpg" ,
221+ "width" : 900 ,
222+ "height" : 738 ,
223+ }
224+ ],
225+ "createdAt" : 1729094524000 ,
226+ "likeCount" : 451 ,
227+ "repostCount" : 104 ,
228+ "impressionCount" : 82378 ,
229+ "links" : [
230+ {
231+ "linkId" : "9c139b99-8111-e4f0-ad41-fc9e40d08722" ,
232+ "url" : "https://www.quakeprediction.com/Earthquake%20Forecast%20Japan.html" ,
233+ }
234+ ],
235+ "link" : "https://x.com/earthquakejapan/status/1846718284369912064" ,
236+ },
237+ },
238+ ]
239+ },
240+ ),
241+ ]
242+
243+
144244class TopicListResponse (BaseModel ):
145245 data : TopicListWithExamples
146246
@@ -155,6 +255,11 @@ class PostListResponse(BaseModel):
155255 meta : PostsPaginationMetaWithExamples
156256
157257
258+ class SearchResponse (BaseModel ):
259+ data : SearchWithExamples
260+ meta : SearchPaginationMetaWithExamples
261+
262+
158263def str_to_twitter_timestamp (s : str ) -> TwitterTimestamp :
159264 try :
160265 return TwitterTimestamp .from_int (int (s ))
@@ -310,4 +415,94 @@ def get_posts(
310415
311416 return PostListResponse (data = posts , meta = PaginationMeta (next = next_url , prev = prev_url ))
312417
418+ @router .get ("/search" , description = V1DataSearchDocs .description , response_model = SearchResponse )
419+ def search (
420+ note_includes_text : Union [None , str ] = Query (default = None , ** V1DataSearchDocs .params ["note_includes_text" ]),
421+ note_excludes_text : Union [None , str ] = Query (default = None , ** V1DataSearchDocs .params ["note_excludes_text" ]),
422+ post_includes_text : Union [None , str ] = Query (default = None , ** V1DataSearchDocs .params ["post_includes_text" ]),
423+ post_excludes_text : Union [None , str ] = Query (default = None , ** V1DataSearchDocs .params ["post_excludes_text" ]),
424+ language : Union [LanguageIdentifier , None ] = Query (default = None , ** V1DataSearchDocs .params ["language" ]),
425+ topic_ids : Union [List [TopicId ], None ] = Query (default = None , ** V1DataSearchDocs .params ["topic_ids" ]),
426+ note_status : Union [None , List [str ]] = Query (default = None , ** V1DataSearchDocs .params ["note_status" ]),
427+ note_created_at_from : Union [None , TwitterTimestamp , str ] = Query (
428+ default = None , ** V1DataSearchDocs .params ["note_created_at_from" ]
429+ ),
430+ note_created_at_to : Union [None , TwitterTimestamp , str ] = Query (
431+ default = None , ** V1DataSearchDocs .params ["note_created_at_to" ]
432+ ),
433+ x_user_names : Union [List [str ], None ] = Query (default = None , ** V1DataSearchDocs .params ["x_user_name" ]),
434+ x_user_followers_count_from : Union [None , int ] = Query (
435+ default = None , ** V1DataSearchDocs .params ["x_user_followers_count_from" ]
436+ ),
437+ x_user_follow_count_from : Union [None , int ] = Query (
438+ default = None , ** V1DataSearchDocs .params ["x_user_follow_count_from" ]
439+ ),
440+ post_favorite_count_from : Union [None , int ] = Query (
441+ default = None , ** V1DataSearchDocs .params ["post_favorite_count_from" ]
442+ ),
443+ post_repost_count_from : Union [None , int ] = Query (
444+ default = None , ** V1DataSearchDocs .params ["post_repost_count_from" ]
445+ ),
446+ post_impression_count_from : Union [None , int ] = Query (
447+ default = None , ** V1DataSearchDocs .params ["post_impression_count_from" ]
448+ ),
449+ post_includes_media : bool = Query (default = True , ** V1DataSearchDocs .params ["post_includes_media" ]),
450+ offset : int = Query (default = 0 , ge = 0 , ** V1DataSearchDocs .params ["offset" ]),
451+ limit : int = Query (default = 100 , gt = 0 , le = 1000 , ** V1DataSearchDocs .params ["limit" ]),
452+ ) -> SearchResponse :
453+ return SearchResponse (
454+ data = [
455+ SearchedNote (
456+ noteId = "1845672983001710655" ,
457+ language = "ja" ,
458+ topics = [
459+ {
460+ "topicId" : 26 ,
461+ "label" : {"ja" : "セキュリティ上の脅威" , "en" : "security threat" },
462+ "referenceCount" : 0 ,
463+ },
464+ {"topicId" : 47 , "label" : {"ja" : "検閲" , "en" : "Censorship" }, "referenceCount" : 0 },
465+ {"topicId" : 51 , "label" : {"ja" : "テクノロジー" , "en" : "technology" }, "referenceCount" : 0 },
466+ ],
467+ postId = "1846718284369912064" ,
468+ summary = "Content Security Policyは情報の持ち出しを防止する仕組みではありません。コンテンツインジェクションの脆弱性のリスクを軽減する仕組みです。適切なContent Security Policyがレスポンスヘッダーに設定されている場合でも、外部への通信をブロックできない点に注意が必要です。 Content Security Policy Level 3 https://w3c.github.io/webappsec-csp/" , # noqa: E501
469+ current_status = "NEEDS_MORE_RATINGS" ,
470+ created_at = 1728877704750 ,
471+ post = {
472+ "postId" : "1846718284369912064" ,
473+ "xUserId" : "90954365" ,
474+ "xUser" : {
475+ "userId" : "90954365" ,
476+ "name" : "earthquakejapan" ,
477+ "profileImage" : "https://pbs.twimg.com/profile_images/1638600342/japan_rel96_normal.jpg" ,
478+ "followersCount" : 162934 ,
479+ "followingCount" : 6 ,
480+ },
481+ "text" : "今後48時間以内に日本ではマグニチュード6.0の地震が発生する可能性があります。地図をご覧ください。" ,
482+ "mediaDetails" : [
483+ {
484+ "mediaKey" : "3_1846718279236177920-1846718284369912064" ,
485+ "type" : "photo" ,
486+ "url" : "https://pbs.twimg.com/media/GaDcfZoX0AAko2-.jpg" ,
487+ "width" : 900 ,
488+ "height" : 738 ,
489+ }
490+ ],
491+ "createdAt" : 1729094524000 ,
492+ "likeCount" : 451 ,
493+ "repostCount" : 104 ,
494+ "impressionCount" : 82378 ,
495+ "links" : [
496+ {
497+ "linkId" : "9c139b99-8111-e4f0-ad41-fc9e40d08722" ,
498+ "url" : "https://www.quakeprediction.com/Earthquake%20Forecast%20Japan.html" ,
499+ }
500+ ],
501+ "link" : "https://x.com/earthquakejapan/status/1846718284369912064" ,
502+ },
503+ )
504+ ],
505+ meta = PaginationMeta (next = None , prev = None ),
506+ )
507+
313508 return router
0 commit comments