o
    ge                     @   s   d Z ddlZddlZddlmZ ddlmZ ddlm	Z	 ddl
mZmZmZmZmZmZmZ ddlZddlmZ dd	lmZmZmZmZmZmZ G d
d dZG dd dZdS )z2 Classes for interacting with Salesforce Bulk API     N)OrderedDict)partial)sleep)AnyDictIterableListOptionalUnioncast   )SalesforceGeneralError)BulkDataAnyBulkDataStrHeadersProxiescall_salesforcelist_from_generatorc                
   @   sL   e Zd ZdZ		ddededee deej fddZ	d	ed
dfddZ
dS )SFBulkHandlerz Bulk API request handler
    Intermediate class which allows us to use commands,
     such as 'sf.bulk.Contacts.create(...)'
    This is really just a middle layer, whose sole purpose is
    to allow the above syntax
    N
session_idbulk_urlproxiessessionc                 C   sB   || _ |pt | _|| _|s|dur|| j_d| j dd| _dS )a  Initialize the instance with the given parameters.

        Arguments:

        * session_id -- the session ID for authenticating to Salesforce
        * bulk_url -- API endpoint set in Salesforce instance
        * proxies -- the optional map of scheme to proxy server
        * session -- Custom requests session, created in calling code. This
                     enables the use of requests Session features not otherwise
                     exposed by simple_salesforce.
        Nzapplication/json1)zContent-TypezX-SFDC-SessionzX-PrettyPrint)r   requestsSessionr   r   r   headers)selfr   r   r   r    r   O/home/ubuntu/webapp/venv/lib/python3.10/site-packages/simple_salesforce/bulk.py__init__   s   zSFBulkHandler.__init__namereturn
SFBulkTypec                 C   s   t || j| j| jdS )N)object_namer   r   r   )r#   r   r   r   )r   r!   r   r   r   __getattr__;   s
   zSFBulkHandler.__getattr__)NN)__name__
__module____qualname____doc__strr	   r   r   r   r    r%   r   r   r   r   r      s$    
!r   c                   @   s  e Zd ZdZdedededejfddZ	d<d	ed
e	de
e defddZdedefddZdedefddZdeded	edefddZdededefddZdeded	edee fddZdededee fddZ			d=d eeef d	ed!ed"e	d#e	dee fd$d%Zded	ed&edee fd'd(Z			)			d>d	eded
e	de
e d*eeef d!ed"e	d#e	deee  fd+d,Z	)			d?ded*ed
e	d"e	d#e	dee fd-d.Z	)			d?ded*ed
e	d"e	d#e	dee fd/d0Z	)			d?deded*ed
e	d"e	d#e	dee fd1d2Z	)			d?ded*ed
e	d"e	d#e	dee fd3d4Z 	)			d?ded*ed
e	d"e	d#e	dee fd5d6Z!		d@ded7e	d!edee fd8d9Z"		d@ded7e	d!edee fd:d;Z#dS )Ar#   z& Interface to Bulk/Async API functionsr$   r   r   r   c                 C   s   || _ || _|| _|| _dS )a  Initialize the instance with the given parameters.

        Arguments:

        * object_name -- the name of the type of SObject this represents,
                         e.g. `Lead` or `Contact`
        * bulk_url -- API endpoint set in Salesforce instance
        * headers -- bulk API headers
        * session -- Custom requests session, created in calling code. This
                     enables the use of requests Session features not otherwise
                     exposed by simple_salesforce.
        N)r$   r   r   r   )r   r$   r   r   r   r   r   r   r    H   s   
