o
    [hfG                     @   s  d dl Z d dlmZmZmZ d dlZd dlZd dlZd dlZd dl	Z	d dl
mZmZ d dlmZ d dlZd dlmZ d dlmZ d dlmZmZ d dlmZ d d	lmZmZmZ d d
lmZmZ d dlm Z  e Z!e"dZ#h dZ$dZ%de% &dd' Z(de% &dd' Z)G dd dZ*G dd dZ+G dd de+Z,G dd de-Z.dd Z/G dd dZ0G dd  d Z1G d!d" d"e-Z2G d#d$ d$e-Z3d%d& Z4d'd( Z5G d)d* d*e6Z7G d+d, d,ee8Z9d-d. Z:d/d0 Z;dS )1    N)ListSetTuple)unquoteurlsplit)
HTMLParser)settings)Currency)ParticipantSession)common)get_dotted_nameget_admin_secret_codeget_models_module)dbsession_scopecall_live_method_compatz
otree.bots>   Zcsrfmiddlewaretokentimeout_happenederror_fieldsadmin_secret_code	must_failz
Checking the HTML may not find all form fields and buttons
(e.g. those added with JavaScript),
so you can disable this check by yielding a Submission
with check_html=False, e.g.:

yield Submission(pages.PageName, {{...}}, check_html=False)
z
Bot is trying to submit page {page_name},
but no button was found in the HTML of the page.
(searched for <input> with type='submit' or <button> with type != 'button').

 z
Bot is trying to submit page {page_name} with fields: "{fields}",
but these form fields were not found in the HTML of the page
(searched for tags {tags} with name= attribute matching the field name).
c                   @      e Zd ZdS )BOTS_CHECK_HTMLN__name__
__module____qualname__ r    r    u/home/ubuntu/experiments/live_experiments/Pythonexperiments/Otree/venv/lib/python3.10/site-packages/otree/bots/bot.pyr   H       r   c                   @   s    e Zd Z	deddddZdS )
SubmissionNF)
check_htmlr   c                C   s   |pi }|  }|tkrtj}|rd|tjj< t|tjj< t|dr+d	|}t
||D ]}t|| trAtt|| ||< q-|| _t|| _|| _|| _d S )NTZwait_for_all_groupszwYour bot yielded '{}', which is a wait page. You should delete this line, because bots handle wait pages automatically.)copyr   r   otree	constantsr   ADMIN_SECRET_CODEr   hasattrformatAssertionError
isinstancer	   strdecimalDecimal
page_classr   Zpage_class_dotted	post_datar$   )self	PageClassr1   r$   r   msgkeyr    r    r!   __init__M   s(   	


zSubmission.__init__N)r   r   r   r   r6   r    r    r    r!   r#   L   s    r#   c                       s,   e Zd ZdZ	dedd fddZ  ZS )SubmissionMustFailzVlets you intentionally submit with invalid
    input to ensure it's correctly rejectedN)r$   r   c                   s:   |pi }t  j|||d d| jd< |r|| jd< d S d S )N)r$   Tr   r   )superr6   r1   )r2   r3   r1   r$   r   	__class__r    r!   r6   {   s   
zSubmissionMustFail.__init__r7   )r   r   r   __doc__r   r6   __classcell__r    r    r:   r!   r8   w   s    r8   c                   @   r   )ExpectErrorNr   r    r    r    r!   r>      r"   r>   c            	   	   G   s   t | dkr| \}}d}nt | dkr| \}}}nd}t|tjtjtjtjtjtjdd dd d}||vrBd	| d
}t||| ||}|srd|d||d|dd}d|d| d|}|	||}t
|d S )N   ==   zexpect() takes 2 or 3 argumentsc                 S   s   | |v S r7   r    abr    r    r!   <lambda>       zexpect.<locals>.<lambda>c                 S   s   | |vS r7   r    rB   r    r    r!   rE      rF   )r@   z!=><z>=z<=innot in"z" not allowed in expect()z	Expected z, actual value is z was not foundz& was not expected but was found anyway)r@   rI   rJ   zAssertion failed: r   )len
ValueErroroperatoreqnegtltgelegetr>   )	argslhsrhsopr4   	operatorsresZerror_messagesZdefault_msgr    r    r!   expect   s:   r\   c                   @   s   e Zd ZddddZedd Zdd Zd	d
 Zdd Zdd Z	dd Z
