diff --git a/crowdin_api/api_resources/ai/resource.py b/crowdin_api/api_resources/ai/resource.py index 38678c2..fb9d9b6 100644 --- a/crowdin_api/api_resources/ai/resource.py +++ b/crowdin_api/api_resources/ai/resource.py @@ -16,6 +16,7 @@ GenerateAiPromptCompletionRequest, GenerateAiReportRequest, EditAiSettingsPatch, + AiFileTranslationRequest, ) from crowdin_api.sorting import Sorting from crowdin_api.utils import convert_enum_collection_to_string_if_exists, convert_enum_to_string_if_exists @@ -684,6 +685,99 @@ def list_supported_ai_provider_models( params=params ) + def get_ai_file_translations_path( + self, user_id: int, job_identifier: Optional[str] = None + ): + if job_identifier is not None: + return f"users/{user_id}/ai/file-translations/{job_identifier}" + return f"users/{user_id}/ai/file-translations" + + def create_ai_file_translation( + self, + user_id: int, + request_data: AiFileTranslationRequest, + ): + """ + AI File Translations + + Link to documentation: + https://support.crowdin.com/developer/api/v2/#tag/AI/operation/api.users.ai.file-translations.post + """ + + return self.requester.request( + method="post", + path=self.get_ai_file_translations_path(user_id), + request_data=request_data, + ) + + def get_ai_file_translation_status( + self, + user_id: int, + job_identifier: str, + ): + """ + Get File Translations Status + + Link to documentation: + https://support.crowdin.com/developer/api/v2/#tag/AI/operation/api.users.ai.file-translations.get + """ + + return self.requester.request( + method="get", + path=self.get_ai_file_translations_path(user_id, job_identifier), + ) + + def cancel_ai_file_translation( + self, + user_id: int, + job_identifier: str, + ): + """ + Cancel File Translations + + Link to documentation: + https://support.crowdin.com/developer/api/v2/#tag/AI/operation/api.users.ai.file-translations.delete + """ + + return self.requester.request( + method="delete", + path=self.get_ai_file_translations_path(user_id, job_identifier), + ) + + def download_ai_file_translation( + self, + user_id: int, + job_identifier: str, + ): + """ + Download Translated File + + Link to documentation: + https://support.crowdin.com/developer/api/v2/#tag/AI/operation/api.users.ai.file-translations.download + """ + + return self.requester.request( + method="get", + path=self.get_ai_file_translations_path(user_id, job_identifier) + "/download", + ) + + def download_ai_file_translation_strings( + self, + user_id: int, + job_identifier: str, + ): + """ + Download Translated File Strings + + Link to documentation: + https://support.crowdin.com/developer/api/v2/#tag/AI/operation/api.users.ai.file-translations.download-strings + """ + + return self.requester.request( + method="get", + path=self.get_ai_file_translations_path(user_id, job_identifier) + "/translations", + ) + class EnterpriseAIResource(BaseResource): """ @@ -1315,3 +1409,91 @@ def list_supported_ai_provider_models( path="ai/providers/supported-models", params=params ) + + def get_ai_file_translations_path( + self, job_identifier: Optional[str] = None + ): + if job_identifier is not None: + return f"ai/file-translations/{job_identifier}" + return "ai/file-translations" + + def create_ai_file_translation( + self, + request_data: AiFileTranslationRequest, + ): + """ + AI File Translations + + Link to documentation: + https://support.crowdin.com/developer/enterprise/api/v2/#tag/AI/operation/api.ai.file-translations.post + """ + + return self.requester.request( + method="post", + path=self.get_ai_file_translations_path(), + request_data=request_data, + ) + + def get_ai_file_translation_status( + self, + job_identifier: str, + ): + """ + Get File Translations Status + + Link to documentation: + https://support.crowdin.com/developer/enterprise/api/v2/#tag/AI/operation/api.ai.file-translations.get + """ + + return self.requester.request( + method="get", + path=self.get_ai_file_translations_path(job_identifier), + ) + + def cancel_ai_file_translation( + self, + job_identifier: str, + ): + """ + Cancel File Translations + + Link to documentation: + https://support.crowdin.com/developer/enterprise/api/v2/#tag/AI/operation/api.ai.file-translations.delete + """ + + return self.requester.request( + method="delete", + path=self.get_ai_file_translations_path(job_identifier), + ) + + def download_ai_file_translation( + self, + job_identifier: str, + ): + """ + Download Translated File + + Link to documentation: + https://support.crowdin.com/developer/enterprise/api/v2/#tag/AI/operation/api.ai.file-translations.download + """ + + return self.requester.request( + method="get", + path=self.get_ai_file_translations_path(job_identifier) + "/download", + ) + + def download_ai_file_translation_strings( + self, + job_identifier: str, + ): + """ + Download File Strings + + Link to documentation: + https://support.crowdin.com/developer/enterprise/api/v2/#tag/AI/operation/api.ai.file-translations.download-strings + """ + + return self.requester.request( + method="get", + path=self.get_ai_file_translations_path(job_identifier) + "/translations", + ) diff --git a/crowdin_api/api_resources/ai/tests/test_ai_resources.py b/crowdin_api/api_resources/ai/tests/test_ai_resources.py index 4002abd..f5572ad 100644 --- a/crowdin_api/api_resources/ai/tests/test_ai_resources.py +++ b/crowdin_api/api_resources/ai/tests/test_ai_resources.py @@ -28,7 +28,8 @@ AiToolObject, AiToolFunction, GenerateAiReportRequest, - GeneralReportSchema + GeneralReportSchema, + AiFileTranslationRequest, ) from crowdin_api.api_resources.enums import PatchOperation from crowdin_api.requester import APIRequester @@ -1103,6 +1104,107 @@ def test_list_supported_ai_provider_models(self, m_request, in_params, request_p params=request_params ) + @pytest.mark.parametrize( + "in_params, path", + ( + ({"user_id": 1}, "users/1/ai/file-translations"), + ({"user_id": 1, "job_identifier": "job-id"}, "users/1/ai/file-translations/job-id"), + ), + ) + def test_get_ai_file_translations_path(self, in_params, path, base_absolut_url): + resource = self.get_resource(base_absolut_url) + assert resource.get_ai_file_translations_path(**in_params) == path + + @pytest.mark.parametrize( + "incoming_data, request_data", + ( + ( + AiFileTranslationRequest( + storageId=1, + targetLanguageId="uk", + ), + { + "storageId": 1, + "targetLanguageId": "uk", + }, + ), + ), + ) + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_create_ai_file_translation(self, m_request, incoming_data, request_data, base_absolut_url): + m_request.return_value = "response" + + user_id = 1 + + resource = self.get_resource(base_absolut_url) + assert resource.create_ai_file_translation(user_id, incoming_data) == "response" + + m_request.assert_called_once_with( + method="post", + path=f"users/{user_id}/ai/file-translations", + request_data=request_data, + ) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_get_ai_file_translation_status(self, m_request, base_absolut_url): + m_request.return_value = "response" + + user_id = 1 + job_identifier = "job-id" + + resource = self.get_resource(base_absolut_url) + assert resource.get_ai_file_translation_status(user_id, job_identifier) == "response" + + m_request.assert_called_once_with( + method="get", + path=f"users/{user_id}/ai/file-translations/{job_identifier}", + ) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_cancel_ai_file_translation(self, m_request, base_absolut_url): + m_request.return_value = "response" + + user_id = 1 + job_identifier = "job-id" + + resource = self.get_resource(base_absolut_url) + assert resource.cancel_ai_file_translation(user_id, job_identifier) == "response" + + m_request.assert_called_once_with( + method="delete", + path=f"users/{user_id}/ai/file-translations/{job_identifier}", + ) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_download_ai_file_translation(self, m_request, base_absolut_url): + m_request.return_value = "response" + + user_id = 1 + job_identifier = "job-id" + + resource = self.get_resource(base_absolut_url) + assert resource.download_ai_file_translation(user_id, job_identifier) == "response" + + m_request.assert_called_once_with( + method="get", + path=f"users/{user_id}/ai/file-translations/{job_identifier}/download", + ) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_download_ai_file_translation_strings(self, m_request, base_absolut_url): + m_request.return_value = "response" + + user_id = 1 + job_identifier = "job-id" + + resource = self.get_resource(base_absolut_url) + assert resource.download_ai_file_translation_strings(user_id, job_identifier) == "response" + + m_request.assert_called_once_with( + method="get", + path=f"users/{user_id}/ai/file-translations/{job_identifier}/translations", + ) + class TestEnterpriseAIResources: resource_class = EnterpriseAIResource @@ -2104,3 +2206,98 @@ def test_list_supported_ai_provider_models(self, m_request, in_params, request_p path="ai/providers/supported-models", params=request_params ) + + @pytest.mark.parametrize( + "in_params, path", + ( + ({}, "ai/file-translations"), + ({"job_identifier": "job-id"}, "ai/file-translations/job-id"), + ), + ) + def test_get_ai_file_translations_path(self, in_params, path, base_absolut_url): + resource = self.get_resource(base_absolut_url) + assert resource.get_ai_file_translations_path(**in_params) == path + + @pytest.mark.parametrize( + "incoming_data, request_data", + ( + ( + AiFileTranslationRequest( + storageId=1, + targetLanguageId="uk", + ), + { + "storageId": 1, + "targetLanguageId": "uk", + }, + ), + ), + ) + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_create_ai_file_translation(self, m_request, incoming_data, request_data, base_absolut_url): + m_request.return_value = "response" + + resource = self.get_resource(base_absolut_url) + assert resource.create_ai_file_translation(incoming_data) == "response" + + m_request.assert_called_once_with( + method="post", + path="ai/file-translations", + request_data=request_data, + ) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_get_ai_file_translation_status(self, m_request, base_absolut_url): + m_request.return_value = "response" + + job_identifier = "job-id" + + resource = self.get_resource(base_absolut_url) + assert resource.get_ai_file_translation_status(job_identifier) == "response" + + m_request.assert_called_once_with( + method="get", + path=f"ai/file-translations/{job_identifier}", + ) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_cancel_ai_file_translation(self, m_request, base_absolut_url): + m_request.return_value = "response" + + job_identifier = "job-id" + + resource = self.get_resource(base_absolut_url) + assert resource.cancel_ai_file_translation(job_identifier) == "response" + + m_request.assert_called_once_with( + method="delete", + path=f"ai/file-translations/{job_identifier}", + ) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_download_ai_file_translation(self, m_request, base_absolut_url): + m_request.return_value = "response" + + job_identifier = "job-id" + + resource = self.get_resource(base_absolut_url) + assert resource.download_ai_file_translation(job_identifier) == "response" + + m_request.assert_called_once_with( + method="get", + path=f"ai/file-translations/{job_identifier}/download", + ) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_download_ai_file_translation_strings(self, m_request, base_absolut_url): + m_request.return_value = "response" + + job_identifier = "job-id" + + resource = self.get_resource(base_absolut_url) + assert resource.download_ai_file_translation_strings(job_identifier) == "response" + + m_request.assert_called_once_with( + method="get", + path=f"ai/file-translations/{job_identifier}/translations", + ) diff --git a/crowdin_api/api_resources/ai/types.py b/crowdin_api/api_resources/ai/types.py index ce13ed7..11eda9e 100644 --- a/crowdin_api/api_resources/ai/types.py +++ b/crowdin_api/api_resources/ai/types.py @@ -287,3 +287,18 @@ class EditAiSettingsPatch(TypedDict): op: PatchOperation path: EditAiSettingsPatchPath value: Any + + +class AiFileTranslationRequest(TypedDict): + storageId: int + targetLanguageId: str + sourceLanguageId: Optional[str] + type: Optional[str] + parserVersion: Optional[int] + tmIds: Optional[Iterable[int]] + glossaryIds: Optional[Iterable[int]] + aiPromptId: Optional[int] + aiProviderId: Optional[int] + aiModelId: Optional[str] + instructions: Optional[Iterable[str]] + attachmentIds: Optional[Iterable[int]]