+
    i                       R t ^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RI	H	t	 ^ RI
Ht ^ RIHtHtHt ^ RIHt ^ RIHt ^ RIHt ^ R	IHt ^ R
IHt ^ RIHt ^ RIHt ^ RI H!t! ^ RI"H#t$ Rt%Rt&]PN                  PQ                  RR4      PS                  4       ;'       g    Rt*Rt+Rt,Rrt-0 Rsmt.Rt/Rt0Rtt1^xt2^t3^t4^<t5^t6R R lt7R R lt8R R lt9R R lt:R R  lt;R! R" lt<R# R$ lt=R% R& lt>R' R( lt?]23R) R* llt@]33R+ R, lltAR- R. ltBR/ R0 ltC]43R1 R2 lltDR3 R4 ltER5 R6 ltFR7 R8 ltGRuR9 R: lltHR; R< ltIR= R> ltJRv]43R? R@ lltKRA RB ltLRC RD ltMRE RF ltNRwRG RH lltORI RJ ltPRK RL ltQRM RN ltRRO RP ltS]53RQ RR lltTRS RT ltURU RV ltVRW RX ltWRY RZ ltXRxR[ R\ lltYR] R^ ltZRyR_ R` llt[RzRa Rb llt\Rc Rd lt]Re Rf lt^Rg Rh lt_]63Ri Rj llt`R{Rk Rl lltaR|Rm Rn lltbRo Rp ltc]dRq8X  d   ]e! ]c! 4       4      hR# )}a   
Pull_On_Hand_Non-Footwear.py (v2)

Attaches to an already-open Firefox session created by Open_PowerBi_patched.py (via powerbi_session.json),
loads the Inventory Detail paginated report, switches into the paginated-reports iframe, sets:

  Store Type = Dealership
  As Of      = Current Date
  Category   = exact category pass for Apparel & Gear, then exact category pass for Footwear Accessories
  Store No   = 614-WALDORF, MD
  Bucket     = On hand

Then:
  - Clicks View report (if needed)
  - Clicks Export -> Comma Separated Values (.csv)
  - Waits for each download to complete (monitors download folder)
  - Keeps the per-category source CSVs temporary only
  - Saves the merged output into Non-Footwear as: yyyymmdd_On_Hand_Non_Footwear.csv

Env (optional)
--------------
POWERBI_SESSION_FILE=/full/path/to/powerbi_session.json
POWERBI_DOWNLOAD_DIR=/full/path/to/downloads

Debug (optional)
--------------
If the script cannot find/click the Export (or Download) button, set:

  POWERBI_DEBUG_EXPORT=1

It will save:
  export_debug/export_debug_default.png
  export_debug/export_debug_default.json
  export_debug/export_debug_frame.png
  export_debug/export_debug_frame.json

Send me those JSON contents (or paste the console output) and I can lock the selector.

Requires
--------
pip install selenium
)annotationsN)datetime)Path)IterableOptionalTuple)urlparse)	webdriver)By)Keys)ActionChains)	WebDriver)Options)WebDriverWait)expected_conditionszhttps://app.powerbi.com/groups/me/apps/e65cf9b1-6444-4b63-8a86-b94e47839c83/rdlreports/c98f4a29-f532-4bf7-929d-4328ef57a6c2?experience=power-bi
DealershipPOWERBI_AS_OF_VALUEzCurrent Datez614-WALDORF, MDzOn handzconfig/powerbi_session.jsonpaginated-reports.powerbi.comc                    V ^8  d   QhRRRR/# )   msgstrreturnNone )formats   "4.\Pulled_Info\Inventory\Pull_On_Hand_Non-Footwear.py__annotate__r   e   s      C D     c                    \         P                  P                  RR4      P                  4       P	                  4       R9   d   \        V 4       R# R# )z0Verbose logging controlled by POWERBI_VERBOSE=1.POWERBI_VERBOSE N1trueyesyon)osenvirongetstriplowerprint)r   s   &r   _vr.   e   s:    	zz~~',224::<@__c
 `r   c                    V ^8  d   QhRRRR/# )r   startr   r   zIterable[Path]r   )r   s   "r   r   r   o   s       . r   c              #  d   "   V P                  4       pVx  VP                   F  pVx  K	  	  R # 5iN)resolveparents)r0   pparents   &  r   _walk_parentsr7   o   s(     A
G)) s   .0c                   V ^8  d   QhRR/# )r   r   
list[Path]r   )r   s   "r   r   r   v   s      * r   c                 >   . p \         P                  P                  R R4      P                  4       pV'       d7   V P	                  \        V4      P                  4       P                  4       4       V P	                  \
        P                  ! 4       P                  4       \        ,          4       V P	                  \        \        4      P                  4       P                  \        ,          4       V P	                  \
        P                  ! 4       P                  4       R,          \        ,          4       V P	                  \        \        4      P                  4       P                  R,          \        ,          4       \        \
        P                  ! 4       4       FB  pV P	                  V\        ,          4       V P	                  VR,          \        ,          4       KD  	  \        \        \        4      P                  4       P                  4       FB  pV P	                  V\        ,          4       V P	                  VR,          \        ,          4       KD  	  \        4       p. pV  F8  p\        V4      pWc9  g   K  VP                  V4       VP	                  V4       K:  	  V# )POWERBI_SESSION_FILEr!   Powerbi_Background)r(   r)   r*   r+   appendr   
expanduserr3   cwdSESSION_FILENAME__file__r6   r7   setr   add)
candidatesoverridebaseseenoutcss          r   _candidate_session_pathsrK   v   s   Jzz~~4b9??AH$x.335==?@dhhj((*-==>d8n,,.558HHIdhhj((*-AADTTUd8n,,.558LLO__`dhhj)$!112$!558HHI * d8n446==>$!112$!558HHI ? 5DCF=HHQKJJqM	 
 Jr   c                   V ^8  d   QhRR/# )r   r   r   r   )r   s   "r   r   r      s     3 3D 3r   c                     \        4        F  p V P                  4       '       g   K  V u # 	  \        P                  ! 4       P	                  4       \
        ,          # r2   )rK   existsr   r?   r3   r@   r5   s    r   _pick_session_filerP      s<    %'88::H ( 88:"222r   c                    V ^8  d   QhRRRR/# )r   pathr   r   zTuple[str, str, Optional[str]]r   )r   s   "r   r   r      s     2 2T 2&D 2r   c           	     v   V P                  4       '       g   \        4       pR R.pY!R,           Uu. uF  pRV 2NK
  	  up,          p\        V4      ^8  d&   VP                  R\        V4      ^,
           R24       V. RO,          p\	        RP                  V4      4      h\        P                  ! V P                  RR7      4      pVP                  R	4      pVP                  R
4      pVP                  R4      pV'       d	   V'       g)   \        RV  R\        VP                  4       4       24      hWVV3# u upi )zSession file not found.z	Searched:N   Nz  - z
  ... and z more
