
    HE^j                     P   U d dl Z d dlZd dlZd dlZd dlmZmZ d dlm	Z	 d dl
mZ 	 d dlmZ dZdaeej"                     ed<   d	ej"                  fd
Zd	ej"                  fdZdeded	ej,                  fdZddeded	ee   fdZd	eeee      ef   fdZy# e$ r	 d dlZdZY uw xY w)    N)OptionalList)settings)_log)requestsTF_sessionreturnc                      t         6t        rt        j                  d      a t         S t        j                         a t         S )z<Return a persistent requests Session (reuses TLS handshake).	chrome110)impersonate)r   HAS_CURL_CFFIr   Session     &/home/container/backend/scraper/api.py_get_sessionr      s9     ''K@H O  '')HOr   c                  n    t         	 t         j                          da t               S # t        $ r Y w xY w)zcClose the current session if any, and initialize a new one to recover from stale connection errors.N)r   close	Exceptionr   r   r   r   _recreate_sessionr      s<     	NN H>  		s   ( 	44methodurlc                     t               }t        r|j                  dd       	  |j                  | |fi |S # t        $ rL}t        d| d       	 t               } |j                  | |fi |cY d}~S # t        $ r}||d}~ww xY wd}~ww xY w)zPMake an HTTP request with automatic session recovery on failure (Arch 4 helper).r   r   zHTTP connection failed: z). Recreating session and retrying once...N)r   r   
setdefaultrequestr   r   r   )r   r   kwargssessioneretry_es         r   _requestr    (   s    nG-5!wvs5f55 !'s*STU	!')G"7??639&99 	!q 	!!s9   8 	BBA4.B4	B=B  BBBpage	page_sizec                    |t         j                  }t         j                  t        |       t        |      dd}t	        j
                  |      }t        j                  j                  t        j                  j                  |            }dt         j                   d| }t         j                  |z   }d}t        dt         j                  z         D ]Y  }	 t        d|t         j                  d	      }	|	j                  d
k(  r_|	j	                         }
|
j                  d      d
k(  r|
j                  di       c S d|
j                  d       d|
j                  d       }nd|	j                   }|t         j                  k  st         j"                  r0|t%        t         j"                        k  rt         j"                  |   }n&t         j"                  rt         j"                  d   }nd}t'        d|  d| d| d|dz    dt         j                   d       t)        j*                  |       \ t'        d|  ddt         j                  z    d|        y# t         $ r}d| }Y d}~d}~ww xY w)z;Fetch a single page of products from the Lenovo Outlet API.Nv2)pageFilterIdr!   pageSizeversionz?pageFilterId=z&loyalty=false&params=   GET   )headerstimeout   statusdataz
API error z: msgzHTTP zNetwork error: g      @Page z fetch failed (z). Retrying in u   s… (attempt /)z fetch failed after z attempts: )r   	PAGE_SIZEPAGE_FILTER_IDstrjsondumpsurllibparsequoteAPI_URLrangeMAX_PAGE_RETRIESr    HEADERSstatus_codegetr   RETRY_DELAYSlenr   timesleep)r!   r"   params_dictparams_jsonparams_encodedquery_stringfull_url
last_errorattemptresponser/   r   delays                r   fetch_productsrP   7   sO   &&	 !//D		N	K **[)K\\''(:(:;(GHN
001!"	$ 
 ,.H $JX6667	/x9I9ISUVH##s*}}88H%,88FB//#-dhhx.@-ADHHUOCT!UJ$X%9%9$:;
 X...$$3x7L7L3M)M --g6&& --b15oj\~^ehi^i]jjklt  mF  mF  lG  GH  I  JJJu/ 82 	5*1x/H/H+H*IU_T`	ab  	/*1#.J	/s   A!I;6I	I-I((I-c            
         d} d}t        t        dd      }t        dt        j                        }|st	        d       y|j                  d	g       }|r	 dd
l}|j                  j                  |j                  j                  t        j                              }|j                  j                  |d      }t        |dd      5 }t        j                  ||dd       d
d
d
       t	        d|        |j                  dg       }
|
rt!        |
t"              st	        d       y|
r|
d   j                  d      nd
}|st	        d       yt%               g dt"        t&           dd
ffd} ||       |j                  dd      }|r&t)        j*                  |t        j                  z        nd}|dkD  rt	        d| d| d       t-        d|dz         D ]  }t/        j0                  t        j2                         t        |t        j                        }|s3|dz  }d} t	        d| d| d| d        ||kD  rt	        d!| d"        yv|j                  dg       }|r|d   j                  d      s3|dz  }d} t	        d| d#| d| d        ||kD  rt	        d!| d"        y ||d   j                  dg               | fS # 1 sw Y   xY w# t        $ r}	t	        d|	        Y d
}	~	d
}	~	ww xY w)$zlRetrieve every active product across all paginated API pages.
    Returns (all_products, partial_scan).
    Fr   MAX_FAILED_PAGES   r(   )r!   r"   z4First page fetch failed. Aborting scrape completely.)NTfacetGroupsNzfacet_groups.jsonwzutf-8)encoding)indentensure_asciiz!Saved live filter facetGroups to zError saving facetGroups: r/   z<Unexpected API response: 'data.data' is empty or not a list.productsz;Unexpected API response: no products in first page payload.r	   c                     | D ]=  }|j                  d      }|s|vsj                  |       j                  |       ? y )NproductCode)rB   addappend)rY   pcodeall_products
seen_codess      r   _add_uniquez,get_all_active_products.<locals>._add_unique   s?    A55'DJ.t$##A&	 r   countzScaling fetch: Found z items across z pages.Tr2   z	 failed (r3   z).zFailed pages exceeded limit (z). Aborting cycle.z returned empty products ()getattrr   rP   r5   r   rB   ospathdirnameabspathDB_FILEjoinopenr8   dumpr   
isinstancelistsetdictmathceilr>   rE   rF   INTER_PAGE_DELAY)partial_scanfailed_pages_count
max_failedr/   facet_groupsre   db_dirfacet_groups_pathfg_filer   itemsfirst_page_productsrb   rc   total_pagesr^   	page_data
page_itemsr`   ra   s                     @@r   get_all_active_productsr   h   s    L#5q9JqH,>,>?DCD 88M2.L	3WW__RWW__X5E5E%FGF "V5H I'w?7		,N @45F4GHI HHVR E
5$/KL6;%(,,z2JK5J!L'd4j 'T ' #$HHWa E;@$))EH$6$667aKQ$UG>+gNOq+/*AJJx001&A9K9KLI"a'"#uQCy);(<Aj\LM%
28DVWX%"vr2JZ]%6%6z%B"a'"#uQC9:L9MQzlZ\]^%
28DVWX%
1))*b9:- +0 %%s @?  	3-aS122	3s1   A0K K K KK 	K:!K55K:)N)rE   rq   urllib.parser:   r8   typingr   r   backend.configr   backend.logging_configr   	curl_cffir   r   ImportErrorr   r   __annotations__r   r   r7   Responser    intrp   rP   tuplern   boolr   r   r   r   <module>r      s        ! # '"M
 (,(8##
$ +h&& 	8++ 	!S !s !1B1B !/ / / /bM&xT
';T'A!B M&w  Ms   B B%$B%