dd ZdefddZdd Zedd Zejdd Zedd Zejdd Zdd Zdefd d!ZdS )"ParticipantBotN)executed_live_methodsc                C   s   t |trtj|d}n|}|j| _|j| _d | _d | _	d | _
d | _d | _d | _|d u r/t }|| _|D ]}| |_q4|| _|  | _d S N)code)r,   r-   r
   objects_getr`   participant_codeZ_session_codesession_code_clienturl	_response_htmlpathZsubmitssetr^   participant_botplayer_botsget_submitssubmits_generator)r2   Zparticipant_or_coderk   r^   participantrD   r    r    r!   r6      s$   
zParticipantBot.__init__c                 C   s   | j s|   | j S r7   )rd   load_clientr2   r    r    r!   client   s   zParticipantBot.clientc                 C   sP   zdd l }W n ty   td Y nw ddlm} ddlm} ||| _d S )Nr   zBYou need to install requests to run bots ("pip3 install requests"))
TestClient)app)	requestsModuleNotFoundErrorsysexitZstarlette.testclientrr   Z
otree.asgirs   rd   )r2   rt   rr   rs   r    r    r!   ro      s   zParticipantBot.load_clientc                 C   s"   t | j}| jj|dd| _d S )NTallow_redirects)r   Zparticipant_start_urlrb   rq   rU   response)r2   Z	start_urlr    r    r!   open_start_url   s   zParticipantBot.open_start_urlc                 C   s
   t | jS r7   )nextrm   rp   r    r    r!   get_next_submit   s   
zParticipantBot.get_next_submitc                 c   sj    | j D ].}| }|d u rq|D ] }t|tst|}| ||j | | | || |V  qqd S r7   )	rk   
play_roundr,   r#   BareYieldToSubmissionassert_correct_pageplayerassert_html_oklive_method_stuff)r2   
player_bot	generator
submissionr    r    r!   rl      s   


zParticipantBot.get_submitsc                    s   |j }|j  rI|jj|f}|| jvrKt|}t|dd }|rAdd |j	 D  fdd}|||j
|j||jd t  | j| d S d S d S )NZcall_live_methodc                 S   s   i | ]}|j |qS r    )id_in_group).0pr    r    r!   
<dictcomp>  s    z4ParticipantBot.live_method_stuff.<locals>.<dictcomp>c                    s   t  |  |S r7   r   )r   datalive_methodZplayersr    r!   method  s   
z0ParticipantBot.live_method_stuff.<locals>.method)r   caseround_numberr0   group)r0   r   r   Zgroup_idr^   inspect	getmodulegetattrr   Zget_playersr   r   r   commitadd)r2   r   r   r3   recordZbots_moduleZmethod_calls_fnr   r    r   r!   r     s*   

z ParticipantBot.live_method_stuffc                 C   s4   |    z	 |  }| | q ty   Y dS w )zconvenience method for testingTN)r{   r}   submitStopIteration)r2   r   r    r    r!   _play_individually  s   
z!ParticipantBot._play_individuallyr   c                 C   s   |j rBdd |jD }t|}|| j}|r2|j }ttj	|d
|d
dd |jD d|jsD|j }ttj	|dd S d S )Nc                 S   s   g | ]}|t vr|qS r    INTERNAL_FORM_FIELDS)r   fr    r    r!   
<listcomp>+  s    z1ParticipantBot.assert_html_ok.<locals>.<listcomp>z, c                 s   s    | ]}d  |V  qdS )z<{}>N)r*   )r   tagr    r    r!   	<genexpr>6  s    

z0ParticipantBot.assert_html_ok.<locals>.<genexpr>)	page_namefieldstags)r   )r$   r1   PageHtmlCheckerget_missing_fieldshtmlr0   Zurl_nameMissingHtmlFormFieldErrorHTML_MISSING_FIELD_WARNINGr*   join
field_tagssubmit_button_foundMissingHtmlButtonErrorHTML_MISSING_BUTTON_WARNING)r2   r   fields_to_checkcheckermissing_fieldsr   r    r    r!   r   )  s0   

	

zParticipantBot.assert_html_okc           
      C   s   |j j}d| d| jvrHddlm} t| jjdddd }|| j|}tt	|j
|j|d}t|j|j|j jd}d| d	| d
}	t|	d S )N/r   )get_page_lookup   )maxsplit)rs   r   pagezNDiscrepancy between bot code and app code. Bot is trying to submit this page:
z'
But the participant is actually here:
r   )r0   r   rh   Zotree.lookupr   intrsplitrc   dicttyper   r   app_namer+   )
r2   r   r   Z	ClassNamer   idxlookupexpectedactualr4   r    r    r!   r   A  s.   z"ParticipantBot.assert_correct_pagec                 C      | j S r7   )rf   rp   r    r    r!   rz   [     zParticipantBot.responsec                 C   s2   t |j| _t| jj| _|| _|jd| _d S )Nzutf-8)r   re   r   rh   rf   contentdecoder   )r2   rz   r    r    r!   rz   _  s   c                 C   r   r7   )rg   rp   r    r    r!   r   g  r   zParticipantBot.htmlc                 C   s   t t|| _d S r7   )
HtmlStringnormalize_html_whitespacerg   r2   r   r    r    r!   r   k  s   c                 C   s,   t | jsdS | jj| jdd| _t | jS )NFTrx   )is_wait_pagerz   rq   rU   re   rp   r    r    r!   on_wait_pageo  s   

zParticipantBot.on_wait_pagec                 C   sr   |j }t|}d| j }|r|d|7 }|dr|d7 }|dr'|d7 }t| | jj| j	|dd| _
d S )	NzSubmit z, {}r   z, SubmissionMustFailr   z, timeout_happenedTrx   )r1   bot_prettify_post_datarh   r*   rU   loggerinforq   postre   rz   )r2   r   r1   Zpretty_post_dataZ
log_stringr    r    r!   r   y  s   



zParticipantBot.submit)r   r   r   r6   propertyrq   ro   r{   r}   rl   r   r   r#   r   r   rz   setterr   r   r   r    r    r    r!   r]      s,    






r]   c                   @   s   e Zd Zg ZdededefddZdd Zedd	 Zed
d Z	edd Z
edd Zedd Zedd Zedd ZdS )	PlayerBotcase_number	player_pksubsession_pkc           	      C   sv   t |}|j| _|j| _|j| _|| _|| _|| _	|| _
|d kr"d}| j}t|dkr6||t|  | _d S d | _d S )Nr   r   )r   ZPlayerPlayerClassGroupZ
GroupClassZ
SubsessionSubsessionClass
_player_pk_subsession_pk_session_pk_participant_codecasesrL   r   )	r2   r   r   r   r   Z
session_pkrb   Zmodels_moduler   r    r    r!   r6     s   

zPlayerBot.__init__c                 C   s   d S r7   r    rp   r    r    r!   r~     s   zPlayerBot.play_roundc                 C      | j j| jdS N)id)r   ra   r   rp   r    r    r!   r        zPlayerBot.playerc                 C      | j jS )z3can't cache self._group_pk because group can change)r   r   rp   r    r    r!   r     s   zPlayerBot.groupc                 C   r   r   )r   ra   r   rp   r    r    r!   
subsession  r   zPlayerBot.subsessionc                 C   r   r7   )r   r   rp   r    r    r!   r        zPlayerBot.round_numberc                 C      t j| jdS r_   )r
   ra   r   rp   r    r    r!   rn        zPlayerBot.participantc                 C   r   r   )r   ra   r   rp   r    r    r!   session  r   zPlayerBot.sessionc                 C   r   r7   )rj   r   rp   r    r    r!   r     r   zPlayerBot.htmlN)r   r   r   r   r   r6   r~   r   r   r   r   r   rn   r   r   r    r    r    r!   r     s0    






r   c                   @   r   )r   Nr   r    r    r    r!   r     r"   r   c                   @   r   )r   Nr   r    r    r    r!   r     r"   r   c                 C   s>   i }t | ttfr| d }t| dkr| d }n| }t||S )Nr   r?   r   )r,   listtuplerL   r#   )Zyielded_valuer1   r3   r    r    r!   r     s   
r   c                 C   s&   |  dd dd} tdd| } | S )Nr   r   z\s+)replaceresub)r   r    r    r!   r     s   r   c                   @   s$   e Zd Zdd Zdd Zdd ZdS )r   c                 C   s&   d}|  |t| }d| |d  S )z
        Make output more readable by truncating everything before the
         {% content %} block. I also considered indenting the HTML,
         but minidom had a parse error, and BS4 modifies a lot of tags,
         didn't seem optimal.
        z<div class="_otree-content">z...N)indexrL   )r2   Zdiv_strir    r    r!   	truncated  s   zHtmlString.truncatedc                 C      |   S r7   r   rp   r    r    r!   __str__     zHtmlString.__str__c                 C   r   r7   r   rp   r    r    r!   __repr__  r   zHtmlString.__repr__N)r   r   r   r   r   r   r    r    r    r!   r     s    r   c                       s<   e Zd Z fddZdd Zdd Zdd Zd	d
 Z  ZS )r   c                    s(   t    t|| _h d| _d| _d S )N>   selectinputbuttonZtextareaF)r9   r6   ri   r   r   r   )r2   r   r:   r    r!   r6     s   



zPageHtmlChecker.__init__c                 C   s   |  | | jS r7   )feedr   r   r    r    r!   r     s   
z"PageHtmlChecker.get_missing_fieldsc                 C   s4   || j v r|D ]\}}|dkr| j| qd S d S )Nname)r   r   discardr2   r   attrsZ	attr_nameZ
attr_valuer    r    r!   check_if_field  s   
zPageHtmlChecker.check_if_fieldc                 C   sp   | j s2|dkr|D ]\}}|dkr|dkr d S q	d| _ |dkr4|D ]\}}|dkr1|dkr1d| _ q"d S d S d S )Nr   r   Tr   r   )r   r   r    r    r!   check_if_button  s   zPageHtmlChecker.check_if_buttonc                 C   s   |  || | || d S r7   )r   r   )r2   r   r   r    r    r!   handle_starttag  s   zPageHtmlChecker.handle_starttag)	r   r   r   r6   r   r   r   r   r=   r    r    r:   r!   r     s    r   c                 C   s   | j tjjtjjkS r7   )headersrU   r&   r'   Zwait_page_http_headerZget_param_truth_value)rz   r    r    r!   r     s   r   c                 C   s$   t | dr	|  } dd |  D S )Nr   c                 S   s   i | ]\}}|t vr||qS r    r   )r   kvr    r    r!   r   .  s    z*bot_prettify_post_data.<locals>.<dictcomp>)r)   r   items)r1   r    r    r!   r   %  s   
r   )<rv   typingr   r   r   r   r.   loggingrN   r   urllib.parser   r   html.parserr   Zotree.constantsr&   r   Zotree.currencyr	   Zotree.modelsr
   r   r   Zotree.commonr   r   r   Zotree.databaser   r   Z
otree.liver   r(   	getLoggerr   r   ZDISABLE_CHECK_HTML_INSTRUCTIONSr   stripr   r   r   r#   r8   r+   r>   r\   r]   r   r   r   r   r   r-   r   objectr   r   r   r    r    r    r!   <module>   sb    
+' PC"