utf-8encodingexecutor_url
session_iddownload_dirzBad session file z keys=)r!   zFix:z7  - Run from folder containing powerbi_session.json, ORzB  - export POWERBI_SESSION_FILE=/full/path/to/powerbi_session.json)rN   rK   lenr=   FileNotFoundErrorjoinjsonloads	read_textr*   
ValueErrorlistkeys)rR   searchedr   r5   datarZ   r[   r\   s   &       r   _load_session_filerh      s   ;;==+-(+6SM2Mq$qc
M22x=2JJCMB$6#7u=> 
 	
  		#//::dnngn67D88N+L,'J88N+Lz,TF&diik9J8KLMM\11# 3s   D6c               $    V ^8  d   QhRRRRRR/# )r   rZ   r   r[   r   RemoteWebDriverr   )r   s   "r   r   r      s!      C S _ r   c                  aa \         P                  oRVV3R llpV\         n         \        4       p \        P                  ! WR7      pSTn        S\         n        TP                  pT#   \
         dF     \        P                  ! T R7      p LE  \
         d    \        P                  ! T / R7      p  Lki ; ii ; i  S\         n        i ; i)Nc                4   < VR 8X  d	   RR/ /RS/# S! WV4      # )
newSessionvaluecapabilities	sessionIdr   )selfcommandparamsoriginal_executer[   s   &&&r   _patched_execute+attach_to_session.<locals>._patched_execute   s,    l"nb1;
KKv66r   )command_executoroptions)rw   )rw   desired_capabilitiesr2   )rj   executeFirefoxOptionsr	   Remote	TypeErrorr[   current_url)rZ   r[   ru   rx   driver_rt   s   &f    @r   attach_to_sessionr      s    &..7 7
 /O3 "	b%%|UF '"2AM  	bb"))<H b"))<^`ab	b #3sL   
B= A* 
B= *B:6BB= #B62B:3B= 5B66B::B= =C
c               $    V ^8  d   QhRRRRRR/# )r   ar   br   boolr   )r   s   "r   r   r      s!      c c d r   c                     \        V 4      p\        V4      pVP                  VP                  VP                  3VP                  VP                  VP                  38H  #   \         d     R # i ; iF)r   schemenetlocrR   	Exception)r   r   papbs   &&  r   _url_same_pager      s\    a[a[		299bgg.299bii2QQQ s   AA A.-A.c                   V ^8  d   QhRR/# r   r   rj   r   )r   s   "r   r   r      s      / r   c                $   \          Uu. uF	  pR V R2NK  	  ppVP                  R4       VP                  R4       V F7  p V P                  \        P                  V4      pV'       d   V^ ,          u # K9  	  R# u upi   \
         d     KP  i ; i)ziframe[src*='']ziframe[name*='rdl' i]ziframe[id*='rdl' i]N)IFRAME_SRC_HINTSr=   find_elementsr
   CSS_SELECTORr   )r   h	selectorsselframess   &    r   %_find_report_frame_in_current_contextr      s    0@A0@1=2&0@IA,-*+	))"//3?Fay     B  		s   A;0B  BBc                   V ^8  d   QhRR/# r   r   )r   s   "r   r   r      s     ! !o !r   c                    V P                   P                  4        \        V 4      pVe   V P                   P	                  V4       R#  V P                  \        P                  R4      pVR,           Ff  p V P                   P                  4        V P                   P	                  V4       \        V 4      pVe   V P                   P	                  V4        R# Kh  	   V P                   P                  4        R#   \         d     Li ; i  \         d    . p Li ; i  \         d     K  i ; i  \         d     R# i ; i)zAFind report iframe from default content or one nested level down.TiframerT   F)	switch_todefault_contentr   r   framer   r
   r   )r   r   outer_framesouterinners   &    r   !_switch_to_report_iframe_anywherer      s6   ((* 2&9Eu%++BOOXF c""	,,.""5)9&AE   &&u- ! #((* ;      		
  sH   C9 	 D
 6ADD/ 9DD
DDD,+D,/D>=D>c               $    V ^8  d   QhRRRRRR/# r   r   rj   timeoutintr   r   r   )r   s   "r   r   r     s'     B B B3 BRV Br   c                   a
 \         pR p V P                  pV FJ  p V P                  P	                  V4       V P
                  ;'       g    Rp\        Wb4      '       d   Rp MKL  	  V'       g   V P                  V4       \        P                  ! 4       V,           pRo
\        P                  ! 4       V8  d    V P
                  ;'       g    Ro
\        ;QJ d    V
3R lR 4       F  '       g   K   RM	  R M! V
3R lR 4       4      '       d   \        RS
 R24      h V P                  R4      pVR9   d/   \        V 4      '       d    V P                  P                  4        R# \        P                  ! R4       K  Rp	 V P                  p	\!        R	S
: R
V	: 24      h  \         d    . p ELi ; i  \         d     EK  i ; i  \         d    Ro
 ELi ; i  \         d    Rp Li ; i  \         d     R# i ; i  \         d     Li ; i)Fr!   Tc              3  .   <"   T F
  pVS9   x  K  	  R # 5ir2   r   ).0r   last_urls   & r   	<genexpr>'ensure_report_loaded.<locals>.<genexpr>-  s       
 Ms   z2Power BI session is not authenticated. Landed on: z-. Re-run Open_PowerBi and complete login/MFA.zreturn document.readyStateNffffff?zATimed out waiting for paginated report iframe to load. Last URL: z	; title: )zlogin.microsoftonline.comaadcdnmicrosoftonline)interactivecomplete)TARGET_REPORT_URLwindow_handlesr   r   windowr~   r   r*   timeanyRuntimeErrorexecute_scriptr   r   sleeptitleTimeoutError)r   r   desiredfoundhandlesr   curdeadlinereadyr   r   s   &&        @r   ensure_report_loadedr     s   GE'' 	##A&$$**Cc++ ,	  

7yy{W$HH
))+
 	))//RH
 3 

333 

 
 
 DXJ O> > 
	))*FGE //088$$446 

4E 	L	%	4 s    		  	H	&  	E	 !   sv   F -F3F39G G G 	G-  G? F0/F03GGGGG*)G*-G<;G<?HHc               $    V ^8  d   QhRRRRRR/# r   r   )r   s   "r   r   r   S  s'     Z Zo Z Zae Zr   c                    \        W4      pVP                  R  4      pV'       g   \        R4      hVP                  R 4       R# )c                    \        V 4      # r2   )r   ds   &r   <lambda>3switch_to_paginated_report_iframe.<locals>.<lambda>U  s
    ?Br   z,Could not switch to paginated report iframe.c                Z    \        V P                  \        P                  R 4      4      ^ 8  # )z//input[@role='combobox'])r]   r   r
   XPATHr   s   &r   r   r   X  s    Q__RXX7RSTWXXr   N)r   untilr   )r   r   waitoks   &&  r   !switch_to_paginated_report_iframer   S  s7    )D	B	CBIJJJJXYr   c                    V ^8  d   QhRRRR/# r   r   rj   r   r   r   )r   s   "r   r   r   _  s      o 4 r   c                P     V P                  R V4       R#   \         d     R# i ; i)z@arguments[0].scrollIntoView({block:'center', inline:'nearest'});N)r   r   r   elements   &&r   _scroll_into_viewr   _  s+    `bij s    %%c                    V ^8  d   QhRRRR/# r   r   )r   s   "r   r   r   f  s       T r   c                `   \        W4        VP                  4        R #   \         d     Mi ; i \        T 4      P	                  T4      P                  R4      P                  4       P                  4        R #   \         d     Mi ; i T P                  RT4       R #   \         d     R # i ; i)N皙?zarguments[0].click();)r   clickr   r   move_to_elementpauseperformr   r   s   &&r   _safe_clickr   f  s    f& V,,W5;;DAGGIQQS 5w? s0    --AA8 8BB
B B-,B-c                    V ^8  d   QhRRRR/# )r   r   r   r   r   r   )r   s   "r   r   r   x  s     : :C :D :r   c                h   \         P                   ! 4       V,           p\         P                   ! 4       V8  db    V P                  R 4      pV P                  R4      pVe   VR8X  d   Ve   VP                  4       R8X  d   R# \         P                  ! R4       K{  \        R4      h  \         d     L0i ; i)disabledaria-disabledNr!   false333333?zField stayed disabled too long.)r   get_attributer,   r   r   r   )r   r   enddisaria_diss   &&   r   _wait_enabledr   x  s    
))+
C
))+
	''
