22from datetime import datetime
33from enum import Enum
44from typing import Optional , Union , List , Iterable , Callable
5+ from dataclasses import dataclass
56
67from gzip_stream import GZIPCompressedStream
78
89from ._client import LaraObject , LaraClient
910from ._credentials import Credentials
1011from ._errors import LaraApiError
11-
12+ from . _s3client import S3Client , S3UploadFields
1213
1314# Objects --------------------------------------------------------------------------------------------------------------
1415
@@ -35,6 +36,25 @@ def __init__(self, **kwargs):
3536 self .size : int = kwargs .get ('size' )
3637 self .progress : float = kwargs .get ('progress' )
3738
39+ @dataclass
40+ class DocumentOptions :
41+ adapt_to : Optional [List [str ]] = None
42+
43+ class Document (LaraObject ):
44+
45+ def __init__ (self , ** kwargs ):
46+ self .id : str = kwargs .get ('id' )
47+ self .status : DocumentStatus = DocumentStatus (kwargs .get ('status' ))
48+ self .source : Optional [str ] = kwargs .get ('source' )
49+ self .target : str = kwargs .get ('target' )
50+ self .filename : str = kwargs .get ('filename' )
51+ self .created_at : datetime = self ._parse_date (kwargs .get ('created_at' ))
52+ self .updated_at : datetime = self ._parse_date (kwargs .get ('updated_at' ))
53+ self .options : Optional [DocumentOptions ] = DocumentOptions (** kwargs .get ('options' )) if kwargs .get ('options' ) else None
54+ self .translated_chars : Optional [int ] = int (kwargs .get ('translated_chars' )) if kwargs .get ('translated_chars' ) else None
55+ self .total_chars : Optional [int ] = int (kwargs .get ('total_chars' )) if kwargs .get ('total_chars' ) else None
56+ self .error_reason : Optional [str ] = kwargs .get ('error_reason' )
57+
3858
3959class TextBlock (LaraObject ):
4060 def __init__ (self , ** kwargs ):
@@ -148,6 +168,72 @@ def wait_for_import(self, memory_import: MemoryImport, *,
148168 return memory_import
149169
150170
171+
172+ class DocumentStatus (Enum ):
173+ INITIALIZED = 'initialized' # just been created
174+ ANALYZING = 'analyzing' # being analyzed for language detection and chars count
175+ PAUSED = 'paused' # paused after analysis, needs user confirm
176+ READY = 'ready' # ready to be translated
177+ TRANSLATING = 'translating'
178+ TRANSLATED = 'translated'
179+ ERROR = 'error'
180+
181+ class Documents :
182+ def __init__ (self , client : LaraClient ):
183+ self ._client : LaraClient = client
184+ self ._s3client = S3Client ()
185+ self ._polling_interval : int = 2
186+
187+ def upload (self , file_path : str , filename : str , target : str , source : Optional [str ] = None , adapt_to : Optional [List [str ]] = None ) -> Document :
188+ with open (file_path , 'rb' ) as file_payload :
189+ response_data = self ._client .get ('/documents/upload-url' , {'filename' : filename })
190+
191+ url : str = response_data ['url' ]
192+ fields : S3UploadFields = S3UploadFields (** response_data ['fields' ])
193+
194+ self ._s3client .upload (url , fields , file_payload )
195+
196+ body = {
197+ 's3key' : fields ['key' ],
198+ 'target' : target ,
199+ }
200+ if source is not None :
201+ body ['source' ] = source
202+
203+ if adapt_to is not None :
204+ body ['adapt_to' ] = adapt_to
205+
206+ return Document (** self ._client .post ('/documents' , body ))
207+
208+ def status (self , id : str ) -> Document :
209+ return Document (** self ._client .get (f'/documents/{ id } ' ))
210+
211+ def download (self , id : str , output_format : Optional [str ] = None ) -> bytes :
212+ params = {}
213+ if output_format is not None :
214+ params ['output_format' ] = output_format
215+ url : str = self ._client .get (f'/documents/{ id } /download-url' , params )['url' ]
216+ return self ._s3client .download (url = url )
217+
218+ def translate (self , file_path : str , filename : str , target : str , source : Optional [str ] = None ,
219+ adapt_to : Optional [List [str ]] = None , output_format : Optional [str ] = None ) -> Document :
220+
221+ document = self .upload (file_path = file_path , filename = filename , target = target , source = source , adapt_to = adapt_to )
222+
223+ max_wait_time = 60 * 15 # 15 minutes
224+ start = time .time ()
225+
226+ while time .time () - start < max_wait_time :
227+ document = self .status (id = document .id )
228+
229+ if DocumentStatus (document .status ) == DocumentStatus .TRANSLATED :
230+ return self .download (id = document .id , output_format = output_format )
231+ elif DocumentStatus (document .status ) == DocumentStatus .ERROR :
232+ raise LaraApiError (500 , "DocumentError" , document .error_reason )
233+
234+ time .sleep (self ._polling_interval )
235+ raise TimeoutError ()
236+
151237class TranslatePriority (Enum ):
152238 NORMAL = 'normal'
153239 BACKGROUND = 'background'
@@ -170,6 +256,7 @@ def __init__(self, credentials: Credentials = None, *,
170256
171257 self ._client : LaraClient = LaraClient (credentials .access_key_id , credentials .access_key_secret , server_url )
172258 self .memories : Memories = Memories (self ._client )
259+ self .documents : Documents = Documents (self ._client )
173260
174261 def languages (self ) -> List [str ]:
175262 return self ._client .get ('/languages' )
0 commit comments