nadeshiko-sdk 0.1.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.
- nadeshiko/__init__.py +10 -0
- nadeshiko/_version.py +1 -0
- nadeshiko/api/__init__.py +1 -0
- nadeshiko/api/auth/__init__.py +3 -0
- nadeshiko/api/auth/internal/__init__.py +11 -0
- nadeshiko/api/auth/internal/get_discord_auth_url.py +176 -0
- nadeshiko/api/auth/internal/login.py +213 -0
- nadeshiko/api/auth/internal/login_discord.py +204 -0
- nadeshiko/api/auth/internal/login_google.py +204 -0
- nadeshiko/api/auth/internal/register.py +212 -0
- nadeshiko/api/auth_jwt/__init__.py +3 -0
- nadeshiko/api/auth_jwt/internal/__init__.py +7 -0
- nadeshiko/api/auth_jwt/internal/logout.py +151 -0
- nadeshiko/api/lists/__init__.py +8 -0
- nadeshiko/api/lists/internal/__init__.py +12 -0
- nadeshiko/api/lists/internal/list_add_item.py +215 -0
- nadeshiko/api/lists/internal/list_create.py +194 -0
- nadeshiko/api/lists/internal/list_destroy.py +193 -0
- nadeshiko/api/lists/internal/list_remove_item.py +207 -0
- nadeshiko/api/lists/internal/list_update.py +215 -0
- nadeshiko/api/lists/internal/list_update_item.py +229 -0
- nadeshiko/api/lists/list_index.py +257 -0
- nadeshiko/api/lists/list_show.py +193 -0
- nadeshiko/api/media/__init__.py +14 -0
- nadeshiko/api/media/character_show.py +193 -0
- nadeshiko/api/media/episode_index.py +229 -0
- nadeshiko/api/media/episode_show.py +207 -0
- nadeshiko/api/media/internal/__init__.py +16 -0
- nadeshiko/api/media/internal/episode_create.py +215 -0
- nadeshiko/api/media/internal/episode_destroy.py +205 -0
- nadeshiko/api/media/internal/episode_update.py +229 -0
- nadeshiko/api/media/internal/media_create.py +194 -0
- nadeshiko/api/media/internal/media_destroy.py +197 -0
- nadeshiko/api/media/internal/media_update.py +219 -0
- nadeshiko/api/media/internal/segment_create.py +229 -0
- nadeshiko/api/media/internal/segment_destroy.py +219 -0
- nadeshiko/api/media/internal/segment_index.py +243 -0
- nadeshiko/api/media/internal/segment_update.py +243 -0
- nadeshiko/api/media/media_index.py +232 -0
- nadeshiko/api/media/media_show.py +193 -0
- nadeshiko/api/media/segment_show.py +221 -0
- nadeshiko/api/media/segment_show_by_uuid.py +193 -0
- nadeshiko/api/media/seiyuu_show.py +193 -0
- nadeshiko/api/search/__init__.py +11 -0
- nadeshiko/api/search/fetch_media_info.py +278 -0
- nadeshiko/api/search/fetch_sentence_context.py +219 -0
- nadeshiko/api/search/internal/__init__.py +6 -0
- nadeshiko/api/search/search.py +295 -0
- nadeshiko/api/search/search_health_check.py +168 -0
- nadeshiko/api/search/search_multiple.py +238 -0
- nadeshiko/api/user/__init__.py +3 -0
- nadeshiko/api/user/internal/__init__.py +11 -0
- nadeshiko/api/user/internal/create_api_key.py +199 -0
- nadeshiko/api/user/internal/deactivate_api_key.py +194 -0
- nadeshiko/api/user/internal/get_api_keys.py +151 -0
- nadeshiko/api/user/internal/get_identity_me.py +156 -0
- nadeshiko/api/user/internal/get_user_info.py +156 -0
- nadeshiko/client.py +138 -0
- nadeshiko/errors.py +16 -0
- nadeshiko/models/__init__.py +215 -0
- nadeshiko/models/api_key.py +118 -0
- nadeshiko/models/api_key_permission.py +70 -0
- nadeshiko/models/auth_user.py +107 -0
- nadeshiko/models/basic_info.py +132 -0
- nadeshiko/models/category_statistic.py +71 -0
- nadeshiko/models/character.py +100 -0
- nadeshiko/models/character_input.py +130 -0
- nadeshiko/models/character_input_character_role.py +10 -0
- nadeshiko/models/character_with_media.py +124 -0
- nadeshiko/models/character_with_media_media_appearances_item.py +93 -0
- nadeshiko/models/character_with_media_media_appearances_item_role.py +10 -0
- nadeshiko/models/create_api_key_request.py +75 -0
- nadeshiko/models/create_api_key_response.py +70 -0
- nadeshiko/models/deactivate_api_key_request.py +62 -0
- nadeshiko/models/deactivate_api_key_response.py +62 -0
- nadeshiko/models/discord_auth_url_response.py +62 -0
- nadeshiko/models/discord_login_request.py +62 -0
- nadeshiko/models/episode.py +253 -0
- nadeshiko/models/episode_create_request.py +145 -0
- nadeshiko/models/episode_list_response.py +94 -0
- nadeshiko/models/episode_update_request.py +135 -0
- nadeshiko/models/error.py +128 -0
- nadeshiko/models/error_errors.py +46 -0
- nadeshiko/models/fetch_media_info_response.py +115 -0
- nadeshiko/models/fetch_media_info_type.py +10 -0
- nadeshiko/models/fetch_sentence_context_request.py +96 -0
- nadeshiko/models/fetch_sentence_context_response.py +75 -0
- nadeshiko/models/get_api_keys_response.py +86 -0
- nadeshiko/models/google_login_request.py +62 -0
- nadeshiko/models/list_.py +97 -0
- nadeshiko/models/list_add_item_body.py +69 -0
- nadeshiko/models/list_add_item_response_201.py +61 -0
- nadeshiko/models/list_create_request.py +107 -0
- nadeshiko/models/list_create_request_type.py +9 -0
- nadeshiko/models/list_create_request_visibility.py +9 -0
- nadeshiko/models/list_destroy_response_200.py +70 -0
- nadeshiko/models/list_index_type.py +9 -0
- nadeshiko/models/list_index_visibility.py +9 -0
- nadeshiko/models/list_input.py +138 -0
- nadeshiko/models/list_input_list_type.py +9 -0
- nadeshiko/models/list_input_list_visibility.py +9 -0
- nadeshiko/models/list_remove_item_response_200.py +61 -0
- nadeshiko/models/list_type.py +9 -0
- nadeshiko/models/list_update_body.py +78 -0
- nadeshiko/models/list_update_body_visibility.py +9 -0
- nadeshiko/models/list_update_item_body.py +61 -0
- nadeshiko/models/list_update_item_response_200.py +61 -0
- nadeshiko/models/list_visibility.py +9 -0
- nadeshiko/models/list_with_media.py +119 -0
- nadeshiko/models/list_with_media_media_item.py +83 -0
- nadeshiko/models/list_with_media_type.py +9 -0
- nadeshiko/models/list_with_media_visibility.py +9 -0
- nadeshiko/models/login_request.py +70 -0
- nadeshiko/models/login_response.py +84 -0
- nadeshiko/models/logout_response.py +73 -0
- nadeshiko/models/media.py +288 -0
- nadeshiko/models/media_category.py +11 -0
- nadeshiko/models/media_character.py +78 -0
- nadeshiko/models/media_character_role.py +10 -0
- nadeshiko/models/media_create_request.py +270 -0
- nadeshiko/models/media_create_request_category.py +11 -0
- nadeshiko/models/media_destroy_response_200.py +70 -0
- nadeshiko/models/media_index_category.py +9 -0
- nadeshiko/models/media_info_data.py +307 -0
- nadeshiko/models/media_info_path.py +83 -0
- nadeshiko/models/media_info_stats.py +89 -0
- nadeshiko/models/media_list_response.py +94 -0
- nadeshiko/models/media_update_request.py +297 -0
- nadeshiko/models/media_update_request_category.py +9 -0
- nadeshiko/models/quota_info.py +87 -0
- nadeshiko/models/quota_info_quota_limit_type_1.py +8 -0
- nadeshiko/models/register_request.py +78 -0
- nadeshiko/models/register_response.py +76 -0
- nadeshiko/models/register_response_user.py +46 -0
- nadeshiko/models/search_health_check_response.py +138 -0
- nadeshiko/models/search_multiple_request.py +72 -0
- nadeshiko/models/search_multiple_response.py +79 -0
- nadeshiko/models/search_request.py +253 -0
- nadeshiko/models/search_request_content_sort.py +13 -0
- nadeshiko/models/search_request_media_item.py +85 -0
- nadeshiko/models/search_request_media_item_seasons_item.py +69 -0
- nadeshiko/models/search_response.py +158 -0
- nadeshiko/models/segment.py +303 -0
- nadeshiko/models/segment_create_request.py +203 -0
- nadeshiko/models/segment_create_request_status.py +13 -0
- nadeshiko/models/segment_info.py +207 -0
- nadeshiko/models/segment_list_response.py +94 -0
- nadeshiko/models/segment_status.py +13 -0
- nadeshiko/models/segment_update_request.py +198 -0
- nadeshiko/models/segment_update_request_status.py +13 -0
- nadeshiko/models/seiyuu.py +86 -0
- nadeshiko/models/seiyuu_with_roles.py +108 -0
- nadeshiko/models/seiyuu_with_roles_roles_item.py +109 -0
- nadeshiko/models/seiyuu_with_roles_roles_item_role.py +10 -0
- nadeshiko/models/sentence.py +88 -0
- nadeshiko/models/statistic.py +132 -0
- nadeshiko/models/statistic_season_with_episode_hits.py +74 -0
- nadeshiko/models/statistic_season_with_episode_hits_additional_property.py +46 -0
- nadeshiko/models/user_info_response.py +79 -0
- nadeshiko/models/user_info_response_user.py +91 -0
- nadeshiko/models/user_role.py +71 -0
- nadeshiko/models/word_match.py +107 -0
- nadeshiko/models/word_match_media.py +98 -0
- nadeshiko/types.py +54 -0
- nadeshiko_sdk-0.1.0.dist-info/METADATA +147 -0
- nadeshiko_sdk-0.1.0.dist-info/RECORD +167 -0
- nadeshiko_sdk-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
|
5
|
+
|
|
6
|
+
from attrs import define as _attrs_define
|
|
7
|
+
from attrs import field as _attrs_field
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from ..models.user_role import UserRole
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
T = TypeVar("T", bound="UserInfoResponseUser")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@_attrs_define
|
|
17
|
+
class UserInfoResponseUser:
|
|
18
|
+
"""
|
|
19
|
+
Attributes:
|
|
20
|
+
username (str): User's display name
|
|
21
|
+
email (str): User's email address
|
|
22
|
+
roles (list[UserRole]): User's roles
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
username: str
|
|
26
|
+
email: str
|
|
27
|
+
roles: list[UserRole]
|
|
28
|
+
additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
|
|
29
|
+
|
|
30
|
+
def to_dict(self) -> dict[str, Any]:
|
|
31
|
+
username = self.username
|
|
32
|
+
|
|
33
|
+
email = self.email
|
|
34
|
+
|
|
35
|
+
roles = []
|
|
36
|
+
for roles_item_data in self.roles:
|
|
37
|
+
roles_item = roles_item_data.to_dict()
|
|
38
|
+
roles.append(roles_item)
|
|
39
|
+
|
|
40
|
+
field_dict: dict[str, Any] = {}
|
|
41
|
+
field_dict.update(self.additional_properties)
|
|
42
|
+
field_dict.update(
|
|
43
|
+
{
|
|
44
|
+
"username": username,
|
|
45
|
+
"email": email,
|
|
46
|
+
"roles": roles,
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
return field_dict
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
|
|
54
|
+
from ..models.user_role import UserRole
|
|
55
|
+
|
|
56
|
+
d = dict(src_dict)
|
|
57
|
+
username = d.pop("username")
|
|
58
|
+
|
|
59
|
+
email = d.pop("email")
|
|
60
|
+
|
|
61
|
+
roles = []
|
|
62
|
+
_roles = d.pop("roles")
|
|
63
|
+
for roles_item_data in _roles:
|
|
64
|
+
roles_item = UserRole.from_dict(roles_item_data)
|
|
65
|
+
|
|
66
|
+
roles.append(roles_item)
|
|
67
|
+
|
|
68
|
+
user_info_response_user = cls(
|
|
69
|
+
username=username,
|
|
70
|
+
email=email,
|
|
71
|
+
roles=roles,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
user_info_response_user.additional_properties = d
|
|
75
|
+
return user_info_response_user
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def additional_keys(self) -> list[str]:
|
|
79
|
+
return list(self.additional_properties.keys())
|
|
80
|
+
|
|
81
|
+
def __getitem__(self, key: str) -> Any:
|
|
82
|
+
return self.additional_properties[key]
|
|
83
|
+
|
|
84
|
+
def __setitem__(self, key: str, value: Any) -> None:
|
|
85
|
+
self.additional_properties[key] = value
|
|
86
|
+
|
|
87
|
+
def __delitem__(self, key: str) -> None:
|
|
88
|
+
del self.additional_properties[key]
|
|
89
|
+
|
|
90
|
+
def __contains__(self, key: str) -> bool:
|
|
91
|
+
return key in self.additional_properties
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from typing import Any, TypeVar
|
|
5
|
+
|
|
6
|
+
from attrs import define as _attrs_define
|
|
7
|
+
from attrs import field as _attrs_field
|
|
8
|
+
|
|
9
|
+
from ..types import UNSET, Unset
|
|
10
|
+
|
|
11
|
+
T = TypeVar("T", bound="UserRole")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@_attrs_define
|
|
15
|
+
class UserRole:
|
|
16
|
+
"""User role information
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
id_role (int | Unset): Role ID
|
|
20
|
+
name (str | Unset): Role name
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
id_role: int | Unset = UNSET
|
|
24
|
+
name: str | Unset = UNSET
|
|
25
|
+
additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
|
|
26
|
+
|
|
27
|
+
def to_dict(self) -> dict[str, Any]:
|
|
28
|
+
id_role = self.id_role
|
|
29
|
+
|
|
30
|
+
name = self.name
|
|
31
|
+
|
|
32
|
+
field_dict: dict[str, Any] = {}
|
|
33
|
+
field_dict.update(self.additional_properties)
|
|
34
|
+
field_dict.update({})
|
|
35
|
+
if id_role is not UNSET:
|
|
36
|
+
field_dict["id_role"] = id_role
|
|
37
|
+
if name is not UNSET:
|
|
38
|
+
field_dict["name"] = name
|
|
39
|
+
|
|
40
|
+
return field_dict
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
|
|
44
|
+
d = dict(src_dict)
|
|
45
|
+
id_role = d.pop("id_role", UNSET)
|
|
46
|
+
|
|
47
|
+
name = d.pop("name", UNSET)
|
|
48
|
+
|
|
49
|
+
user_role = cls(
|
|
50
|
+
id_role=id_role,
|
|
51
|
+
name=name,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
user_role.additional_properties = d
|
|
55
|
+
return user_role
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def additional_keys(self) -> list[str]:
|
|
59
|
+
return list(self.additional_properties.keys())
|
|
60
|
+
|
|
61
|
+
def __getitem__(self, key: str) -> Any:
|
|
62
|
+
return self.additional_properties[key]
|
|
63
|
+
|
|
64
|
+
def __setitem__(self, key: str, value: Any) -> None:
|
|
65
|
+
self.additional_properties[key] = value
|
|
66
|
+
|
|
67
|
+
def __delitem__(self, key: str) -> None:
|
|
68
|
+
del self.additional_properties[key]
|
|
69
|
+
|
|
70
|
+
def __contains__(self, key: str) -> bool:
|
|
71
|
+
return key in self.additional_properties
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
|
5
|
+
|
|
6
|
+
from attrs import define as _attrs_define
|
|
7
|
+
from attrs import field as _attrs_field
|
|
8
|
+
|
|
9
|
+
from ..types import UNSET, Unset
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from ..models.word_match_media import WordMatchMedia
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
T = TypeVar("T", bound="WordMatch")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@_attrs_define
|
|
19
|
+
class WordMatch:
|
|
20
|
+
"""Word matching result with occurrences across media
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
word (str | Unset): The word that was searched for Example: 彼女.
|
|
24
|
+
is_match (bool | Unset): Indicates whether the word was found in any segment Example: True.
|
|
25
|
+
total_matches (int | Unset): Total number of times this word appears across all media Example: 1523.
|
|
26
|
+
media (list[WordMatchMedia] | Unset): List of media containing this word
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
word: str | Unset = UNSET
|
|
30
|
+
is_match: bool | Unset = UNSET
|
|
31
|
+
total_matches: int | Unset = UNSET
|
|
32
|
+
media: list[WordMatchMedia] | Unset = UNSET
|
|
33
|
+
additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
|
|
34
|
+
|
|
35
|
+
def to_dict(self) -> dict[str, Any]:
|
|
36
|
+
word = self.word
|
|
37
|
+
|
|
38
|
+
is_match = self.is_match
|
|
39
|
+
|
|
40
|
+
total_matches = self.total_matches
|
|
41
|
+
|
|
42
|
+
media: list[dict[str, Any]] | Unset = UNSET
|
|
43
|
+
if not isinstance(self.media, Unset):
|
|
44
|
+
media = []
|
|
45
|
+
for media_item_data in self.media:
|
|
46
|
+
media_item = media_item_data.to_dict()
|
|
47
|
+
media.append(media_item)
|
|
48
|
+
|
|
49
|
+
field_dict: dict[str, Any] = {}
|
|
50
|
+
field_dict.update(self.additional_properties)
|
|
51
|
+
field_dict.update({})
|
|
52
|
+
if word is not UNSET:
|
|
53
|
+
field_dict["word"] = word
|
|
54
|
+
if is_match is not UNSET:
|
|
55
|
+
field_dict["is_match"] = is_match
|
|
56
|
+
if total_matches is not UNSET:
|
|
57
|
+
field_dict["total_matches"] = total_matches
|
|
58
|
+
if media is not UNSET:
|
|
59
|
+
field_dict["media"] = media
|
|
60
|
+
|
|
61
|
+
return field_dict
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
|
|
65
|
+
from ..models.word_match_media import WordMatchMedia
|
|
66
|
+
|
|
67
|
+
d = dict(src_dict)
|
|
68
|
+
word = d.pop("word", UNSET)
|
|
69
|
+
|
|
70
|
+
is_match = d.pop("is_match", UNSET)
|
|
71
|
+
|
|
72
|
+
total_matches = d.pop("total_matches", UNSET)
|
|
73
|
+
|
|
74
|
+
_media = d.pop("media", UNSET)
|
|
75
|
+
media: list[WordMatchMedia] | Unset = UNSET
|
|
76
|
+
if _media is not UNSET:
|
|
77
|
+
media = []
|
|
78
|
+
for media_item_data in _media:
|
|
79
|
+
media_item = WordMatchMedia.from_dict(media_item_data)
|
|
80
|
+
|
|
81
|
+
media.append(media_item)
|
|
82
|
+
|
|
83
|
+
word_match = cls(
|
|
84
|
+
word=word,
|
|
85
|
+
is_match=is_match,
|
|
86
|
+
total_matches=total_matches,
|
|
87
|
+
media=media,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
word_match.additional_properties = d
|
|
91
|
+
return word_match
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def additional_keys(self) -> list[str]:
|
|
95
|
+
return list(self.additional_properties.keys())
|
|
96
|
+
|
|
97
|
+
def __getitem__(self, key: str) -> Any:
|
|
98
|
+
return self.additional_properties[key]
|
|
99
|
+
|
|
100
|
+
def __setitem__(self, key: str, value: Any) -> None:
|
|
101
|
+
self.additional_properties[key] = value
|
|
102
|
+
|
|
103
|
+
def __delitem__(self, key: str) -> None:
|
|
104
|
+
del self.additional_properties[key]
|
|
105
|
+
|
|
106
|
+
def __contains__(self, key: str) -> bool:
|
|
107
|
+
return key in self.additional_properties
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from typing import Any, TypeVar
|
|
5
|
+
|
|
6
|
+
from attrs import define as _attrs_define
|
|
7
|
+
from attrs import field as _attrs_field
|
|
8
|
+
|
|
9
|
+
from ..types import UNSET, Unset
|
|
10
|
+
|
|
11
|
+
T = TypeVar("T", bound="WordMatchMedia")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@_attrs_define
|
|
15
|
+
class WordMatchMedia:
|
|
16
|
+
"""Media entry containing word matches
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
media_id (int | Unset): Unique identifier for the media Example: 110316.
|
|
20
|
+
english_name (str | Unset): English translation of the media name Example: Steins;Gate.
|
|
21
|
+
japanese_name (str | Unset): Original Japanese name of the media Example: シュタインズ・ゲート.
|
|
22
|
+
romaji_name (str | Unset): Romaji transliteration of the media name Example: Steins;Gate.
|
|
23
|
+
matches (int | Unset): Number of times the word appears in this media Example: 234.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
media_id: int | Unset = UNSET
|
|
27
|
+
english_name: str | Unset = UNSET
|
|
28
|
+
japanese_name: str | Unset = UNSET
|
|
29
|
+
romaji_name: str | Unset = UNSET
|
|
30
|
+
matches: int | Unset = UNSET
|
|
31
|
+
additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
|
|
32
|
+
|
|
33
|
+
def to_dict(self) -> dict[str, Any]:
|
|
34
|
+
media_id = self.media_id
|
|
35
|
+
|
|
36
|
+
english_name = self.english_name
|
|
37
|
+
|
|
38
|
+
japanese_name = self.japanese_name
|
|
39
|
+
|
|
40
|
+
romaji_name = self.romaji_name
|
|
41
|
+
|
|
42
|
+
matches = self.matches
|
|
43
|
+
|
|
44
|
+
field_dict: dict[str, Any] = {}
|
|
45
|
+
field_dict.update(self.additional_properties)
|
|
46
|
+
field_dict.update({})
|
|
47
|
+
if media_id is not UNSET:
|
|
48
|
+
field_dict["media_id"] = media_id
|
|
49
|
+
if english_name is not UNSET:
|
|
50
|
+
field_dict["english_name"] = english_name
|
|
51
|
+
if japanese_name is not UNSET:
|
|
52
|
+
field_dict["japanese_name"] = japanese_name
|
|
53
|
+
if romaji_name is not UNSET:
|
|
54
|
+
field_dict["romaji_name"] = romaji_name
|
|
55
|
+
if matches is not UNSET:
|
|
56
|
+
field_dict["matches"] = matches
|
|
57
|
+
|
|
58
|
+
return field_dict
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
|
|
62
|
+
d = dict(src_dict)
|
|
63
|
+
media_id = d.pop("media_id", UNSET)
|
|
64
|
+
|
|
65
|
+
english_name = d.pop("english_name", UNSET)
|
|
66
|
+
|
|
67
|
+
japanese_name = d.pop("japanese_name", UNSET)
|
|
68
|
+
|
|
69
|
+
romaji_name = d.pop("romaji_name", UNSET)
|
|
70
|
+
|
|
71
|
+
matches = d.pop("matches", UNSET)
|
|
72
|
+
|
|
73
|
+
word_match_media = cls(
|
|
74
|
+
media_id=media_id,
|
|
75
|
+
english_name=english_name,
|
|
76
|
+
japanese_name=japanese_name,
|
|
77
|
+
romaji_name=romaji_name,
|
|
78
|
+
matches=matches,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
word_match_media.additional_properties = d
|
|
82
|
+
return word_match_media
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def additional_keys(self) -> list[str]:
|
|
86
|
+
return list(self.additional_properties.keys())
|
|
87
|
+
|
|
88
|
+
def __getitem__(self, key: str) -> Any:
|
|
89
|
+
return self.additional_properties[key]
|
|
90
|
+
|
|
91
|
+
def __setitem__(self, key: str, value: Any) -> None:
|
|
92
|
+
self.additional_properties[key] = value
|
|
93
|
+
|
|
94
|
+
def __delitem__(self, key: str) -> None:
|
|
95
|
+
del self.additional_properties[key]
|
|
96
|
+
|
|
97
|
+
def __contains__(self, key: str) -> bool:
|
|
98
|
+
return key in self.additional_properties
|
nadeshiko/types.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Contains some shared types for properties"""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping, MutableMapping
|
|
4
|
+
from http import HTTPStatus
|
|
5
|
+
from typing import IO, BinaryIO, Generic, Literal, TypeVar
|
|
6
|
+
|
|
7
|
+
from attrs import define
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Unset:
|
|
11
|
+
def __bool__(self) -> Literal[False]:
|
|
12
|
+
return False
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
UNSET: Unset = Unset()
|
|
16
|
+
|
|
17
|
+
# The types that `httpx.Client(files=)` can accept, copied from that library.
|
|
18
|
+
FileContent = IO[bytes] | bytes | str
|
|
19
|
+
FileTypes = (
|
|
20
|
+
# (filename, file (or bytes), content_type)
|
|
21
|
+
tuple[str | None, FileContent, str | None]
|
|
22
|
+
# (filename, file (or bytes), content_type, headers)
|
|
23
|
+
| tuple[str | None, FileContent, str | None, Mapping[str, str]]
|
|
24
|
+
)
|
|
25
|
+
RequestFiles = list[tuple[str, FileTypes]]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@define
|
|
29
|
+
class File:
|
|
30
|
+
"""Contains information for file uploads"""
|
|
31
|
+
|
|
32
|
+
payload: BinaryIO
|
|
33
|
+
file_name: str | None = None
|
|
34
|
+
mime_type: str | None = None
|
|
35
|
+
|
|
36
|
+
def to_tuple(self) -> FileTypes:
|
|
37
|
+
"""Return a tuple representation that httpx will accept for multipart/form-data"""
|
|
38
|
+
return self.file_name, self.payload, self.mime_type
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
T = TypeVar("T")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@define
|
|
45
|
+
class Response(Generic[T]):
|
|
46
|
+
"""A response from an endpoint"""
|
|
47
|
+
|
|
48
|
+
status_code: HTTPStatus
|
|
49
|
+
content: bytes
|
|
50
|
+
headers: MutableMapping[str, str]
|
|
51
|
+
parsed: T | None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
__all__ = ["UNSET", "File", "FileTypes", "RequestFiles", "Response", "Unset"]
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nadeshiko-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for Nadeshiko API
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: attrs>=23.0
|
|
7
|
+
Requires-Dist: httpx>=0.27.0
|
|
8
|
+
Requires-Dist: python-dateutil>=2.8.0
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: openapi-python-client>=0.22.0; extra == 'dev'
|
|
11
|
+
Requires-Dist: pytest>=9.0.0; extra == 'dev'
|
|
12
|
+
Requires-Dist: pyyaml>=6.0; extra == 'dev'
|
|
13
|
+
Requires-Dist: ruff>=0.9.0; extra == 'dev'
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
> This repository is still in WIP and not ready for production use
|
|
17
|
+
|
|
18
|
+
# Nadeshiko Python SDK
|
|
19
|
+
|
|
20
|
+
Python SDK for the [Nadeshiko API](https://nadeshiko.co)
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from nadeshiko import Nadeshiko, Environment
|
|
26
|
+
from nadeshiko.api.search import search
|
|
27
|
+
|
|
28
|
+
# Configure your client
|
|
29
|
+
client = Nadeshiko(
|
|
30
|
+
api_key='your-api-key-here',
|
|
31
|
+
base_url=Environment.PRODUCTION, # or Environment.LOCAL, or custom URL
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# Use API methods (names match OpenAPI operationIds)
|
|
35
|
+
result = search.sync(
|
|
36
|
+
client=client,
|
|
37
|
+
body={
|
|
38
|
+
'query': '彼女',
|
|
39
|
+
'limit': 10,
|
|
40
|
+
},
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
if isinstance(result, Error):
|
|
44
|
+
print(result.code, result.detail)
|
|
45
|
+
else:
|
|
46
|
+
print(result.sentences)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## API Methods
|
|
50
|
+
|
|
51
|
+
All methods are organized under `nadeshiko.api.{module}` and match the OpenAPI operationIds exactly:
|
|
52
|
+
|
|
53
|
+
You can check the full specification from the [OpenAPI spec page](https://nadeshiko.co/api/v1/docs).
|
|
54
|
+
|
|
55
|
+
## Error Handling
|
|
56
|
+
|
|
57
|
+
All methods return a union type: `Response | Error | None`.
|
|
58
|
+
|
|
59
|
+
**Check for errors:**
|
|
60
|
+
```python
|
|
61
|
+
from nadeshiko import Nadeshiko, Environment
|
|
62
|
+
from nadeshiko.api.search import search
|
|
63
|
+
from nadeshiko.models import Error
|
|
64
|
+
|
|
65
|
+
client = Nadeshiko(api_key='your-api-key', base_url=Environment.PRODUCTION)
|
|
66
|
+
result = search.sync(client=client, body={'query': '彼女'})
|
|
67
|
+
|
|
68
|
+
if isinstance(result, Error):
|
|
69
|
+
# Error type is fully generated from OpenAPI spec
|
|
70
|
+
print(result.code) # e.g., 'RATE_LIMIT_EXCEEDED'
|
|
71
|
+
print(result.title) # e.g., 'Rate Limit Exceeded'
|
|
72
|
+
print(result.detail) # Detailed message
|
|
73
|
+
print(result.status) # HTTP status code
|
|
74
|
+
else:
|
|
75
|
+
print(result.sentences)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
In general, all SDK methods return typed errors generated from the OpenAPI spec:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
class Error:
|
|
82
|
+
code: str # e.g., 'RATE_LIMIT_EXCEEDED', 'AUTH_CREDENTIALS_INVALID'
|
|
83
|
+
title: str # Short summary
|
|
84
|
+
detail: str # Detailed explanation
|
|
85
|
+
status: int # HTTP status code
|
|
86
|
+
type_: str | Unset # URI to error documentation
|
|
87
|
+
instance: str | Unset # Trace ID
|
|
88
|
+
errors: dict | Unset # Validation errors
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Handle each error independently based on the error code returned by the API.
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from nadeshiko import Nadeshiko, Environment
|
|
95
|
+
from nadeshiko.api.search import search
|
|
96
|
+
from nadeshiko.models import Error
|
|
97
|
+
|
|
98
|
+
client = Nadeshiko(api_key='your-api-key')
|
|
99
|
+
result = search.sync(client=client, body={'query': '彼女'})
|
|
100
|
+
|
|
101
|
+
if isinstance(result, Error):
|
|
102
|
+
# All error fields are typed
|
|
103
|
+
match result.code:
|
|
104
|
+
case 'RATE_LIMIT_EXCEEDED':
|
|
105
|
+
print('Wait before retrying')
|
|
106
|
+
case 'AUTH_CREDENTIALS_INVALID':
|
|
107
|
+
print('Check your API key')
|
|
108
|
+
case 'VALIDATION_FAILED':
|
|
109
|
+
print('Field errors:', result.errors)
|
|
110
|
+
case _:
|
|
111
|
+
print(result.detail)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
You can check the full list of errors codes for each endpoint from the [OpenAPI spec page](https://nadeshiko.co/api/v1/docs).
|
|
115
|
+
|
|
116
|
+
## Type Support
|
|
117
|
+
|
|
118
|
+
All types are auto-generated from the OpenAPI spec.
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from nadeshiko.models import (
|
|
122
|
+
SearchRequest,
|
|
123
|
+
SearchResponse,
|
|
124
|
+
Sentence,
|
|
125
|
+
MediaInfoData,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
request: SearchRequest = {
|
|
129
|
+
'query': '彼女',
|
|
130
|
+
'limit': 10,
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
sentence: Sentence = {
|
|
134
|
+
'basic_info': { # ... },
|
|
135
|
+
'segment_info': { # ... },
|
|
136
|
+
'media_info': { # ... },
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Examples
|
|
141
|
+
|
|
142
|
+
See `examples/usage.py` for more usage examples.
|
|
143
|
+
|
|
144
|
+
## References
|
|
145
|
+
|
|
146
|
+
- [Nadeshiko Website](https://nadeshiko.co)
|
|
147
|
+
- [API Documentation](https://nadeshiko.co/settings/api)
|