3C,,_=Hsbyx/?8>>CSW^C^ 	

4
8
99  		s   AB# #B10B1c                   V ^8  d   QhRR/# )r   r   r   r   )r   s   "r   r   r     s      3 r   c                z     V P                  R 4      ;'       g    RP                  4       #   \         d     R# i ; i)rn   r!   )r   r+   r   )inps   &r   _visible_valuer     s;    !!'*00b7799 s   + + ::c                    V ^8  d   QhRRRR/# )r   r   rj   labelr   r   )r   s   "r   r   r     s     L L L Lr   c           
        R RRRRRRRRR	/pW9   d8   V P                  \        P                  W!,          4      pV'       d
   V^ ,          # R
\        P                  ! V4       R2pV P                  \        P
                  V4      pV'       d
   V^ ,          # R\        P                  ! V4       R2pV P                  \        P
                  V4      pV'       d
   V^ ,          # \        RV 24      h)
Store TypezStoreType-inputAs Ofz
AsOf-inputCategoryzCategory-inputStore NozStoreNo-inputBucketzBucket-inputz)//input[@role='combobox' and @aria-label=]z3//input[@role='combobox' and contains(@aria-label, z)]z)Could not find combobox input for label: )r   r
   IDr`   dumpsr   r   )r   r   id_mapelsxpxp2s   &&    r   _find_combobox_inputr     s    '$O.F ""255&-8q6M4TZZ5F4Gq	IB


rxx
,C
1v?

5@Q?RRT
UC


rxx
-C
1v
B5'J
KKr   c                   V ^8  d   QhRR/# r   r   )r   s   "r   r   r     s      ? r   c                    V P                  \        P                  R 4      pV'       d
   V^ ,          # V P                  \        P                  R4      pV'       d
   V^ ,          # R# )z<//div[@role='listbox' and @aria-labelledby='Category-label']zD//div[@role='listbox' and .//*[@id[starts-with(.,'Category-list')]]]N)r   r
   r   )r   boxess   & r   _find_category_listboxr    sK      +ijEQx  +qrEQxr   c               (    V ^8  d   QhRRRRRRRR/# )	r   r   rj   r   r   triesr   r   r   r   )r   s   "r   r   r     s)      ? 3 C PT r   c                    \        V4       F  p V P                  \        P                  R V R24      p\	        W4       \        P                  ! R4        VP                  \        P                  4       \        P                  ! R4       V P                  \        P                  R4      '       d    R# VR8X  g   K  \        V 4      f   K   R# 	  R#   \
         d    \	        Y4        Li ; i  \
         d     Li ; i)z[aria-label='Open r   皙?r   //*[@role='option']Nr   )rangefind_elementr
   r   r   r   r   r   	send_keysr   
ARROW_DOWNr   r   r  )r   r   r   r  r   exps   &&&&  r   _open_dropdownr    s    5\	%%%boo9KE7RT7UVC$ 	

4	MM$//* 	

4*?@@J#9&#A#M%   	%$	%  		s#   /CC/C,+C,/C=<C=c                    V ^8  d   QhRRRR/# )r   r   rj   rn   r   r   )r   s   "r   r   r     s      ? 3 r   c                   VP                  4       pV P                  \        P                  R 4      pV F  p VP	                  4       '       g   K   TP                  ;'       g    RP                  4       ;'       g*    TP                  R4      ;'       g    RP                  4       pTP                  4       T8X  g   K  Tu # 	  V Fp  pVP                  ;'       g    RP                  4       ;'       g*    VP                  R4      ;'       g    RP                  4       pW%P                  4       9   g   Kn  Vu # 	  R#   \
         d     EK  i ; i)r  r!   r   N)	r,   r   r
   r   is_displayedr   textr+   r   )r   rn   lowoptsoptts   &&    r   _find_option_clickabler    s   
++-C*?@D	##%% & XX^^""$RR):):7)C)I)Ir(P(P(R779J  XX^^""$RR):):7)C)I)Ir(P(P(R'')J    		s   D::E
	E
c               (    V ^8  d   QhRRRRRRRR/# )r   r   rj   r   r   rn   r   r   r   )r   s   "r   r   r     s.     .S .S .S .SC .SD .Sr   c                   \        W4      p\        V4       \        W4       \        V4      P	                  4       VP	                  4       8X  d   \        V R V R24       R# \        WV4       \        W4        VP                  \        P                  R4       VP                  V4       \        P                  ! R4       \        W4      pVe   \        W4       M? VP                  \        P                  4       VP                  \        P                   4        VP                  \        P"                  4       \        P                  ! 4       \$        ,           pRp\        P                  ! 4       V8  d   \        V4      pVP	                  4       VP	                  4       8X  d   \        V R V R24       R# VP	                  4       VP	                  4       9   d   \        V R V R24       R# \        P                  ! R4       K  \'        V RV RV: 24      h  \         d     ELi ; i  \         d     ELi ; i  \         d     ELi ; i)	z
: set to ''.Nr   皙?r!   r  z: did not become ''. Last value: )r   r   r   r   r,   r-   r  r   r	  r   CONTROLr   r   r   r  r
  ENTERTABFIELD_TIMEOUTr   )r   r   rn   r   r  r   lasts   &&&    r   set_single_comboboxr     s   
v
-C#f"c  "ekkm3z%+,6#&dllC( MM%JJt
 
/C
F 	MM$//*MM$**%dhh ))+
%CD
))+
c"::<5;;=(UG:eWB/0;;=DJJL(UG:dV2./

3
% 25'Q
RRA    		
  s6   9 H >H+ H= H('H(+H:9H:=IIc               (    V ^8  d   QhRRRRRRRR/# )	r   r   rj   labelsztuple[str, ...]r   r   r   r   r   )r   s   "r   r   r     s6     W WWW W 
	Wr   c                   \         P                   ! 4       V,           pRp\         P                   ! 4       V8  d2    \        V ^R7       V F  p\        W4      p\        V^R7       K  	  R# \        RV 24      h  \         d<   p\        T4      P                   RT 2p\         P                  ! R4        Rp?K  Rp?ii ; i)zHWait for filter controls to be rebuilt after a report parameter changes.Nr   : r  z/Filter panel did not become ready. Last error: )	r   r   r   r   r   type__name__r   r   )r   r"  r   r   
last_errorr   r   es   &&&     r   wait_for_filter_panel_readyr*    s     ))+
C $J
))+
	-fa@*69c1-   
 HU
VV	  	 G,,-Rs3JJJsOO	s   .A8 8B>0B99B>c                   V ^8  d   QhRR/# )r   r   rd   r   )r   s   "r   r   r   &  s      t r   c                j     V P                  \        P                  R 4      #   \         d    . u # i ; i)z.//*[@role='menuitemcheckbox'])r   r
   r   r   )listboxs   &r   _listbox_itemsr.  &  s3    $$RXX/OPP 	s   " 22c                   V ^8  d   QhRR/# )r   
label_textr   r   )r   s   "r   r   r   -  s     	 	 	r   c                   VP                  4       P                  4       p\        V 4       F@  p VP                  ;'       g    R P                  4       P                  4       pWB8X  g   K>  Vu # 	  R#   \         d    R p Li ; ir!   N)r+   r,   r.  r  r   )r-  r0  targetittxts   &&   r   _find_menuitemcheckboxr6  -  su    %%'FW%	77==b'')//1C =I & 	  	C	s   A0A00B ?B c                   V ^8  d   QhRR/# )r   r   zOptional[bool]r   )r   s   "r   r   r   9  s       r   c                     V P                  R 4      ;'       g    RP                  4       pVR9   d   VR8H  #  R#   \         d     R# i ; i)zaria-checkedr!   r$   N)r$   r   )r   r,   r   )elvs   & r   _aria_checkedr;  9  s\    n-33::<!!; "   s   : : A	A	c          
     ,    V ^8  d   QhRRRRRRRRRR	/# )
r   r   rj   r0  r   r   r   allow_missingr   r   r   )r   s   "r   r   r   C  s=     $\ $\/ $\ $\VZ $\ko $\  }A $\r   c                   \         P                   ! 4       \        ,           p. p\         P                   ! 4       V8  d   \        W4      pVfo   \        V 4      pVe   Tp\	        V4       U	u. uF&  qP
                  ;'       g    RP                  4       NK(  	  pp	V'       d   R # \         P                  ! R4       K  \        V4      p
W8X  d   R # \        W4       \         P                  ! R4       \        V 4      pVe   Tp\        W4      pVf   K  \        V4      pW8X  g   EK  R # V'       d   R # V Uu. uF  q'       g   K  VNK  	  upR,          p\        RV RV RV 24      hu up	i u upi )Nr!   r  g      ?N   NzCould not set 'z' to z. Items seen: )r   r  r6  r  r.  r  r+   r   r;  r   r   )r   r-  r0  r   r=  r   
last_itemsitemlb2r4  r   item2cur2rJ   previews   &&&&&          r   _set_menuitemcheckboxrG  C  s4   
))+
%CJ
))+
%g:<(0C<J7<ST<Sb77==b//1<SJTJJsOD!>F!

4$V,?G&w;=U#?$**Qqq**3/G
E'.QXPYZ
[[7 U4 +s   *E&E&4	E+E+c               $    V ^8  d   QhRRRRRR/# )r   r   rj   included_labelszIterable[str]r   r   r   )r   s   "r   r   r   j  s,     ic ic ic icSW icr   c           	     R   V Uu. uF=  p\        V4      P                  4       '       g   K$  \        V4      P                  4       NK?  	  pp\        V4      ^8X  Ed+   V^ ,          p\        V R4      p\	        V4       \        W4       \        W4        V P                  \        P                  R4      pV'       d4   \        W^ ,          4       \        P                  ! R4       \        W4        TP                  \        P                  R4        TP                  T4       \        P                  ! R4       TP                  \        P                   4       \        P                  ! R4       TP                  \        P"                  4       \        P                  ! 4       \$        ,           pRp\        P                  ! 4       T8  d   \'        T4      pTP)                  R4      ;'       g    RP                  4       p	TP+                  4       TP+                  4       9   g$   TP+                  4       T	P+                  4       9   d   \-        RT;'       g    T	 R	24       R
# \        P                  ! R4       K  \/        RT RT: 24      hRp
\        W
4      p\	        V4       \        W4       \1        W
V4       \3        V \$        4      pVP5                  R 4      pVf   \/        R4      h. p\7        V4       FB  pVP8                  ;'       g    RP                  4       pV'       g   K1  VP;                  V4       KD  	  V'       g   \/        R4      hV Uu0 uFB  p\        V4      P                  4       '       g   K$  VP                  4       P+                  4       kKD  	  ppV'       g   \=        R4      hV FB  pVP                  4       P+                  4       pVV9   pV\>        9   d   Rp\A        WVVRR7       KD  	  . p\7        V4       FU  pVP8                  ;'       g    RP                  4       pV'       g   K1  \C        V4      '       g   KD  VP;                  V4       KW  	  V U
u0 uF   qP                  4       P+                  4       kK"  	  pp
V'       g   \/        R4      hV\>        ,          '       d$   \/        R\E        V\>        ,          4      : 24      hVV8w  d&   \/        R\E        V4      : R\E        V4      : 24      h VP                  \        P"                  4        VP                  \        PF                  4       \        P                  ! 4       \$        ,           pRp\        P                  ! 4       V8  d=   \'        V4      pV'       d   \-        RV R	24       R
# \        P                  ! R4       KV  \/        RV: 24      hu upi   \         d     ELi ; i  \         d     ELi ; i  \         d     ELoi ; iu upi u up
i   \         d     Li ; i  \         d     Li ; i)   r   zB//*[@id='Category']//*[contains(@class,'fui-Combobox__clearIcon')]g333333?r   g333333?r!   r   zCategory: set to 'r  Nr  z&Category: did not accept typed value 'r  c                    \        V 4      # r2   )r  r   s   &r   r   $set_category_exact.<locals>.<lambda>  s
    #9!#<r   z Category listbox did not appear.z1Category listbox appeared but contained no items.z'At least one category must be included.F)r=  z&Category: no categories were selected.z)Category: excluded items still selected: zCategory: expected z, got z?Category: did not settle to exact selection. Final field text: )$r   r+   r]   r   r   r   r   r   r
   r   r   r   r   r	  r   r  r  ESCAPEr  r   r   r,   r-   r   r  r   r   r.  r  r=   rc   EXCLUDED_CATEGORIESrG  r;  sortedr  )r   rI  rB  includedrn   r   clear_iconsr   r  r   r   r   r-  r"  r  included_lows
item_labelr  r   selected_labelsselected_lowss   &&                   r   set_category_exactrW  j  s   .=SodTAR!D	!oHS
8}"6:6c&&F 	 ..rxx9}~KFN3

3F(	MM$,,,	MM% JJsOMM$**%JJsOMM$++& iikM)iikC!#&D&&w/552<<>E{{}

,0N*4==5/<=JJsOCE7/Z^Yabcc E
v
-C#f"6#&/Djj<=G=>>Fw'		R&&(4MM$ (
 NOO6E[odTIZ)TZZ\'')oM[BCC
 &&(&%%Gfz7RWX  "$Ow'		R&&(4M$''""4( (
 9HHu[[]((*MHCDD***Fvm^qNqGrFuvww%01F0IPVWdPeOhijjdkk"dhh ))+
%CD
))+
c"&tfB/0

3
XY]X`a
bbQ T  		  		  		H \" I    sx   !WWAW . W A;W+ !W=5"W=&X<X X WWW('W(+W:9W:XXX&%X&c                    V ^8  d   QhRRRR/# r   r   )r   s   "r   r   r     s       T r   c                l   \        V ^4      p\        P                  R3\        P                  R3\        P                  R3\        P                  R33 FK  p VP	                  \
        P                  ! V4      4      p\        W4       \        P                  ! R4        R# 	  R#   \         d     K_  i ; i)   z)//button[normalize-space()='View report']z2//button[.//span[normalize-space()='View report']]zbutton[title='View report']z button[aria-label='View report']g      ?N)r   r
   r   r   r   ECelement_to_be_clickabler   r   r   r   )r   r   locbtns   &   r   click_view_report_if_presentr_    s    #D	>?	GH	78	<=		**R77<=C$JJsO  		s   AB$$B32B3c                   V ^8  d   QhRR/# r   r   r   r   )r   s   "r   r   r     s     " " "r   c                <    V P                  4       '       g   R#   T P                  R4      pTe
   TR8w  d   R#  T P                  R4      ;'       g    RP                  4       P	                  4       pTR8X  d   R#   T P
                  ;'       g    / pTP                  R4      ;'       g    ^ ^ 8:  g!   TP                  R4      ;'       g    ^ ^ 8:  d   R#  R#   \         d     R# i ; i  \         d     Li ; i  \         d     Li ; i  \         d     R# i ; i)	zBest-effort check that an element is *the* visible, usable instance.