zSFBulkType.__init__N	operation
use_serialexternal_id_fieldr"   c              	   C   s`   || j |rdnddd}|dkr||d< | j d}t|d| j| jtj|d	d
d}|jtdS )z Create a bulk job

        Arguments:

        * operation -- Bulk operation to be performed by job
        * use_serial -- Process batches in order
        * external_id_field -- unique identifier field for upsert operations
        r   r   JSON)r+   objectconcurrencyModecontentTypeupsertexternalIdFieldNamejobPOSTF	allow_nanurlmethodr   r   dataobject_pairs_hook)r$   r   r   r   r   jsondumpsr   )r   r+   r,   r-   payloadr9   resultr   r   r   _create_job`   s"   
zSFBulkType._create_jobjob_idc              	   C   sD   ddi}| j  d| }t|d| j| jtj|ddd}|jtdS )	z Close a bulk job stateClosedjob/r5   Fr6   r8   r<   )r   r   r   r   r>   r?   r   )r   rC   r@   r9   rA   r   r   r   
_close_job   s   zSFBulkType._close_jobc                 C   s0   | j  d| }t|d| j| jd}|jtdS )z) Get an existing job to check the status rF   GETr9   r:   r   r   r<   r   r   r   r   r>   r   )r   rC   r9   rA   r   r   r   _get_job   s   zSFBulkType._get_jobr;   c                 C   sP   | j  d| d}|dvrtj|dd}n|}t|d| j| j|d}|jtdS )	z Add a set of data as a batch to an existing job
        Separating this out in case of later
        implementations involving multiple batches
        rF   z/batchqueryqueryAllFr6   r5   r8   r<   )r   r>   r?   r   r   r   r   )r   rC   r;   r+   r9   data_rA   r   r   r   
_add_batch   s   zSFBulkType._add_batchbatch_idc                 C   s6   | j  d| d| }t|d| j| jd}|jtdS )z+ Get an existing batch to check the status rF   /batch/rH   rI   r<   rJ   )r   rC   rQ   r9   rA   r   r   r   
_get_batch   s   zSFBulkType._get_batchc           	      c   s    | j  d| d| d}t|d| j| jd}|dv r:| D ]}| d| }t|d| j| jd }|V  qdS | V  dS )	0 retrieve a set of results from a completed job rF   rR   z/resultrH   rI   rL   /N)r   r   r   r   r>   )	r   rC   rQ   r+   r9   rA   batch_resulturl_query_resultsbatch_query_resultr   r   r   _get_batch_results   s(   	zSFBulkType._get_batch_resultsc                 c   s    | j  d| d| d}t|d| j| jd}| j||dd}g }t|D ] \}}dd	 | |  D }	|	D ]}
||
 q8|	| q%|V  d
S )rT   rF   rR   z/requestrH   rI   batch_results)r+   c                 S   sP   g | ]$\}}t |tr"|d  t| d  |t| d in||iqS ).r   )
isinstancedictlistkeysget).0kvr   r   r   
<listcomp>  s    	zDSFBulkType._get_batch_request_with_batch_results.<locals>.<listcomp>N)
r   r   r   r   rY   	enumerater>   itemsupdateappend)r   rC   rQ   r9   batch_requestrV   resultsidxiflattened_request_dictrequest_fieldr   r   r   %_get_batch_request_with_batch_results   s(   

z0SFBulkType._get_batch_request_with_batch_results   Fbatchwaitbypass_resultsinclude_detailed_resultsc                 C   s   |sE| j |d |d dd }|dvr(t| | j |d |d dd }|dvs|r7| j|d |d d}|S | j|d |d |d}|S ||d dg}|S )z Gets batches from concurrent worker threads.
        self._bulk_operation passes batch jobs.
        The worker function checks each batch job waiting for it complete
        and appends the results.
        jobIdidrC   rQ   rD   	CompletedFailedNotProcessedrC   rQ   r+   )rs   rC   )rS   r   ro   rY   )r   rq   r+   rr   rs   rt   batch_statusrA   r   r   r   worker  s:   


	zSFBulkType.workerr4   c                    s   d}d}g }d}d\}}	t |D ]2\}
}ttj|tdd }t|	| |k||kgr:||||
  |
}d\}}	|	|7 }	|d7 }q|t|d k rT|||d   fd	d
|D S )a  
        Auto-create batches that respect bulk api V1 limits.

        bulk v1 api has following limits
        number of records <= 10000
        AND
        file_size_limit <= 10MB
        AND
        number_of_character_limit <= 10000000

        Documentation on limits can be found at:
        https://developer.salesforce.com/docs/atlas.en-us
        .salesforce_app_limits_cheatsheet.meta
        /salesforce_app_limits_cheatsheet
        /salesforce_app_limits_platform_bulkapi.htm#ingest_jobs

        Our JSON serialization uses the default `ensure_ascii=True`, so the
        character and byte lengths will be the same. Therefore we only need
        to adhere to a single length limit of 10,000,000 characters.

        TODO: In future when simple-salesforce supports bulk api V2
        we should detect api version and set max file size accordingly. V2
        increases file size limit to 150MB

        TODO: support for the following limits have not been added since these
        are record / field level limits and not chunk level limits:
        * Maximum number of fields in a record: 5,000
        * Maximum number of characters in a record: 400,000
        * Maximum number of characters in a field: 131,072
        '  i r   )r   r   )default   r   Nc                    s   g | ]
}j  |d qS )rC   r;   r+   rP   ra   rl   r4   r+   r   r   r   rd   x  s    
z5SFBulkType._add_autosized_batches.<locals>.<listcomp>)re   lenr>   r?   r*   anyrh   )r   r;   r+   r4   record_limit
char_limitbatches
last_breakrecord_count
char_countrl   recordadditional_charsr   r   r   _add_autosized_batches:  s2   $

z!SFBulkType._add_autosized_batchesr   
batch_sizec	                    s  t  ts dkstddvrstd  dkr&t td tj h}	j||d dkrCj	d d}
n t
t  fd	d
 fdd
tt  d D D }
tj|||d}|	||
}|s|dd
 |D ndd
 |D }jd d W d   |S 1 sw   Y  |S dv rj||djd d}jd d j|d |d d}|d dvrt| j|d |d d}|d dvs|d dkrtd|d |d |d j|d |d d}|S )a   String together helper functions to create a complete
        end-to-end bulk API request
        Arguments:
        * operation -- Bulk operation to be performed by job
        * data -- list of dict to be passed as a batch
        * use_serial -- Process batches in serial mode
        * external_id_field -- unique identifier field for upsert operations
        * wait -- seconds to sleep between checking batch status
        * batch_size -- number of records to assign for each batch in the job
                        or `auto`
        autoz'batch size should be auto or an integerrL   zdata should not be empty for r   )r+   r,   r-   rv   )r4   r;   r+   c                    s$   g | ]}|rj  d  |dqS )rv   r   r   r   r   r   r   rd     s    
z.SFBulkType._bulk_operation.<locals>.<listcomp>c                    s$   g | ]}|  |d     qS )r   r   r   )r   r;   r   r   rd     s    r   )r+   rr   rs   rt   c                 S   s$   g | ]}|D ]	}|D ]}|q
qqS r   r   )ra   sublistrl   xr   r   r   rd     s    c                 S   s0   g | ]}|D ]}|  D ]\}}||iqqqS r   )rf   )ra   r   rl   rb   rc   r   r   r   rd     s    )rC   Nr   ru   rw   rD   rx   rz    stateMessager|   )r\   int
ValueErrorminr   
concurrentfuturesThreadPoolExecutorrB   r   r   ranger   r~   maprG   rP   rS   r   r   rY   )r   r+   r;   r,   r-   r   rr   rs   rt   poolr   multi_thread_workerlist_of_resultsrj   rq   r}   r   )r   r;   r4   r+   r   r   _bulk_operation~  s   
	
NN




zSFBulkType._bulk_operationc                 C      | j |d||||d}|S )z soft delete records

        Data is batched by 10,000 records by default. To pick a lower size
        pass smaller integer to `batch_size`. to let simple-salesforce pick
        the appropriate limit dynamically, enter `batch_size='auto'`
        deleter,   r+   r;   r   rs   rt   r   r   r;   r   r,   rs   rt   rj   r   r   r   r        zSFBulkType.deletec                 C   r   )z insert records

        Data is batched by 10,000 records by default. To pick a lower size
        pass smaller integer to `batch_size`. to let simple-salesforce pick
        the appropriate limit dynamically, enter `batch_size='auto'`
        insertr   r   r   r   r   r   r     r   zSFBulkType.insertc              	   C   s   | j |d|||||d}|S )a   upsert records based on a unique identifier

        Data is batched by 10,000 records by default. To pick a lower size
        pass smaller integer to `batch_size`. to let simple-salesforce pick
        the appropriate limit dynamically, enter `batch_size='auto'`
        r2   )r,   r+   r-   r;   r   rs   rt   r   )r   r;   r-   r   r,   rs   rt   rj   r   r   r   r2   (  s   	zSFBulkType.upsertc                 C   r   )z update records

        Data is batched by 10,000 records by default. To pick a lower size
        pass smaller integer to `batch_size`. to let simple-salesforce pick
        the appropriate limit dynamically, enter `batch_size='auto'`
        rg   r   r   r   r   r   r   rg   B  r   zSFBulkType.updatec                 C   r   )z hard delete records

        Data is batched by 10,000 records by default. To pick a lower size
        pass smaller integer to `batch_size`. to let simple-salesforce pick
        the appropriate limit dynamically, enter `batch_size='auto'`
        
hardDeleter   r   r   r   r   r   hard_deleteZ  r   zSFBulkType.hard_deletelazy_operationc                 C       | j d||d}|r|S t|S )z bulk query rM   r+   r;   rr   r   r   r   r;   r   rr   rj   r   r   r   rM   r  s   zSFBulkType.queryc                 C   r   )z bulk queryAll rN   r   r   r   r   r   r   	query_all  s   zSFBulkType.query_all)N)rp   FF)FNr   rp   FF)r   FFF)Frp   )$r&   r'   r(   r)   r*   r   r   r   r    boolr	   r   rB   rG   rK   r   rP   rS   r   rY   ro   r   r   r~   r   r   r
   r   r   r   r   r2   rg   r   rM   r   r   r   r   r   r#   E   s   

$





(

(
H
	


}





r#   )r)   concurrent.futuresr   r>   collectionsr   	functoolsr   timer   typingr   r   r   r   r	   r
   r   r   
exceptionsr   utilr   r   r   r   r   r   r   r#   r   r   r   r   <module>   s    $ 3