o
    [h                     @   s(  d dl Z d dl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	 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 d d
lmZmZmZ d dlm  mZ d dlm  m Z! d dl"Z#d dl$Z#d dl%Z#d dl&m'Z' d dl(Z#d dl)Z#d dl*Z#d dl+Z#d dl#m,Z, d dl-m.Z. d dl"m/Z/m0Z0m1Z1m2Z2m3Z3m4Z4m5Z5 d dl6m7Z7m8Z8 d dl9m:Z: d dl;m<Z< d dl=m>Z>m?Z? d dl)m@Z@mAZAmBZBmCZC d dlDmEZEmFZFmGZG d dlHmIZI eJeKZLe1 ZMdZNG dd dZOG dd deOZPG dd dZQG dd deOeQZRG dd  d eSZTG d!d" d"ZUdS )#    N)escape)Path)List)Optional)run_in_threadpool)FormData)Request)RedirectResponseHTMLResponse)ReceiveScopeSend)
json_dumps)settings)bot_prettify_post_data)get_app_label_from_import_pathget_dotted_nameget_admin_secret_code
DebugTableBotErrorNON_FIELD_ERROR_KEYget_constants)dbdbq)get_form)core_gettext)get_min_idx_for_appget_page_lookup)ParticipantSession	BaseGroupBaseSubsession)CompletedSubsessionWaitPageCompletedGroupWaitPageCompletedGBATWaitPage)renderzk
<html>
    <head>
        <title>Bot completed</title>
    </head>
    <body>Bot completed</body>
</html>
c                   @   sT  e Zd ZU eed< edd Zdedede	ddfd	d
Z
dejfddZdd ZdBddZdZejZdd Zdd Zedd Zedd Zedd Zdd Zdd Zdd  ZdZd!d" Zd#d$ Zd%d& Zd'd( Z e!de"fd)d*Z#e!de$fd+d,Z%e!de&fd-d.Z'd/d0 Z(dCd3d4Z)d5d6 Z*d7d8 Z+d9d: Z,d;d< Z-d=d> Z.d?d@ Z/dAZ0dS )DFormPageOrInGameWaitPagerequestc                 C   s   | ddid d S Ntypehttp clsr+   r+   {/home/ubuntu/experiments/live_experiments/Pythonexperiments/Otree/venv/lib/python3.10/site-packages/otree/views/abstract.pyinstantiate_without_requestF   s   z4FormPageOrInGameWaitPage.instantiate_without_requestscopereceivesendreturnNc                 C   s&   |d dksJ || _ || _|| _d S r(   )r0   r1   r2   )selfr0   r1   r2   r+   r+   r.   __init__J   s   
z!FormPageOrInGameWaitPage.__init__c                 C   s   |    S N)dispatch	__await__r4   r+   r+   r.   r8   P      z"FormPageOrInGameWaitPage.__await__c                 O   s<   | j rtt| || jg|R i |S t| ||i |S )z
        the default user-defined methods should not reference self, so they can work
        both as Player methods and Page methods.
        )	is_noselfgetattrr)   player)r4   method_nameargskwargsr+   r+   r.   call_user_definedS   s   "z*FormPageOrInGameWaitPage.call_user_definedc                    s   t | j| jd | _}|jd }d}tjt||d}| }|j	j
|ks,t|dd}n/| | z|jdkr@| j I d H | _W n tjjyQ   tj }Y n
w t| j|I d H }|| j| j| jI d H  d S )N)r1   participant_codezGThis user does not exist in the database. Maybe the database was reset.)codemsg.  status_codePOST)r   r0   r1   r'   Zpath_paramsr   Z
get_or_404r   _url_i_should_be_onurlpathr	   set_attributesmethodform
_form_data	starletterequestsZClientDisconnect	responsesResponser   inner_dispatchr2   )r4   r'   rB   rD   participantZurl_should_be_onresponser+   r+   r.   r7   \   s&   