Power BI often renders duplicate command bar buttons (hidden/overflow variants).
If we click a hidden clone, nothing happens (even with JS click).
Fr   r!   r   r$   widthheightT)r  r   r   r+   r,   rectr*   )r9  r   r   rs   &   r   _element_interactablerg    s      !
z*?sby$$_5;;BBDJJLv 
GGMMrEE'NaA%!%%/*>*>Q1)D *E 3        s]   C C* C; &C; =D D +D D C'&C'*C87C8;D	D	DDc                   V ^8  d   QhRR/# r   r   )r   s   "r   r   r     s     	 	 	r   c                    Vw  r# V P                  W#4      pT F  p\        T4      '       g   K  Tu # 	  R #   \         d     R # i ; ir2   )r   r   rg  )r   r]  byr   r   r9  s   &&    r   _first_interactablerk    sS    GB""2+  $$I    s   7 AAc                    V ^8  d   QhRRRR/# )r   r   rj   r   r   r   )r   s   "r   r   r     s     #A #A? #AS #Ar   c           	       a \        W4      p\        P                  R 3\        P                  R3\        P                  R3\        P                  R3\        P                  R3\        P                  R3\        P                  R3\        P                  R33pV F/  o VP	                  V3R l4      pVe   \        R	S 24       Vu # K1  	  V F?  o VP	                  \        P                  ! S4      4      pVe   \        R
S 24       Vu # KA  	  \        R4      h  \         d     K  i ; i  \         d     Kn  i ; i)zVbutton[data-testid='toolbar-export-dropdown'], [data-testid='toolbar-export-dropdown']z\button[data-testid='trident-export-menu-button'], [data-testid='trident-export-menu-button']zZbutton[data-testid='toolbar-download-dropdown'], [data-testid='toolbar-download-dropdown']z`button[data-testid='trident-download-menu-button'], [data-testid='trident-download-menu-button']z//*[@data-testid='toolbar-export-dropdown' or @data-testid='trident-export-menu-button' or @data-testid='toolbar-download-dropdown' or @data-testid='trident-download-menu-button']zI//*[self::button or @role='button'][@title='Export' or @title='Download']z//*[self::button or @role='button'][contains(translate(@aria-label,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'),'export') or contains(translate(@aria-label,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'),'download')]z//*[self::button or @role='button'][normalize-space()='Export' or normalize-space()='Download' or .//span[normalize-space()='Export' or normalize-space()='Download']]c                   < \        V S4      # r2   rk  r   r]  s   &r   r   (wait_for_export_button.<locals>.<lambda>0  s    &9!S&Ar   z1[export] picked interactable export/download via z6[export] fell back to presence_of_element_located via z&Export/Download button did not appear.)
r   r
   r   r   r   r.   r   r[  presence_of_element_locatedr   )r   r   r   locatorsr9  r]  s   &&   @r   wait_for_export_buttonrt    sM   )D
 
rs	xy	vw	|}	  I  	J	^_	  G  	H	  |  	}	H 	ABB~FseLM	   	B::3?@B~KC5QR	   ?
@@  		  		s$   (D8D.D+*D+.D=<D=c                    V ^8  d   QhRRRR/# )r   r   rj   r   z	list[str]r   )r   s   "r   r   r   C  s      ? y r   c                   V P                  \        P                  R 4      p. pV FC  p VP                  ;'       g    RP	                  4       pV'       d   VP                  V4       KC  KE  	  \        4       p. pV F-  pWu9  g   K  VP                  V4       VP                  V4       K/  	  VR,          #   \         d     K  i ; i)z//*[@role='menuitem' or @role='option' or @role='menuitemcheckbox']//*[self::span or self::div or self::button]|//*[@role='menuitem' or @role='option' or @role='menuitemcheckbox']r!   r?  )	r   r
   r   r  r+   r=   r   rB   rC   )r   itemsrH   r4  r5  rG   uniqr  s   &       r   _dump_export_menu_textry  C  s        ,a  bEC	77==b'')C

3   5DD=HHQKKKN  9  		s   B4B4B44CCc                   V ^8  d   QhRR/# ra  r   )r   s   "r   r   r   X  s     0 0t 0r   c                     \         P                  P                  R R4      P                  4       P	                  4       p V R9   # )POWERBI_DEBUG_EXPORTr!   r"   )r(   r)   r*   r+   r,   )r:  s    r   _debug_export_enabledr}  X  s5    


-r288:@@BA///r   c                   V ^8  d   QhRR/# r   r   )r   s   "r   r   r   ]  s     =C =C/ =Cr   c           	     |    Rp V P                  V4      #   \         d   pR. R. R\        T4      /u Rp?# Rp?ii ; i)zCollect potentially relevant clickable elements in the *current browsing context*.

This runs inside whatever frame you're currently in.
u  
        const kw = /export|download|csv/i;
        const nodes = Array.from(document.querySelectorAll(
          'button, [role="button"], [role="menuitem"], [role="option"], [data-testid], a'
        ));

        function t(v){ return (v || '').toString().trim(); }
        function trunc(s, n){ s = t(s); return s.length > n ? (s.slice(0, n) + '…') : s; }

        const out = [];
        for (const e of nodes) {
          try {
            const text = trunc(e.innerText || e.textContent || '', 140);
            const aria = trunc(e.getAttribute('aria-label') || '', 140);
            const title = trunc(e.getAttribute('title') || '', 140);
            const dt = trunc(e.getAttribute('data-testid') || '', 140);
            const role = t(e.getAttribute('role') || '');
            const hay = (text + ' ' + aria + ' ' + title + ' ' + dt).trim();
            if (!kw.test(hay)) continue;

            const cs = window.getComputedStyle(e);
            const rect = e.getBoundingClientRect();
            const vis = cs && cs.display !== 'none' && cs.visibility !== 'hidden' && rect.width > 0 && rect.height > 0;

            out.push({
              tag: e.tagName,
              id: t(e.id),
              className: trunc(e.className || '', 180),
              role,
              text,
              ariaLabel: aria,
              title,
              dataTestid: dt,
              disabled: !!e.disabled,
              ariaDisabled: t(e.getAttribute('aria-disabled')),
              href: trunc(e.getAttribute('href') || '', 200),
              visible: vis,
              rect: { x: rect.x, y: rect.y, w: rect.width, h: rect.height },
            });
          } catch (err) {
            // ignore
          }
        }

        const iframes = Array.from(document.querySelectorAll('iframe')).map(f => ({
          id: t(f.id),
          name: t(f.name),
          className: trunc(f.className || '', 180),
          src: trunc(f.getAttribute('src') || '', 240)
        }));

        return { candidates: out.slice(0, 200), iframes };
    rD   iframeserrorN)r   r   repr)r   jsr)  s   &  r   _js_collect_exportishr  ]  sJ    