z!FormPageOrInGameWaitPage.dispatchc                 C      t  )zinner dispatch functionNotImplementedErrorr4   r'   r+   r+   r.   rT   }   s   z'FormPageOrInGameWaitPage.inner_dispatchc                 C   rW   r6   rX   r9   r+   r+   r.   get_template_name   s   z*FormPageOrInGameWaitPage.get_template_namec                 C   s   d|| j f }|S )Nz(/p/{participant_code}/%s/%s/{page_index}__name__)r-   name_in_urlpr+   r+   r.   url_pattern   s   z$FormPageOrInGameWaitPage.url_patternc                 C   s   d| d| d| j  d| S )z9need this because reverse() is too slow in create_sessionz/p//r\   )r-   rB   r^   
page_indexr+   r+   r.   get_url   s   z FormPageOrInGameWaitPage.get_urlc                 C   s   t | ddS )zusing dots seems not to work.-)r   replacer,   r+   r+   r.   url_name   s   z!FormPageOrInGameWaitPage.url_namec                 C   s   t | j ddS )NrE   rF   )r	   rU   rI   r9   r+   r+   r.   '_redirect_to_page_the_user_should_be_on   s   z@FormPageOrInGameWaitPage._redirect_to_page_the_user_should_be_onc                 K   s   |j | t| dd | j| j| j| j| jt| dd | jjt	t| dd d
 | j
}|||j< i }| d}|p6i }t|ts@td| d}zt||d< W n tya } ztd| d d }~ww | | | | tjru| || _|S )	Nobject
timer_textlive_method)
viewri   r=   group
subsessionsessionrU   rj   Zcurrent_page_nameZhas_live_methodvars_for_templatez'vars_for_template did not return a dictjs_varsz#js_vars contains an invalid value; )updater<   r=   rm   rn   ro   rU   	__class__r]   bool
_ConstantsrA   
isinstancedict	Exceptionr   	TypeErrorr   DEBUG_get_debug_tablesZdebug_tables)r4   contextZ	Constantsrp   Z	user_varsrq   excr+   r+   r.   get_context_data   s>   







z)FormPageOrInGameWaitPage.get_context_datac                 C   s   t |  || jdS )N)Ztemplate_type)r%   r[   _template_typer4   r|   r+   r+   r.   render_to_response   s   z+FormPageOrInGameWaitPage.render_to_responsec                 C      i S r6   r+   r9   r+   r+   r.   rp         z*FormPageOrInGameWaitPage.vars_for_templatec                 C   r   r6   r+   r9   r+   r+   r.   rq      r   z FormPageOrInGameWaitPage.js_varsc              	   C   s   g }|rdd |  D }t|}|td|d | j}| j}tdd|jfd|jfd|jfd	|	 fd
|j
p8dfd|jfgd}|| |S )Nc                 S   s    g | ]\}}|t t|fqS r+   )r   repr.0kvr+   r+   r.   
<listcomp>   s     z>FormPageOrInGameWaitPage._get_debug_tables.<locals>.<listcomp>rp   )titlerowsz
Basic infozID in groupGroupzRound numberr   zParticipant label zSession code)itemssortedappendr   r=   rU   Zid_in_groupgroup_idround_number_numeric_labellabel_session_code)r4   rp   Ztablesr   r   r=   rU   Zbasic_info_tabler+   r+   r.   r{      s&   

z*FormPageOrInGameWaitPage._get_debug_tablesc                 C   s
   |  dS )Nis_displayed)rA   r9   r+   r+   r.   _is_displayed      
z&FormPageOrInGameWaitPage._is_displayedc                 C   s   | j jS )z3can't cache self._group_pk because group can change)r=   rm   r9   r+   r+   r.   rm         zFormPageOrInGameWaitPage.groupc                 C   s   | j j| jdS )z!so that it doesn't rely on playerid)SubsessionClassobjects_get_subsession_pkr9   r+   r+   r.   rn      s   z#FormPageOrInGameWaitPage.subsessionc                 C   s   t j| jdS )Nr   )r   r   _session_pkr9   r+   r+   r.   ro         z FormPageOrInGameWaitPage.sessionc                 C   s   t |j|j}|| _|j}tj|}t|| _	t
|d| _t
|d| _t
|d| _| jj||jd| _|j| _|j| _|j| _|j| _|| _|j| _||_| jj|_tt |_|j|_d S )NPlayerr   Z
Subsession)rU   r   ) r   r   _index_in_pagesZ_lookupapp_nameotreecommonZget_models_moduler   ru   r<   PlayerClass
GroupClassr   r   r   r=   subsession_idr   
session_pkr   r   Z_participant_pkrU   _current_app_namers   r]   Z_current_page_nameinttime_last_request_timestampZ_round_number)r4   rU   lookupr   Zmodels_moduler+   r+   r.   rL      s*   

z'FormPageOrInGameWaitPage.set_attributesoriginal_viewWaitPagec                C   s4   |j | _ |j| _|j| _|j| _|j| _|j| _dS )zoput it here so it can be compared with set_attributes...
        but this is really just a method on wait pagesN)ru   r   r   r   r   r   )r4   r   r+   r+   r.   set_attributes_waitpage_clone  s   z6FormPageOrInGameWaitPage.set_attributes_waitpage_clonec                 C   s   | j }| j|jksJ |  }t|}t| jd |jd D ]M}||_||jd kr- d S |r6||kr6 d S t|j|j	 }|
| j  |sN| rN d S t|trk|jrWqt  | \}}|rk|rk||j qd S )N      )rU   r   %_get_next_page_index_if_skipping_appsrt   rangeZ_max_page_indexr   r   Z
page_classr/   rL   r   rv   r   group_by_arrival_timer   commit_tally_unvisited_run_aapa_and_notify_group_or_subsession)r4   rU   Zpage_index_to_skip_toZis_skipping_appsrb   pageis_lastsomeone_waitingr+   r+   r.   _increment_index_in_pages-  s<   
z2FormPageOrInGameWaitPage._increment_index_in_pagesc                 C      dS NTr+   r9   r+   r+   r.   r   d  r   z%FormPageOrInGameWaitPage.is_displayedc                 C   s   | j   d S r6   )rU   _update_monitor_tabler9   r+   r+   r.   r   g  s   z.FormPageOrInGameWaitPage._update_monitor_tablec                 C   s   |   sd S t| dsd S | jj}| jjd }||}||d d  }| d|}|rA||vr:d| d}t|t	| jj
|S d S )NZapp_after_this_pageapp_sequencer   "z"" is not in the upcoming_apps list)r   hasattrrU   r   ro   configindexrA   InvalidAppErrorr   r   )r4   Zcurrent_appr   Zcurrent_app_indexZupcoming_appsZapp_to_skip_torD   r+   r+   r.   r   j  s   

z>FormPageOrInGameWaitPage._get_next_page_index_if_skipping_appsc                 C   sD   t t }| j}|j}tjj| | j |j	|j
|dd ||_d S )Nr   rl   r   Zparticipant__id_in_sessionZparticipant__codesession_codeZis_wait_page)r   r   rU   r   r   common2make_page_completion_rowr=   get_folder_nameid_in_sessionrC   _last_page_timestamp)r4   nowrU   r   r+   r+   r.   _record_page_completion_time  s   
	z5FormPageOrInGameWaitPage._record_page_completion_timec                 C   s&   t j| jjt| j| j| jj| jdS )N)rB   Z	page_namerb   r   Zlive_method_name)	channel_utilsZ	live_pathrU   rC   r)   r]   r   r   rk   r9   r+   r+   r.   live_url  s   z!FormPageOrInGameWaitPage.live_urlr   )r3   N)r   r   )1r]   