4
BjC$$R(( Cb)R$q'BBCs    ;6;;c               $    V ^8  d   QhRRRRRR/# )r   r   rj   r   r   r   r   r   )r   s   "r   r   r     s!     < <o <c <d <r   c                   \         P                  ! 4       P                  4       R,          pVP                  RRR7        VRV R2,          pV P	                  \        V4      4       \        RV 24       R
VR\        V RR4      RRR/ /p V P                  VR&   \        V 4      VR&    VRV R2,          pVP                  \        P                  ! V^R7      RR7       \        RV 24       R	#   \         d   p\        RT RT 24        R	p?LR	p?ii ; i  \         d     Li ; i  \         d   p\        RT RT 24        R	p?R	# R	p?ii ; i)zSSave screenshot + JSON of export/download/csv-related UI elements for this context.export_debugTr4   exist_okexport_debug_z.pngzExport debug screenshot: z(debug) screenshot failed in r%  Nr   urlr~   r!   r   	collectedz.json)indentrW   rX   zExport debug JSON: z(debug) json write failed in )r   r?   r3   mkdirsave_screenshotr   r-   r   getattrr   r  
write_textr`   r   )r   r   out_dirpngr)  payloadjsns   &&     r   dump_export_debugr    sZ   hhj  "^3GMM$M.<-wd33s3x()#/0
 	wv}b1R	G!<< 18GK<-we44tzz'!4wG#C5)*%  <-eWBqc:;;<    <-eWBqc:;;<sB   5C. 
D (AD( .D9DDD%$D%(E3E

Ec               $    V ^8  d   QhRRRRRR/# r   r   rj   r   r   r   r   r   )r   s   "r   r   r     s'     L L? LS LY] Lr   c                  a \        W4      p\        P                  R3\        P                  R3\        P                  R33pV EF  o VP	                  V3R l4      pVf   K    \        W4        V P                  RV4        \        V 4      P                  V4      P                  R4      P                  4       P                  4        \        P                  ! R4        TP                  \         P"                  4       \        P                  ! R	4       TP                  \         P$                  4        \+        T \-        ^\/        T^,          4      4      R7        R# 	  R#   \
         d    Rp ELi ; i  \
         d     ELi ; i  \
         d     ELi ; i  \
         d'     TP                  4         L  \
         d      Li ; ii ; i  \
         Ed      \        T 4      P                  \         P"                  4      P                  R	4      P                  \         P$                  4      P                  4         EL  \
         d     T P'                  \        P(                  R
4      P                  \         P"                  4       \        P                  ! R	4       T P'                  \        P(                  R
4      P                  \         P$                  4         EL  \
         d       ELi ; ii ; ii ; i  \
         dx     \        T 4      P                  \         P$                  4      P                  4        \+        T \-        ^\/        T^,          4      4      R7         R#   \
         d      EK6  i ; ii ; i)a  Fallback: focus the *File* button, press Right Arrow to move to Export, then press Enter.

Why this exists:
  - Power BI/Fluent UI command bars often render *duplicate* Export buttons (hidden/overflow variants).
  - Selenium may click a hidden clone successfully (no exception) but nothing opens.
  - Keyboard navigation on the command bar is frequently more reliable.

Returns True if a menu overlay appears after attempting the key sequence.
zPbutton[title='File'], button[aria-label='File'], [role='menuitem'][title='File']z//*[self::button or @role='menuitem' or @role='button'][@title='File' or normalize-space()='File' or .//span[normalize-space()='File']]z//*[self::button or @role='menuitem' or @role='button'][contains(translate(@aria-label,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'),'file')]c                0   < \        V S4      ;'       g    R # r2   ro  rp  s   &r   r   8_open_export_menu_via_file_right_arrow.<locals>.<lambda>  s    ,?3,G,O,O4,Or   Nzarguments[0].focus();r   gQ?r   bodyr$  TF)r   r
   r   r   r   r   r   r   r   r   r   r   r   r   r   r	  r   ARROW_RIGHTr  r  TAG_NAME_wait_for_export_menumaxr   )r   r   r   	file_locsfile_btnr]  s   &&   @r   &_open_export_menu_via_file_right_arrowr    s    )D 
lm	  ]  	^	  p  	qI 	zz"OPH 	f/
	!!"98D	 00:@@FLLNVVX 	

4	t//0JJttzz*		!&#aWq[9I2JKc t o  	H	  		  		
  	  	  		V$..t/?/?@FFtLVVW[WaWabjjl ''V<FFtGWGWXJJt$''V<FFtzzRR  		  	V$..tzz:BBD%fc!S1=M6NO 	s   	E#%E71F	AF AG5'K=#E43E47FF	FFG'F99GGGGK:A"IK6BK!K:!K2	,K6-K:1K2	2K66K:=M?	AM**M;5M?:M;;M?c               $    V ^8  d   QhRRRRRR/# r   r   )r   s   "r   r   r     s!      / C D r   c                (   \        W4      p\        P                  R3\        P                  R3\        P                  R3\        P                  R33 F*  p VP	                  \
        P                  ! V4      4        R# 	  R#   \         d     K>  i ; i)z4Wait for the Export/Download menu overlay to appear.zul.ms-ContextualMenu-listzdiv.ms-ContextualMenuz//*[@role='menu']z'//ul[contains(@class,'ContextualMenu')]N)r   r
   r   r   r   r[  rr  r   )r   r   r   r]  s   &&  r   r  r    s    )D	56	12	&'	<=		JJr55c:;  		s   %BBBc               $    V ^8  d   QhRRRRRR/# r  r   )r   s   "r   r   r     s!     + +_ +s +D +r   c                   \        W4      p\        P                  R3.p\        P                  R3\        P                  R3\        P                  R3\        P                  R3\        P                  R3\        P                  R3.pV FT  p VP	                  \
        P                  ! V4      4      pVe(   \        RV 24       \        W4       \        R	4        R
# KV  	  V FT  p VP	                  \
        P                  ! V4      4      pVe(   \        RV 24       \        W4       \        R	4        R
# KV  	  R#   \         d     K  i ; i  \         d     Kz  i ; i)zJAssumes the export menu is already open in the *current browsing context*.zDbutton[data-testid='export-csv-btn'], [data-testid='export-csv-btn']z"//*[@data-testid='export-csv-btn']z//span[contains(translate(normalize-space(.), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'), 'comma separated values')]/ancestor::button[1]z//li[contains(translate(@title,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'), 'comma separated values')]/descendant::button[1]zy//button[contains(translate(@title,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'), 'comma separated values')]z//*[self::button or @role='menuitem' or @role='option'][contains(translate(normalize-space(.), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'), 'comma separated values')]z//*[self::button or @role='menuitem' or @role='option'][contains(translate(normalize-space(.), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'), '.csv')]z[export] clicking CSV via Export: clicked CSV option.TF)r   r
   r   r   r   r[  rr  r.   r   r-   r   )r   r   r   css_locsxp_locsr]  r9  s   &&     r   _try_click_csv_menu_itemr    ss   )D 
`aH 
78	  n  	o	  a  	b	  O  	P	 S 	T	 A 	B	G 	B::3?@B~/u56F'34	   	B::3?@B~/u56F'34	     		  		s&   AD='AE=EEEEc               (    V ^8  d   QhRRRRRRRR/# )r   r   rj   button_timeoutr   menu_timeoutr   r   r   )r   s   "r   r   r   J  s6     u uuu u 
	ur   c                6    \        R\        V RR4       24       \        R4       Rp \	        WR7      p\        R4       \        R4       \        W4       R	pT'       g   \        R4       R# \        P                  ! R4       \        R4       \        ^\        T^,          4      4      p \        YR7       \        YR7      '       d   \        R4       R	#  T P                  P!                  4         \        YR7       \        YR7      '       d   \        R4       R	#   T P                  P!                  4        T P#                  \$        P&                  R4      p\)        TR,          4       F  w  r T P                  P+                  T
4       \        T ^R7      '       d9   \        RT	 24       \        R4         T P                  P!                  4         R	#   T P                  P!                  4        K  	  R#   \         d     ELi ; i  \         d   p\        R
T 24       \        R4        \        T P                  R4      4      pT'       d   \        R4       \        R4        Rp?EL  \         d    p\        RT 24       Rp Rp? Rp?EL;Rp?ii ; iRp?ii ; i  \         d,     \        YR7      '       d     EL  \         d      ELi ; ii ; i  \         d%     \        YR7        EL  \         d      ELi ; ii ; i  \         d     ELi ; i  \         d      R	# i ; i  \         d     ELii ; i  \         d     EK  i ; i   T P                  P!                  4        i   \         d     i i ; i; i  \         d     R# i ; i)zTry clicking Export/Download and then the CSV item, without switching frames.

Returns True on success, False if the CSV option wasn't found/clicked.
z [export] attempt in context url=r~   r!   z/Export: searching for Export/Download button...Fr$  z([export] clicking export/download buttonz#Export: clicking Export/Download...Tz<[export] export button not found/clickable in this context: z@Export: export button not found/clickable; trying JS fallback...a  
                const sel = [
                  "[data-testid='toolbar-export-dropdown']",
                  "[data-testid='trident-export-menu-button']",
                  "[data-testid='toolbar-download-dropdown']",
                  "[data-testid='trident-download-menu-button']",
                  "button[title='Export']",
                  "button[title='Download']",
                  "button[aria-label*='Export']",
                  "button[aria-label*='Download']",
                  "[role='button'][title='Export']",
                  "[role='button'][title='Download']",
                ].join(',');
                const b = document.querySelector(sel);
                if (b) { b.click(); return true; }
                return false;
                z*[export] JS-clicked export/download buttonz#Export: JS-clicked Export/Download.z#[export] JS fallback click failed: Nz7Export: could not find Export/Download in this context.r   z4Export: export menu should be open; selecting CSV...r  r   :N   Nz$[export] clicked CSV inside iframe #)r.   r  r   r-   rt  r   r   r   r   r   r  r   r  r  r  r   r   r   r
   r   	enumerater   )r   r  r  export_clicked
export_btnr)  e2per_ctx_timeoutr   idxfrs   &&&        r   &_attempt_export_csv_in_current_contextr  J  sO   
-gf]2.N-OPQ 