__module____qualname__r   __annotations__classmethodr/   r   r   r   r5   typing	Generatorr8   rA   r7   template_namer   rz   Zis_debugrT   r[   r`   rc   rg   rh   r~   r   r   rp   rq   r{   r   propertyr    rm   r!   rn   r   ro   rL   r   r   r   r   r   r   r   rk   r+   r+   r+   r.   r&   C   sR   
 

	


*
!7	r&   c                   @   s   e Zd ZdZg Zd Zdd ZdefddZdd Z	d	d
 Z
dd Zdd Zdd Zd-dejjjfddZdd Zdd Zdd ZdZdd Zd.ddZdd  Zd!d" Zd#d$ Zd%d& Zd'Zd(d) Zd*d+ ZdZdZ e!d,Z"dS )/PageNc                 C   s   |j dkr	|  S |  S )NrH   )rM   postgetrZ   r+   r+   r.   rT     s   
zPage.inner_dispatchrV   c                 C   s   | j jrLtj| j j| jjj|j	dd tj
| j jd}|r?d}|d}| j|7  _tt|jd t| |jd< dS tj| j j| j jd dS dS )	a  we use this hack of appending to the HTML, rather than sending a context var,
        because we don't know if we need to submit the page until we try enqueueing the next post data.
        i guess that can't be done until we have the HTML of the page.
        zutf-8)rB   request_pathhtmlrB   aO  
                <script>
                    var form = document.querySelector('#form');
                    form.submit();
                    // browser-bot-auto-submit
                    form.on('submit', function (e) {
                        e.preventDefault();
                    });
                </script>
                utf8zContent-Length)r   rB   N)rU   is_browser_botbrowser_botsrL   rC   r'   rJ   rK   bodydecodeZenqueue_next_post_dataencodestrr   headerslenZsend_completion_messager   )r4   rV   Zhas_next_submissionZauto_submit_jsZextra_contentr+   r+   r.   browser_bot_stuff  s*   



zPage.browser_bot_stuffc                 C   sh   |   s|   |  S |   |  r|  }| j|d}nt }| j|d}| 	|}| 
| |S )N)instancerN   )r   r   rh   r   has_form
get_objectr   MockFormr~   r   r   )r4   objrN   r|   rV   r+   r+   r.   r     s   

zPage.getc                 C   s&   | j d ur| j S dt| j| jjS )Nz
{}/{}.html)r   formatr   r   rs   r]   r9   r+   r+   r.   r[     s
   
zPage.get_template_namec                 C   s   t |  S r6   )rt   _get_form_fieldsr9   r+   r+   r.   r     r:   zPage.has_formc                 C   s   t | dr
| dS | jS )NZget_form_fields)r   rA   form_fieldsr9   r+   r+   r.   r        

zPage._get_form_fieldsc                 C   s8   | j s	d}t|d| jd| j| j| j| j| ji| j  S )Nz'Page has form_fields but not form_modelr=   rm   )
form_modelrx   r=   rm   r   r   )r4   rD   r+   r+   r.   r     s   zPage.get_objectr3   c                 C   s   |   }t||| |d}|S )N)field_namesrl   formdata)r   r   )r4   r   r   fieldsrN   r+   r+   r.   r     s   zPage.get_formc                 C   sX   | j |d}dd |jD }|r|d | _|dd  | _| |}tjj|jtjj	< |S )Nr   c                 S   s   g | ]}|t kr|qS r+   )r   )r   fnamer+   r+   r.   r     s    z%Page.form_invalid.<locals>.<listcomp>r   r   )
r~   errorsZfirst_field_with_errorsZother_fields_with_errorsr   r   	constantsget_param_truth_valuer   !redisplay_with_errors_http_header)r4   rN   r|   Zfields_with_errorsrV   r+   r+   r.   form_invalid  s   

zPage.form_invalidc                 C   s2   |r| drd| jjt|}t|d S d S )N	must_failzwPage "{}": Bot tried to submit intentionally invalid data with SubmissionMustFail, but it passed validation anyway: {}.)r   r   rs   r]   r   r   )r4   is_bot	post_datarD   r+   r+   r.   _check_submission_must_fail   s   z Page._check_submission_must_failc                 C   s   |   }| j||d}|| _| jr| || d S | jj}| r.| || |	| d S |rr| j
j}|dsPdd |j D }d||t|}t||drrt|d}t|j }	||	ksrd|||	}t|| |}
| |
 |
S )N)r   r   r   c                 S   s    g | ]\}}d  |t|qS )z{}: {})r   r   r   r+   r+   r.   r   <  s    z)Page.post_handle_form.<locals>.<listcomp>z{Page "{}": Bot submission failed form validation: {} Check your bot code, then create a new session. Data submitted was: {}Zerror_fieldszZPage {}, SubmissionMustFail: Expected error_fields were {}, but actual error_fields are {})r   r   rN   timeout_happened_process_auto_submitted_formrU   _is_botvalidater  populate_objrs   r]   r   r   r   r   r   r   setgetlistkeysr   r   )r4   r  r   rN   r   ZPageNamer   rD   Zexpected_error_fieldsZactual_error_fieldsrV   r+   r+   r.   post_handle_form,  sD   




zPage.post_handle_formc                 C   sz  | j }|tjj}|tjjtk}t|o|p|  | _| j	j
r>tj| j	jd}t|}|dd | D  t|}|  rL| |}|rK|S nRt| dr| js| j	j}| di }|r|rs|dssd| jj|}	t|	| jt|dd}
| |
}tjj|jtjj < | !| |S |r|dr| "|| | j#rt| jd	ni }| jdi | | $  | %  | & S )Nr   c                 S   s   i | ]	\}}|t |qS r+   )r   r   r+   r+   r.   
<dictcomp>q  s    zPage.post.<locals>.<dictcomp>error_messager   zePage "{}": Bot submission failed form validation: {} Check your bot code, then create a new session. )r  r   )r  before_next_page)r  )'rO   r   r   r   r  Zadmin_secret_codeADMIN_SECRET_CODErt   _is_past_timeoutrU   r   r   Zpop_enqueued_post_datarC   rw   rr   r   StarletteFormDatar   r  r   r  rA   r   rs   r]   r   r~   r   r   r   r   r   r   r  r;   r   r   rh   )r4   r  Zauto_submittedZhas_secret_codeZ
submissiondrespr   r  rD   r|   rV   
extra_argsr+   r+   r.   r   Z  s\   


z	Page.postFc                 C      d S r6   r+   )r4   r  r+   r+   r.   r    r   zPage.before_next_pagec                 C   s   t j| jj| jdS )z[called from template. can't start with underscore because used
        in template
        )rB   rb   )r   Zauto_advance_pathrU   rC   r   r9   r+   r+   r.   
socket_url  s   
zPage.socket_urlc                 C   sD   | j pi }|  D ]}||vrt|  }t||j}|||< q	|S )z timeout_submission is deprecated)timeout_submissionr   r)   r   r<   Zauto_submit_default)r4   r  
field_nameZ
ModelClassvaluer+   r+   r.   _get_timeout_submission  s   
zPage._get_timeout_submissionc                 C   s   |   }|  |j}|jr'|s't| dr'zt| d|j}W n   d}Y |r,|}n#|jrHi }|jD ]}|| ||< q4|j  |	| ni }|	| |D ]
}t
||||  qQdS )z
        # an empty submitted form looks like this:
        # {'f_currency': None, 'f_bool': None, 'f_int': None, 'f_char': ''}
        r  TN)r  r  non_field_errorr   r   rt   rA   dataclearr  setattr)r4   rN   r   r  Zhas_non_field_errorZauto_submit_values_to_user  r+   r+   r.   r    s0   




z!Page._process_auto_submitted_formc                 C   s.   | j }|j|jko|jduo|jt  dk S )a  
        Need to check that we are actually past the expiration time.
        Otherwise, a participant could skip past a page before it's ready,
        by bypassing the execution of error_message(),
        which is skipped when there is a timeout.
        Nr   )rU   _timeout_page_indexr   _timeout_expiration_timer   )r4   ppr+   r+   r.   r    s   zPage._is_past_timeoutunsetc                 C   s   | j dkr
|  | _ | j S )Nr"  )_remaining_timeout_secondsremaining_timeout_seconds_innerr9   r+   r+   r.   remaining_timeout_seconds  r   zPage.remaining_timeout_secondsc                 C   s   t   }| j}|j|jkr|jd u rd S |j| S t| dr$| d}n| j}|j|_|d u r4d |_d S || |_tj	j
rP| jjsPtjj| jj| jj|d d |S )NZget_timeout_seconds   )rB   rb   delay)r   rU   r  r   r   r   rA   timeout_secondsr   r   USE_TIMEOUT_WORKERr   tasksZsubmit_expired_urlrC   )r4   current_timerU   r(  r+   r+   r.   r$    s,   



	z$Page.remaining_timeout_seconds_innerz Time left to complete this page:r6   )F)#r]   r   r   r   r   r   rT   r
   r   r   r[   r   r   r   r   ZformsZ	ModelFormr   r   r  r  rO   r   r  r  r  r  r  r#  r%  r$  r(  r  r   rj   r+   r+   r+   r.   r     s8    ',
@*(r   c                   @   sJ   e Zd ZU dZdZeed< dd Zdd Ze	dZ
dZd	d
 Zdd ZdS )GenericWaitPageMixinzused for in-game wait pages, as well as other wait-type pages oTree has
    (like waiting for session to be created, or waiting for players to be
    assigned to matches

    Nr'   c                 C   r   )z-built-in wait pages should not be overridableotree/WaitPage.htmlr+   r9   r+   r+   r.   r[   (  s   z&GenericWaitPageMixin.get_template_namec                 C   s8   d| j _|   t|  |  }tjj|j	tjj
< |S r   )rU   is_on_wait_pager   r%   r[   r~   r   r   r   r   Zwait_page_http_header)r4   rV   r+   r+   r.   _get_wait_page,  s   z#GenericWaitPageMixin._get_wait_pagezPlease waitc                 C   r   )z
        needs to be a method because it could say
        "waiting for the other player", "waiting for the other players"...
        r   r+   r9   r+   r+   r.   _get_default_body_text9  s   z+GenericWaitPageMixin._get_default_body_textc                 C   s*   | j }| j}|d u r|  }t| ||dS )N)rl   
title_text	body_text)r1  r2  r0  rw   )r4   r1  r2  r+   r+   r.   r~   @  s
   z%GenericWaitPageMixin.get_context_data)r]   r   r   __doc__r'   r   r   r[   r/  r   r1  r2  r0  r~   r+   r+   r+   r.   r,    s   
 
r,  c                       s   e Zd ZdZdZdZd Zdd Zdd Zdd Z	d	d
 Z
dd Zdd Zdd Zdd Zedd Zdd ZdZe fddZdee fddZdee fddZd d! Zd"d# Zd$d% Zd&d' Zd(d) Zd*d+ Z  Z S ),r   ze
    Wait pages during game play (i.e. checkpoints),
    where users wait for others to complete
    Fc                 C   s   t | }tj| fi |S r6   )r,  r~   r&   r   r+   r+   r.   r~   X  s   
zWaitPage.get_context_datac                 C   s    | j r| j S td rdS dS )aR  fallback to otree/WaitPage.html, which is guaranteed to exist.
        the reason for the 'if' statement, rather than returning a list,
        is that if the user explicitly defined template_name, and that template
        does not exist, then we should not fail silently.
        (for example, the user forgot to add it to git)
        z_templates/global/WaitPage.htmlzglobal/WaitPage.htmlr-  )r   r   existsr9   r+   r+   r.   r[   \  s
   zWaitPage.get_template_namec                 C   s   |   S r6   )r   rZ   r+   r+   r.   rT   j  r   zWaitPage.inner_dispatchc                 C   s4   | j dkr|  }|S | jr|  }|S |  }|S r   )wait_for_all_groupsinner_dispatch_subsessionr   inner_dispatch_gbatinner_dispatch_group)r4   r  r+   r+   r.   r   o  s   
zWaitPage.getc                 C   s   | j rd}t|d}n|}t|d}t| j}t|tr"|| n(tt|dkrCt| ddidd}|j	| d ||_
|  n|di | | j|d dS )	a{  
        group_or_subsession is passed explicitly, because in the case of GBAT it might
        not include the current player, so we can't just use self.player.group.

        new design is that if anybody is waiting on the wait page, we run AAPA.
        If nobody is shown the wait page, we don't need to notify or even create a
        CompletedGroupWaitPage record.
        N)rn   )rm   z(self)r)   r*   )r   r+   )r5  rw   r)   after_all_players_arriverv   r   rA   inspect	signaturer   _group_for_wp_clone_mark_completed_and_notify)r4   group_or_subsessionrm   Znoself_kwargsZaapaZwpr+   r+   r.   r   z  s   	



zWaitPage._run_aapa_and_notifyc                 C   sd   t j| j| jj| jdr|  S |  }|  \}}|r"|s"| 	 S |r.|s(|r.| 
| j |  S )N)rb   r   
session_id)r#   objects_existsr   r=   r   r   _response_when_readyr   r   r/  r   rm   r4   r   r   r   r+   r+   r.   r8    s   zWaitPage.inner_dispatch_groupc                 C   s^   t j| j| jdr|  S |  }|  \}}|r|s|  S |r+|s%|r+| | j	 |  S )N)rb   ro   )
r"   r@  r   ro   rA  r   r   r/  r   rn   rB  r+   r+   r.   r6    s   z"WaitPage.inner_dispatch_subsessionc                 C   s   t j| j| jj| jdr|  S |  s|  S | j}d|_	| j|_
d|_tt |_| j| j}|rA| | |jrA|  S |  S )N)rb   id_in_subsessionro   TF)r$   r@  r   rm   rC  ro   rA  r   rU   Z_gbat_is_connectedZ_gbat_page_indexZ_gbat_groupedr   r   r   rn   Z_gbat_try_to_make_new_groupr   r/  )r4   rU   Zgbat_new_groupr+   r+   r.   r7    s*   	
zWaitPage.inner_dispatch_gbatc                 C   s   | j r| jS | jS r6   )r5  rn   rm   r9   r+   r+   r.   r     s   zWaitPage._group_or_subsessionc                 C   s8   | j }| jr	|jn|j}t|t||jk	tS )a  
        group_or_subsession needs to be passed because it could be a newly created group with GBAT,
        and may not equal self.group because the current player may not be part of the group.
        (for example, when creating single-player groups with waiting_too_long).
        )
r   r5  r   r   r   joinr   filterr   Zwith_entities)r4   r>  r   Zfk_fieldr+   r+   r.   #_get_participants_for_this_waitpage  s   z,WaitPage._get_participants_for_this_waitpageNc                    s   | j pt jS r6   )r<  superrm   r9   rs   r+   r.   rm     r   zWaitPage.groupparticipantsc              	   C   s<   | j  }| jj}|D ]}tjj| ||j|j|dd qdS )z
        this is more accurate than page load,
        because the player may delay doing that,
        to make it look like they waited longer.
        r   r   N)	r=   r   rU   r   r   r   r   r   rC   )r4   rI  r   r   r!  r+   r+   r.   _mark_page_completions  s   
zWaitPage._mark_page_completionsrm   c                 C   sF  t | j| jd}| j}| jrtjdi | n"| jr*t	t
di |d|ji nt	tdi |d|ji | |p?| j}| t| |D ]	}tt |_qJtjjrk| jjsktjjdd |D d| jd | jrtjtjdi |dd	id
 d S | jrtjdi |}ntj di |d|ji}tj|dd	id
 d S )N)rb   r?  rC  r   c                 S      g | ]}|j qS r+   r   )r   r!  r+   r+   r.   r   7      z7WaitPage._mark_completed_and_notify.<locals>.<listcomp>
   )Zparticipant_pksr'  rb   statusreadyrm   r  r+   )!rw   r   r   r   r5  r"   Zobjects_creater   r   addr$   rC  r#   r   rF  rn   rJ  listr   r   r   r   r   r)  rU   r   r*  Zensure_pages_visitedr   sync_group_sendZgbat_group_nameZsubsession_wait_page_nameZgroup_wait_page_name)r4   rm   Zbase_kwargsr   rI  r!  Zchannels_group_namer+   r+   r.   r=    sT   

z#WaitPage._mark_completed_and_notifyc                 C   sd   | j }| j}| jj}| jrtj||| j || jjdS | j	r'tj
|||dS tj|||| jjdS )N)r   rb   r   participant_idZ	player_id)r   rb   rT  )r   rb   rT  r   )r   r   rU   r   r   r   Z	gbat_pathr=   r   r5  Zsubsession_wait_page_pathZgroup_wait_page_pathr   )r4   r   rb   rT  r+   r+   r.   r  O  s.   zWaitPage.socket_urlc           	         s      j} jj}g }g }|D ]}||g|j jk | qt|dkrUt|dkr/d}nddd |D }|D ]}||_q;t	j
t	|tdd |D |d	d
d t| }t fdd|D }||fS )N   r   r   z, c                 s   s    | ]}|  V  qd S r6   )r   r   r_   r+   r+   r.   	<genexpr>{  s    z,WaitPage._tally_unvisited.<locals>.<genexpr>c                 S   rK  r+   )r   rV  r+   r+   r.   r     rL  z-WaitPage._tally_unvisited.<locals>.<listcomp>Zupdate_notes)Zidsnoter)   rP  c                    s   g | ]}|j  j ko|jqS r+   )r   r.  rV  r9   r+   r.   r     s    )rF  r   rU   r   r   r   r   rD  _monitor_noter   rS  Zsession_monitor_group_namerw   rt   any)	r4   rI  r   visitedZ	unvisitedr_   rX  r   r   r+   r9   r.   r   i  s:   

zWaitPage._tally_unvisitedc                 C   r   r   r+   r9   r+   r+   r.   r     r   zWaitPage.is_displayedc                 C   s"   | j }d|_d|_|   |  S )z
        Before calling this function, the following must be satisfied:
        - The completion object exists
        OR
        - The player skips this page
        FN)rU   r.  rY  r   rh   )r4   rU   r+   r+   r.   rA    s
   zWaitPage._response_when_readyc                 C   r  r6   r+   r9   r+   r+   r.   r9    r   z!WaitPage.after_all_players_arrivec                 C   s4   | j j d }|dkrtdS |dkrtdS dS )Nr   z#Waiting for the other participants.z"Waiting for the other participant.r   )r   Z
player_setcountr   )r4   Znum_other_playersr+   r+   r.   r0    s   zWaitPage._get_default_body_text)!r]   r   r   r3  r5  r   r   r~   r[   rT   r   r   r8  r6  r7  r   r   rF  r<  rm   r   r   rJ  r   r    r=  r  r   r   rA  r9  r0  __classcell__r+   r+   rH  r.   r   M  s6     -
:+r   c                   @   s   e Zd ZdS )r   N)r]   r   r   r+   r+   r+   r.   r     s    r   c                   @   s.   e Zd Zdd ZdddZg Zedd ZdS )	r   c                 c   s    d S r6   r+   r9   r+   r+   r.   __iter__  s   zMockForm.__iter__Nc                 C   s
   || _ d S r6   )r  )r4   r  r+   r+   r.   r5     r   zMockForm.__init__c                 C   s
   t | jS r6   )rt   r  r9   r+   r+   r.   r     s   
zMockForm.errorsr6   )r]   r   r   r^  r5   r   r   r   r+   r+   r+   r.   r     s    
r   )Vr:  loggingr   r   r   r   pathlibr   r   r   Zstarlette.exceptionsrP   Zstarlette.concurrencyr   Zstarlette.datastructuresr   r  Zstarlette.requestsr   Zstarlette.responsesr	   r
   Zstarlette.typesr   r   r   Zotree.bots.browserZbotsZbrowserr   Zotree.channels.utilsZchannelsutilsr   Zotree.commonr   Zotree.common2Zotree.constantsZotree.currencyr   Zotree.formsZotree.modelsZotree.tasksZotree.views.cbvr   Zotree.bots.botr   r   r   r   r   r   r   r   Zotree.databaser   r   Zotree.forms.formsr   Z
otree.i18nr   Zotree.lookupr   r   r   r   r    r!   Zotree.models_concreter"   r#   r$   Zotree.templatingr%   	getLoggerr]   loggerr  ZBOT_COMPLETE_HTML_MESSAGEr&   r   r,  r   rx   r   r   r+   r+   r+   r.   <module>   sd    $	

  _   .  e