;<N##+FK

5634F'@ GHJJt	
@A!S!123Of>  @@+,((*	!&B $FDD/0 E((*%%boox@ -GC  &&r*+FA>>=cUCD78$$446 ?$$446 ." U    #
I!MNPQ	#!&"7"7# N& ?@;< 	#4RD9:"NN	#7#X  	5fVV 		"  	6vWW 		  & !   
 ! $$446   sz  G9 /H +J# L 2K >L AN	 1AL3:L N	 MM3N	 9HHJ J08I..J9J	JJJJ #K/KKKKKL(K74L 7LLL LLL LL L0+N	 /L00N	 3M>MMMMN	 MN	 NM54N5N	 NN	NN	 	NNc                    V ^8  d   QhRRRR/# r   r   )r   s   "r   r   r     s     ; ; ;4 ;r   c                   \        V 4      '       d   R#  V P                  \        P                  R4      pV Fn  p V P                  P                  V4       \        V ^^
R7      '       d      V P                  P                  4         R#   V P                  P                  4        Kp  	   V P                  P                  4        \        V 4      '       d   R# \        4       '       d&    \        V R4        \        V 4       \        V R4       \        V 4      p\        RV 24      h  \         d5     T P                  P                  4          R#   \         d       R# i ; ii ; i  \         d     Li ; i  \         d5     T P                  P                  4         EKk    \         d      EK{  i ; ii ; i   T P                  P                  4        i   \         d1     T P                  P                  4         i   \         d      i i ; ii ; i; i  \         d     EL}i ; i  \         d     ELti ; i  \         d     ELVi ; i  \         d     ELPi ; i)zClick Export/Download and choose CSV.

Power BI can surface the control either inside the paginated report frame *or* on the
outer (app.powerbi.com) top bar. We try both.
Nr   )r  r  defaultr   zCould not click Export/Download -> CSV. Set POWERBI_DEBUG_EXPORT=1 and rerun to generate export_debug/*.json + *.png. Seen menu items: )r  r   r
   r   r   r   parent_framer   r   r}  r  r   ry  r   )r   inner_framesr  rG   s   &   r   
export_csvr    s?    .f55++BOOXFB  &&r*9&QRacdd$$113 e
$$113 (((* .f55 	fi0	-f5fg. "&)D
	 6	# = ! ((88::$ 	  
 ! ((88::$ 	$$113  ((88:$ 	      		
  		s<  %H /E',D%H 	F:E8%H *H* (H< 5I %E$1EH E E$H E  E$$H 'E52F:4E55F:8F7F"H "F3-F7.H 2F33F77H :H<GHH	#G?=H?H
H	HHH	HH H'&H'*H98H9<I
IIIc                    V ^8  d   QhRRRR/# )r   session_download_dirOptional[str]r   r   r   )r   s   "r   r   r     s      }  r   c                   \         P                  P                  RR4      P                  4       pV'       d=   \	        V4      P                  4       P                  4       pVP                  RRR7       V# V '       d=   \	        V 4      P                  4       P                  4       pVP                  RRR7       V# \        P                  ! 4       R,          P                  4       pVP                  RRR7       V# )z
Where Firefox is actually downloading *right now*.
We DO NOT force it to Inventory (or anywhere). We just watch the real folder.
Priority:
  1) POWERBI_DOWNLOAD_DIR env override
  2) download_dir stored in powerbi_session.json
  3) ./downloads fallback
POWERBI_DOWNLOAD_DIRr!   Tr  	downloads)	r(   r)   r*   r+   r   r>   r3   r  r?   )r  envr5   s   &  r   resolve_download_dirr    s     **../
4
:
:
<C
I  "**,	t,%&113;;=	t,	k	!**,AGGD4G(Hr   c                    V ^8  d   QhRRRR/# )r   r   r   r   	set[Path]r   )r   s   "r   r   r     s     3 3t 3	 3r   c                    V P                  4       '       g   \        4       # V P                  4        Uu0 uF  qP                  4       '       g   K  VkK  	  up# u upi r2   )rN   rB   iterdiris_file)r   r5   s   & r   _snapshot_filesr    s:    88::uyy{2{!iikAA{222s   AAc               (    V ^8  d   QhRRRRRRRR/# )r   r\   r   beforer  r   r   r   r   )r   s   "r   r   r   $  s/     !U !U !Ui !U# !Uei !Ur   c                f   \         P                   ! 4       V,           pRp\         P                   ! 4       V8  Ed]   V P                  4        Uu. uF  qUP                  4       '       g   K  VNK  	  ppV Uu. uF'  qUP                  P	                  R4      '       d   K%  VNK)  	  ppV Uu. uF  qUV9  g   K  VNK  	  ppV'       g   \         P
                  ! R4       K  VP                  R RR7       V^ ,          p	T	pW	P                  R,           ,          P                  4       '       d   \         P
                  ! R4       EK"  \        ;QJ d    R V 4       F  '       g   K   RM	  RM! R V 4       4      '       d   \         P
                  ! R4       EKu  V	# \        R	V 24      hu upi u upi u upi )
z_
Waits for a new fully-downloaded file to appear in download_dir.
Handles Firefox .part files.
N.partr   c                6    V P                  4       P                  # r2   )statst_mtimerO   s   &r   r   'wait_for_new_download.<locals>.<lambda>5  s    qvvx00r   T)keyreversec              3  V   "   T F  qP                   P                  R 4      x  K!  	  R# 5i)r  N)nameendswith)r   r5   s   & r   r   (wait_for_new_download.<locals>.<genexpr>?  s     7Avvw''s   ')Fz.Download did not complete in time. Last seen: )
r   r  r  r  r  r   sortrN   r   r   )
r\   r  r   r   	last_seenr5   files	completednewcands
   &&&       r   wait_for_new_downloadr  $  s>   
 ))+
CI
))+
(002B2qiik2B %F1VV__W-EQQ	F#7)Qqq)7JJt0$?1v	 II/088::JJt 3773337777JJt
G	{S
TT1 CF7s$   F$(F$4"F)F)'F.4F.c          
     ,    V ^8  d   QhRRRRRRRRR	R/# )
r   
downloadedr   r  Optional[Path]stamp_overrider  suffixr   r   r   )r   s   "r   r   r   H  s:       " 	
 
r   c                *   Vf/   \        \        4      P                  4       P                  R,          pVP	                  RRR7       T;'       g    RP                  4       ;'       g%    \        P                  ! 4       P                  R4      p\        T;'       g    R4      P                  4       P                  RR4      pV'       d   RV 2MRpW RV R	2,          pVP                  4       '       d   VP                  4        V P                  V4       V# )
z
Moves the downloaded file into this script's folder (or out_dir if provided)
and renames it to: YYYYMMDD_On_Hand_Non_Footwear[_Suffix].csv
Non-FootwearTr  r!   %Y%m%d r   _On_Hand_Non_Footwearz.csv)r   rA   r3   r6   r  r+   r   nowstrftimer   replacerN   unlink)r  r  r  r  stampsafe_suffixsuffix_partrH   s   &&&&    r   move_to_dated_namer  H  s     x.((*11NBMM$M.!!r((*OOhlln.E.Eh.OEfll#))+33C=K'2Ak]#K
g2;-tD
DC zz||

sJr   c               (    V ^8  d   QhRRRRRRRR/# )	r   inputsr9   r  r  r  r  r   r   r   )r   s   "r   r   r   d  s*     # #Z # #`n #z~ #r   c           
        V '       g   \        R 4      hVf/   \        \        4      P                  4       P                  R,          pVP                  RRR7       T;'       g    RP                  4       ;'       g%    \        P                  ! 4       P                  R4      pW# R2,          pVP                  4       '       d   VP                  4        Rp\        4       pVP                  RR	RR
7      ;_uu_ 4       p\        P                  ! V4      pV  F  p	V	P                  RR	RR
7      ;_uu_ 4       p
\        P                   ! V
4      p \#        V4      pTf   TpTP'                  T4       MY8w  d   \        RT	 24      hT Ft  p\(        ;QJ d    R T 4       F  '       g   K   RM	  RM! R T 4       4      '       g   K?  \+        T4      pY9   d   KR  TP-                  T4       TP'                  T4       Kv  	  RRR4       K  	  RRR4       V#   \$         d     RRR4       EK  i ; i  + '       g   i     EK+  ; i  + '       g   i     T# ; i)z'No category export files were provided.Nr  Tr  r!   r  z_On_Hand_Non_Footwear.csvwz	utf-8-sig)rY   newlinerf  z-CSV header mismatch while combining exports: c              3  h   "   T F(  p\        T;'       g    R 4      P                  4       x  K*  	  R# 5ir2  )r   r+   )r   cells   & r   r   +combine_category_exports.<locals>.<genexpr>  s&     G34s4::244663s   22F)rc   r   rA   r3   r6   r  r+   r   r  r  rN   r  rB   opencsvwriterreadernextStopIterationwriterowr   tuplerC   )r  r  r  r  out_pathheader	seen_rowshandler  source_pathsource_handler  current_headerrowrow_keys   &&&            r   combine_category_exportsr  d  s   BCCx.((*11NBMM$M.!!r((*OOhlln.E.Eh.OE7";<<H#F&)eI	s["	=	=F#!K!!#R!HHMM2%)&\N >+FOOF+#-$'TU`Ta%bcc!C3G3G333G3GGG #CjG+ MM'*OOC( " IH " 
>, O %  IH	 IHH 
>	=, Os[   88H?0H*H
AH*#H*>;H*9H?H'H*H?&H''H**H<5
H??I	c                   V ^8  d   QhRR/# )r   r   r   r   )r   s   "r   r   r     s     ? ?c ?r   c            
     t   \        4       p \        P                  P                  R R4      P	                  4       p \        V 4      w  r#p\        RV  24       \        W#4      p\        T4      p \        P                  ! RR7      ;_uu_ 4       p\        T4      p	. p
\         F  w  rTP                  \        4       \        T4       \!        T4       \#        TR\$        4       \'        TRR7       \#        TR\(        4       \#        TR	\*        4       \#        TR
\,        4       \/        Y[.4       \1        T4      p\3        T4       \5        T4       \7        Y}\8        R7      p\;        TT	TTR7      pT
P=                  T4       \        RT RT 24       K  	  \?        YR7      p\        RT 24       RRR4       ^ #   \         d   p\        RT 24        Rp?^# Rp?ii ; i  + '       g   i     ^ # ; i  \         d   p TP@                  PC                  4        \        PD                  ! 4       R,          pTPG                  \I        T4      4       \        RT 24       M  \         d     Mi ; i\J        PL                  ! 4       p\        R\O        T4      PP                   RT: RT 24        Rp?^# Rp?ii ; i)POWERBI_OUTPUT_STAMPr!   zUsing session file: z5ERROR: Could not attach to existing Firefox session.
Nrw_non_footwear_)prefixr   r   r   r   )r"  r$  )r  r  r  zCaptured temporary z	 CSV as: )r  zSaved combined CSV as: z#pull_on_hand_non_footwear_error.pngzSaved error screenshot: z.ERROR: Failed while running automation.
Type: z

Message: z
Traceback:
)r   r   r   r   ))rP   r(   r)   r*   r+   rh   r-   r   r   r  tempfileTemporaryDirectoryr   CATEGORY_RUNSr   r   r   r   STORE_TYPE_VALUEr*  AS_OF_VALUESTORE_NO_VALUEBUCKET_VALUErW  r  r_  r  r  DOWNLOAD_TIMEOUTr  r=   r  r   r   r?   r  r   	traceback
format_excr&  r'  )session_pathoutput_stamprZ   r[   r  r   r)  r\   temp_dir_nametemp_direxported_pathscategory_labelr  r  r  
final_pathcombined_pathrH   tbs                      r   mainr&    su   %'L::>>"8"=CCEL9KL9Y6"6$\N34"<<
 ((<=L/((0BCC}M*H)+N*7&

,-$V,1&9#FL:JK+F;fg#FG[A#FJG#FHlC"6+;<(6,V46"2<Qab
/$#/!	
 %%j1+N+;9ZLQR/ +82 5^aM+M?;<; D` o  FqcJK DC` #  	,,.((*DDC""3s8,,SE23 		!!#!W%%& 'u $ 	
 sn   'F8 -G3 D"G.G3 8GGGG0	*G3 0G3 3J7?AIJ2I,)J2+I,,A J22J7__main__))zApparel & GearApparel_And_Gear)zFootwear AccessoriesFootwear_Accessories>   r   footwear
select all)r   z/rdlreports/rdlembedrdl)   )r   r   r   r   )   )
   )      )NNr!   )NN)f__doc__
__future__r   r`   r(   r  r   r  r  r   pathlibr   typingr   r   r   urllib.parser   seleniumr	   selenium.webdriver.common.byr
   selenium.webdriver.common.keysr   'selenium.webdriver.common.action_chainsr   #selenium.webdriver.remote.webdriverr   rj   "selenium.webdriver.firefox.optionsr   r{   selenium.webdriver.support.uir   selenium.webdriver.supportr   r[  r   r  r)   r*   r+   r  r  r  r  rO  r@   IFRAME_SRC_HINTr   PAGE_TIMEOUTIFRAME_TIMEOUTr  EXPORT_TIMEOUTr  r.   r7   rK   rP   rh   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r   r*  r.  r6  r;  rG  rW  r_  rg  rk  rt  ry  r}  r  r  r  r  r  r  r  r  r  r  r  r  r&  r'  
SystemExitr   r   r   <module>rE     s  )V #  	 
      , , !  + / @ L H 7 @
 V    jjnn2NCIIK]]~" 8 0 1   8328<!H BN BJ O] Z$ +8 :L4,&.Sf A W6	$\Nic`&"J	 DR #AJ*0
=C@<@L^ +\up;F23 Qa !UH8#T?D z
TV
 r   