o
    vi[                    @   s  d dl Z d dlZeeZd dlmZ d dlmZmZ d dl	m
Z
 ddlmZ d dlZedddZedddZe
dZdd
dZddiZdddZdd Zdd Zdd Zdd Zedddd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 d.d/ Z!d0d1 Z"d2d3 Z#d4d5 Z$d6d7 Z%d8d9 Z&d:d; Z'd<d= Z(d>d? Z)d@dA Z*dBdC Z+dDdE Z,dFdG Z-dHdI Z.dJdK Z/d dlmZ eddddLdM Z0dNdO Z1dPdQ Z2dRdS Z3dTdU Z4dVdW Z5dXdY Z6dZd[ Z7d\d] Z8d^d_ Z9d`da Z:dbdc Z;ddde Z<dfdg Z=dhdi Z>djdk Z?dldm Z@dndo ZAdpdq ZBdrds ZCdtdu ZDdvdw ZEdxdy ZFdzd{ ZGd|d} ZHd~d ZIdd ZJdd ZKdd ZLdd ZMdd ZNdd ZOdd ZPdd ZQdd ZRdd ZSdS )    N)retry)datetime	timedelta)timezone   )validate_asini  zAmerica/Toronto%Y-%m-%dc              
   C   s   | du st | tr| dkrdS ztt| d }|jtddt}||W S  t	yE } zt
d|  d|  W Y d}~dS d}~ww )	z1Converts Keepa time minutes to a datetime string.Nr   -minutesUTC)tzinfoz Error converting Keepa minutes (z) to datetime: )
isinstanceintKEEPA_EPOCH_DATETIMEr   replacer   
astimezone
TORONTO_TZstrftime	Exceptionloggererror)keepa_minutesdate_formatdt_utc
dt_torontoe r   6/var/www/agentarbitrage/keepa_deals/stable_products.pykeepa_minutes_to_datetime_str(   s   r   z
User-Agentz>Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/90.0.4430.212Fc                 C   s4  zp|  |g }td| d| d| d|  |r t||kr/td| d| d W dS || }td| d| d|  t|trRt|d	krP|d	 nd
}|d
ksZ|d u r]W dS |rhd|| dW S t|| dW S  tt	t
fy } ztd|  d| d| dt|  W Y d }~dS d }~ww )Nzget_stat_value: key=z, index=z, stats[z]=z get_stat_value: No data for key=z, returning '-'r	   z, value=r   $.2f,zget_stat_value failed: stats=z, key=z, error=)getr   debuglenwarningr   listr   
IndexError	TypeErrorAttributeErrorr   str)statskeyindexdivisoris_pricevaluer   r   r   r   get_stat_value:   s(   "
&r3   c              
   C   s  t d| dd  | di }|ddgd d }|d	dgd d }|d
ks8|d
k s8|d u s8|d u rNt d| dd d| d|  ddiS z|| | d }|dd}t d|  d|iW S  ty } zt dt|  ddiW  Y d }~S d }~ww )Nzpercent_down_90 input: asinr	   r-   avg90r          currentr   z#No valid avg90 or current for ASIN z: avg=z, curr=zPercent Down 90d   .0f%zpercent_down_90 result: zpercent_down_90 failed: )r   r%   r$   r   r   r,   )productstats_90avgcurrr2   percentr   r   r   r   percent_down_90P   s"    $
rA   c                 C   sB   |  dd}d|dkrd| dndi}td| d|  |S )Nr4   r	   zAMZ linkzhttps://www.amazon.com/dp/z?psc=1&aod=1zamz_link result for ASIN : r$   r   r%   r<   r4   resultr   r   r   amz_linkr   s   rF   c                 C   s@   |  dd}d|dkrd| ndi}td| d|  |S )Nr4   r	   z
Keepa Linkzhttps://keepa.com/#!product/1-zkeepa_link result for ASIN rB   rC   rD   r   r   r   
keepa_linkz   s   rG   c                 C   sV   |  dd}|  dd}|dkrtd|  td| d|d d   d	|iS )
Ntitler	   r4   unknownz#get_title: No title found for ASIN zget_title result for ASIN rB   2   Titler$   r   r'   r%   )r<   rH   r4   r   r   r   	get_title   s   rM      i  )stop_max_attempt_number
wait_fixedc              
   C   s   |  dd}td|  |dkr"td|  dd  dd	iS z0tt|d
 }|jd u r5t|}n|	t}|
d}td|  dd d|  d|iW S  tyr } ztdt|  dd	iW  Y d }~S d }~ww )NtrackingSincer   zTracking since - raw ts=i z No valid trackingSince for ASIN r4   rI   zTracking sincer	   r
   r   zTracking since result for ASIN rB   ztracking_since failed: )r$   r   r%   r   KEEPA_EPOCH_LEGACYr   r   r   localizer   r   r   r,   )r<   tsdtr   	formattedr   r   r   r   tracking_since   s$   



rW   c                 C   sF   |  dg }d|r|d d ndi}td|  dd d	|  |S )
NcategoryTreezCategories - Rootr   namer	   z categories_root result for ASIN r4   rI   rB   rC   r<   category_treerE   r   r   r   categories_root   s   r\   c                 C   s^   |  dg }dt|dkrddd |dd  D ndi}td|  d	d
 d|  |S )NrX   zCategories - Subr7   z, c                 s       | ]}|d  V  qdS rY   Nr   .0catr   r   r   	<genexpr>       z!categories_sub.<locals>.<genexpr>r	   zcategories_sub result for ASIN r4   rI   rB   )r$   r&   joinr   r%   rZ   r   r   r   categories_sub   s   0re   c                 C   sN   |  dg }d|rddd |D ndi}td|  dd	 d
|  |S )NrX   zCategories - Treez > c                 s   r]   r^   r   r_   r   r   r   rb      rc   z"categories_tree.<locals>.<genexpr>r	   z categories_tree result for ASIN r4   rI   rB   )r$   rd   r   r%   rZ   r   r   r   categories_tree   s    rf   c                 C   s.   |  dd}d|i}td| d|  |S )Nr4   r	   ASINzget_asin result for ASIN rB   rC   rD   r   r   r   get_asin   s   rh   c                 C   J   |  dd}|r| dkrd}d|i}td|  dd d|  |S )	Nmanufacturerr	    Manufacturerzmanufacturer result for ASIN r4   rI   rB   r$   stripr   r%   )r<   manufacturer_valuerE   r   r   r   rj         rj   c                 C   ri   )	Nbrandr	   rk   Brandzget_brand result for ASIN r4   rI   rB   rm   )r<   brand_valuerE   r   r   r   	get_brand   rp   rt   c                 C   ri   )	Nauthorr	   rk   Authorzauthor result for ASIN r4   rI   rB   rm   )r<   author_valuerE   r   r   r   ru      rp   ru   c                 C   ri   )	Nbindingr	   rk   Bindingzbinding result for ASIN r4   rI   rB   rm   )r<   binding_valuerE   r   r   r   rx      rp   rx   c              
   C   s  |  dd}td| d |  dp|  d}|du r*td| d d	d
iS d}t|trut|}z&t|dkrOd|  krFdkrOn nt	|d}n|dkrZt
t|d }W n9 ttfyt   td| d| d d}Y n!w t|trg d}|D ]}z	t	||}W  n	 ty   Y qw |r|d}td| d| d| d d	|iS td| d| dt| d d	d
iS )z
    Retrieves and formats the publication date of the product to a standard 'YYYY-MM-DD' format.
    Handles Keepa Time Minutes (KTM), YYYYMMDD integers, and various common date string formats.
    r4   rI   ASIN z5: Attempting to get and standardize publication date.publicationDatereleaseDateNzD: Neither 'publicationDate' nor 'releaseDate' found. Outputting '-'.zPublication Dater	      i%!iL@z%Y%m%dr   r
   z : Could not parse integer date ''.)r   z%m/%d/%yz%m/%d/%Yz%Y-%mz%b-%yz%Yr   z: Parsed date 'z' to standard format 'z: Date value 'z	' (type: z&) could not be parsed. Outputting '-'.)r$   r   r%   r'   r   r   r,   r&   r   strptimer   r   
ValueErrorr*   r   infotype)product_datar4   
date_value	dt_objectdate_strformats_to_tryfmtformatted_dater   r   r   get_publication_date   sD   
$

"r   c                 C   s6   |  dd}d|dkr|d ddi}|S di}|S )NpackageWeightr    zPackage Weighti  r"   z kgr	   r$   )r<   weightrE   r   r   r   package_weightS  s
   r   c                 C   @   |  dd}|dks|dkrddi}|S d|d ddi}|S )	NpackageHeightr    r   zPackage HeightMissing
   .1f cmr   )r<   heightrE   r   r   r   package_heightZ     r   c                 C   r   )	NpackageLengthr    r   zPackage Lengthr   r   r   r   r   )r<   lengthrE   r   r   r   package_lengthd  r   r   c                 C   r   )	NpackageWidthr    r   zPackage Widthr   r   r   r   r   )r<   widthrE   r   r   r   package_widthn  r   r   c              
   C   s   |  dd}|  dd}td| d|  |dkr*td| d|  d	d
iS z,tt|d }|jd u r=t|}n|	t}|
d}td| d|  d	|iW S  tyy } ztd| dt|  d	d
iW  Y d }~S d }~ww )NlistedSincer   r4   rI   zListed since - raw ts=
 for ASIN zNo valid listedSince (ts=) for ASIN zListed sincer	   r
   r   zListed since result for ASIN rB   zlisted_since failed for ASIN )r$   r   r%   r   rR   r   r   r   rS   r   r   r   r   r,   )r<   rT   r4   rU   r   rV   r   r   r   r   listed_sincex  s&   



r   c                 C   s@  |  di }t|dddd}|dkrd|iS |  d}|rNt|dkrN|d rN|d }t|d	krN|d
 }|d
krNtd|  dd d|  d|diS |  d}|rt|trd
}d
}| D ] \}	}|rt|d	kr|d }
|d
 }|d
kr|
|kr|
}|}qb|d
krtd|  dd d|  d|diS ddiS )Nr-   r8   rN   Fr1   r	   zSales Rank - Currentcsvr7   r    r{   r4   rI   z%: Using CSV fallback for Sales Rank: r#   
salesRanksz,: Using salesRanks fallback for Sales Rank: )r$   r3   r&   r   r   r   dictitems)r<   r-   valr   historylast_valsales_ranksbest_valmax_tscat_idrT   r   r   r   sales_rank_current  s8   

r   c                 C   $   |  di }dt|ddddi}|S )Nr-   zSales Rank - 30 days avg.avg30rN   Fr   r$   r3   r<   r-   rE   r   r   r   sales_rank_30_days_avg     r   c                 C   sB   |  di }dt|ddddi}td|  dd	 d
|  |S )Nr-   zSales Rank - 90 days avg.r5   rN   Fr   z#Sales Rank - 90 days avg. for ASIN r4   rI   rB   r$   r3   r   r%   r   r   r   r   sales_rank_90_days_avg  s   r   c                 C   r   )Nr-   zSales Rank - 180 days avg.avg180rN   Fr   r   r   r   r   r   sales_rank_180_days_avg  r   r   c                 C   r   )Nr-   zSales Rank - 365 days avg.avg365rN   Fr   r   r   r   r   r   sales_rank_365_days_avg  r   r   c              
   C      |  dd}|  di }| dd}td| d|  |dk r0td	| d
|  ddiS zt|}td| d|  d|iW S  tyg } ztd| dt|  ddiW  Y d }~S d }~ww )Nr4   rI   r-   salesRankDrops30r    z,Sales Rank - Drops last 30 days - raw value=r   r   z0No valid Sales Rank - Drops last 30 days (value=r   zSales Rank - Drops last 30 daysr	   z0Sales Rank - Drops last 30 days result for ASIN rB   z.sales_rank_drops_last_30_days failed for ASIN r$   r   r%   r   r,   r   r   r<   r4   r-   r2   rV   r   r   r   r   sales_rank_drops_last_30_days      
r   c              
   C   r   )Nr4   rI   r-   salesRankDrops180r    z-Sales Rank - Drops last 180 days - raw value=r   r   z1No valid Sales Rank - Drops last 180 days (value=r   z Sales Rank - Drops last 180 daysr	   z1Sales Rank - Drops last 180 days result for ASIN rB   z/sales_rank_drops_last_180_days failed for ASIN r   r   r   r   r   sales_rank_drops_last_180_days  r   r   c              
   C   r   )Nr4   rI   r-   salesRankDrops365r    z-Sales Rank - Drops last 365 days - raw value=r   r   z1No valid Sales Rank - Drops last 365 days (value=r   z Sales Rank - Drops last 365 daysr	   z1Sales Rank - Drops last 365 days result for ASIN rB   z/sales_rank_drops_last_365_days failed for ASIN r   r   r   r   r   sales_rank_drops_last_365_days
  r   r   c                 C   s  |  dd}|  di }| dd}td| d|  |d ur`|dkr`zd	|d
 d}td| d|  d|iW S  ty_ } ztd| d| dt|  W Y d }~nd }~ww td| d| d |  d}|  dd}td| d| d| d |r=|  dg }|std| d| d t|D ]\}	}
|
 d}|
 d}|
 dd}td| d|	 d | d!| d"| d ||kr,||kr,|dkrz d	|d
 d}td| d#| d$| d%|  d|iW   S  ty } ztd| d&| dt|  W Y d }~qd }~ww td| d'| d(| d) qtd| d*| d$| d+ n	td| d, td| d- dd.iS )/Nr4   rI   r-   buyBoxPricer    zBuy Box - Current - ASIN z5 - Attempting to use 'buyBoxPrice' field. Raw value: r   r!   r9   r"   zBuy Box - Current for ASIN z: Using 'buyBoxPrice', value: zBuy Box - Currentz# - Error formatting 'buyBoxPrice' (): z/ - 'buyBoxPrice' is missing, None, or invalid (z). Attempting fallback.buyBoxSellerIdbuyBoxConditionr   z - Fallback: buyBoxSellerId='z', buyBoxCondition=''offersz: - Fallback: No offers array found to search for sellerId .sellerId	conditionpricez - Fallback: Checking offer z: sellerId='z', condition='z
', price='z<: Using Fallback Logic - Found matching offer for sellerId 'z' and condition 'z
'. Price: z+ - Fallback: Error formatting offer price (z0 - Fallback: Matching offer found for sellerId 'z' but price is invalid ().z3 - Fallback: No matching offer found for sellerId 'z' with a positive price.zI - Fallback: 'buyBoxSellerId' is missing. Cannot perform fallback search.zc - Final decision: No valid Buy Box price found through primary or fallback methods. Returning '-'.r	   )	r$   r   r%   r   r   r   r,   r'   	enumerate)r<   r4   r-   buy_box_price_rawformatted_pricer   buy_box_seller_idbuy_box_conditionr   iofferoffer_seller_idoffer_conditionoffer_price_centsr   r   r   buy_box_current  sT   
,


*
", r   c                 C   s   |  dd}|  di }| dd gd d }|d u s|dkr+td|  dd	iS d
|d d}td| d|  d|iS )Nr4   rI   r-   r8      r   z)No valid Amazon - Current price for ASIN zAmazon - Currentr	   r!   r9   r"   z!Amazon - Current result for ASIN rB   rL   r<   r4   r-   r   rV   r   r   r   amazon_currenta  s   r   c                 C   s\  |  dd}|  di }d}td| d | dg }td| d	|  |rt|d
kr|d
 }td| d|  |d urt|ttfr|d
krzd|d d}td| d|  W d|iS  ty } zt	d| d| d| d d}W Y d }~d|iS d }~ww t
d| d| d d}d|iS t
d| d d}d|iS )Nr4   rI   r-   r	   z Amazon - 365 days avg. for ASIN z$: Attempting to use stats.avg365[0].r   r{   z: stats.avg365 raw: r   z : Raw value at stats.avg365[0]: r!   r9   r"   z : Using stats.avg365[0], value: : Error formatting price rB   . Setting to '-'.z/: Invalid or missing price at stats.avg365[0] (). Setting to '-'z8: stats.avg365 array is empty or missing. Setting to '-'zAmazon - 365 days avg.r$   r   r%   r&   r   r   floatr   r   r   r'   )r<   r4   r-   	price_stravg365_arrayprice_centsr   r   r   r   amazon_365_days_avg  s4   r   c              
   C   s  |  dd}|  di }| ddgd }t|dkr|d nd}td| d	| d
t|  d|  |dks>|dkrRtd| dt| d|  ddiS zd|d d}td| d|  d|iW S  ty } ztd| dt	|  ddiW  Y d }~S d }~ww )Nr4   rI   r-   r8   r    r6   r   zNew - Current - raw value=, current array=, stats_keys=r   r   zNo valid New - Current (value=, current_length=r   zNew - Currentr	   r!   r9   r"   zNew - Current result for ASIN rB   znew_current failed for ASIN 
r$   r&   r   r%   r(   keysr'   r   r   r,   )r<   r4   r-   r8   r2   rV   r   r   r   r   new_current  s"   * 
r   c                 C   sF  |  dd}|  di }| dg }d}td| dt|  t|dkr|d }td	| d
|  |d ur}|dkr}zd|d d}td| d|  W d|iS  ty| } ztd| d| d| d d}W Y d }~d|iS d }~ww td| d| d d}d|iS td| dt| d d}d|iS )Nr4   rI   r-   r8   r	   z&New, 3rd Party FBA - Current for ASIN z=: Attempting to use stats.current[10]. current_array length: r   r{   z": Raw value at stats.current[10]: r   r!   r9   r"   z": Using stats.current[10], value: r   rB   r   z1: Invalid or missing price at stats.current[10] (r   (: stats.current array is too short (len z$) to access index 10. Setting to '-'zNew, 3rd Party FBA - Current)r$   r   r%   r&   r   r   r   r'   )r<   r4   r-   current_arrayr   r   r   r   r   r   new_3rd_party_fba_current  s2   r   c              
   C   s  |  dd}d}z|  di }| dg }td| d|  |rt|dkr|d }td| d	|  t|trwt|d
krw|d
 }|d urjt|ttfrj|dkrjd|d d}td| d| d n6t	d| d| d n$t	d| d|  nt	d| dt| d W d|iS W d|iS W d|iS W d|iS  t
y } ztd| dt|  d}W Y d }~d|iS d }~ww )Nr4   rI   r	   r-   minr{   z, - new_3rd_party_fba_lowest: stats.min raw: r   z1 - new_3rd_party_fba_lowest: stats.min[10] pair: r   r   r!   r9   r"   z%New, 3rd Party FBA - Lowest for ASIN z: Found price z from stats.min[10][1]z+: Invalid price value in stats.min[10][1] (r   z%: stats.min[10] is not a valid pair: z3: stats.min array is too short or missing (length: z), cannot access index 10.z3Error processing new_3rd_party_fba_lowest for ASIN rB   zNew, 3rd Party FBA - Lowest)r$   r   r%   r&   r   r(   r   r   r   r'   r   r   r,   )r<   r4   r   r-   min_prices_arrayfba_lowest_pairr   r   r   r   r   new_3rd_party_fba_lowest   s<   
r   c                 C   st  |  dd}|  di }| dg }d}d}td| d|  t|d	kr|d	 }td
| d|  |d urt|ttfr|dkrzd|d d}d}td| d| d|  W nH ty } zt	d| d| d| d d}d}W Y d }~n(d }~ww t
d| d| d d}d}nt
d| dt| d d}d}td| d| d|  d|iS ) Nr4   rI   r-   r8   r	   Nonez&New, 3rd Party FBM - Current for ASIN z5: Attempting to use stats.current[7]. current_array:    r{   z!: Raw value at stats.current[7]: r   r!   r9   r"   zstats.current[7]z: Using z	, value: r   z from stats.current[7]: r   z#stats.current[7] (formatting error)z5: Invalid or non-positive price at stats.current[7] (r   z stats.current[7] (invalid value)r   z#) to access index 7. Setting to '-'zstats.current (too short)z: Final result: z
, Source: zNew, 3rd Party FBM - Currentr   )r<   r4   r-   r   r   sourcer   r   r   r   r   new_3rd_party_fbm_current/  s8    r   c           
      C   s  |  dd}d}z|  di }|std| d ddiW S | dg }td| d	|  d
}t||krm|| durmt|| ttfrm|| dkrm|| }|d }d|d}td| d| d| d |}n"td| d| dt||kr|| nd d|  d}W d|iS W d|iS  t	y   td| d| d|  di  dg   d}Y d|iS  t
y   td| d| d|  di  dg   d}Y d|iS  ty }	 ztd| dt|	  d}W Y d}	~	d|iS d}	~	ww )z
    Retrieves the 365-day average price for new items from 3rd party FBM sellers.
    Corresponds to stats.avg365[7].
    Prices are in cents, converted to dollars. Returns '-' if data is unavailable or invalid.
    r4   rI   r	   r-   r{   z<: 'stats' object missing for new_3rd_party_fbm_365_days_avg.z"New, 3rd Party FBM - 365 days avg.r   z5 - new_3rd_party_fbm_365_days_avg: stats.avg365 raw: r   Nr         Y@r!   r"   z,: New, 3rd Party FBM - 365 days avg. found: z from stats.avg365[]zF: New, 3rd Party FBM - 365 days avg. not available or invalid (avg365[z]). Value: N/Az. avg365 array: z$: IndexError accessing stats.avg365[z7] for New, 3rd Party FBM - 365 days avg. avg365 array: z#: TypeError accessing stats.avg365[z6: Unexpected error in new_3rd_party_fbm_365_days_avg: r$   r   r'   r%   r&   r   r   r   r   r)   r*   r   r   r,   )
r   r4   r   r-   r   fbm_avg_indexprice_in_centsprice_in_dollarsr   r   r   r   r   new_3rd_party_fbm_365_days_avgr  sF   
66,,r  c                 C   s~  |  dd}|  di }td| d d}d}| d}| d	d}|d ur`|d
kr`|du r8|}d| }n(| dd}d| }|d urZ|d
krZ|| }|d| d| 7 }n|}|d7 }|d
krzd|d d}td| d| d|  d|iW S  ty }	 ztd| d| dt|	 d W Y d }	~	nd }	~	ww td| d| d | dg }
t|
dkr!|
d }td| d|  |d ur|d
krzd|d d}td| d | d!|  d|iW S  ty }	 ztd| d"| d#t|	  W Y d }	~	n d }	~	ww td| d$| d% ntd| d&t|
 d' td| d(| d) dd*iS )+Nr4   rI   r-   z Buy Box Used - Current for ASIN z^: Starting process. Relevant stats keys: buyBoxUsedIsFBA, buyBoxUsedPrice, buyBoxUsedShipping.r    zNo valid price foundbuyBoxUsedIsFBAbuyBoxUsedPricer   TzFBA item price: buyBoxUsedShippingzFBM item price: z + shipping: z = z$ (shipping not specified or invalid)r!   r9   r"   z!: Price found via primary logic. z. Formatted: zBuy Box Used - Currentz: Error formatting price (z) from primary logic: z. Will attempt fallback.z5: Primary FBA/FBM logic did not yield a valid price (z,). Attempting fallback to stats.current[32].r8       z.: Fallback check of stats.current[32]. Value: z): Using fallback stats.current[32]. Raw: z, Formatted: z,: Error formatting stats.current[32] value (r   z:: Fallback stats.current[32] is missing, None or invalid (r   z2: Fallback stats.current array is too short (len: z) to access index 32.z4: No valid price found. Initial FBA/FBM price calc: zJ. Fallback stats.current[32] also failed or not applicable. Returning '-'.r	   )	r$   r   r%   r   r   r   r,   r&   r'   )r<   r4   r-   final_price_centsprice_source_infobuy_box_used_is_fbaitem_price_centsshipping_price_centsr   r   r8   value_from_current_32r   r   r   buy_box_used_current  sZ   


.
,r  c                 C   s&   |  di }dt|dddddi}|S )Nr-   zUsed - Currentr8   r7   r9   Tr0   r1   r   r   r   r   r   used_current  s   r  c                 C   sD   |  di }dt|dddddi}td|  d	d
 d|  |S )Nr-   zUsed - 365 days avg.r   r7   r9   Tr  z"used_365_days_avg result for ASIN r4   rI   rB   r   r   r   r   r   used_365_days_avg  s   r  c              	   C   sZ   |  di }|  dd}t|ddddd}d	|i}td
| d| dg  d|  |S )Nr-   r4   rI   r8      r9   Tr  zUsed, like new - Currentzused_like_new for ASIN z: stats.current=z, current_price=r   )r<   r-   r4   current_pricerE   r   r   r   used_like_new  s   $r  c                 C     |  dd}d}d}z|  di }|s!td| d ddiW S | d	g }td| d
|  t||kr|| }td| d| d|  |durt|ttfr|dkrzd|d d}td| d| d|  W nR t	y } zt
d| d| d| d d}W Y d}~n9d}~ww td| d| d| d d}ntd| dt| d| d d}W d|iS W d|iS W d|iS W d|iS  t	y } zt
d| dt|  d}W Y d}~d|iS d}~ww )z
    Retrieves the 365-day average 'Used - Like New' price from product stats.
    Corresponds to stats.avg365[19].
    Prices are in cents, converted to dollars. Returns '-' if data is unavailable or invalid.
    r4   rI   r	   r  r-   r{   z8: 'stats' object missing for used_like_new_365_days_avg.zUsed, like new - 365 days avg.r   z1 - used_like_new_365_days_avg: stats.avg365 raw: : Raw value at stats.avg365[z] for Used, like new: Nr   r!   r9   r"   z(Used, like new - 365 days avg. for ASIN : Using stats.avg365[
], value: r   rB   r   0: Invalid or non-positive price at stats.avg365[] (r   ': stats.avg365 array is too short (len ) to access index . Setting to '-'z2: Unexpected error in used_like_new_365_days_avg: r$   r   r'   r%   r&   r   r   r   r   r   r   r,   r<   r4   r   source_indexr-   r   r   r   r   r   r   used_like_new_365_days_avg-  N   
 "	r  c                 C   H   |  di }|  dd}t|ddddd}td	| d
|  d|iS )Nr-   r4   rI   r8   r6   r9   Tr  z#Used, very good - Current for ASIN z#: Using stats.current[20], result: zUsed, very good - Currentr   r<   r-   r4   r   r   r   r   used_very_goodb  
   r#  c                 C   r  )z
    Retrieves the 365-day average 'Used - Very Good' price from product stats.
    Corresponds to stats.avg365[20].
    Prices are in cents, converted to dollars. Returns '-' if data is unavailable or invalid.
    r4   rI   r	   r6   r-   r{   z9: 'stats' object missing for used_very_good_365_days_avg.zUsed, very good - 365 days avg.r   z2 - used_very_good_365_days_avg: stats.avg365 raw: r  z] for Used, very good: Nr   r!   r9   r"   z)Used, very good - 365 days avg. for ASIN r  r  r   rB   r   r  r  r   r  r  r  z3: Unexpected error in used_very_good_365_days_avg: r  r  r   r   r   used_very_good_365_days_avg|  r   r%  c                 C   r!  )Nr-   r4   rI   r8      r9   Tr  zUsed, good - Current for ASIN z#: Using stats.current[21], result: zUsed, good - Currentr   r"  r   r   r   	used_good  r$  r'  c                 C   r  )z
    Retrieves the 365-day average 'Used - Good' price from product stats.
    Corresponds to stats.avg365[21].
    Prices are in cents, converted to dollars. Returns '-' if data is unavailable or invalid.
    r4   rI   r	   r&  r-   r{   z4: 'stats' object missing for used_good_365_days_avg.zUsed, good - 365 days avg.r   z- - used_good_365_days_avg: stats.avg365 raw: r  z] for Used, good: Nr   r!   r9   r"   z$Used, good - 365 days avg. for ASIN r  r  r   rB   r   r  r  r   r  r  r  z.: Unexpected error in used_good_365_days_avg: r  r   r4   r   r  r-   r   r   r   r   r   r   used_good_365_days_avg  r   r)  c                 C   s   |  dd}|  di }| dd gd d }|d u s|dkr+td|  d	d
iS d|d d}td| d|  d	|iS )Nr4   rI   r-   r8   r      r   z*No valid Used - Acceptable price for ASIN zUsed, acceptable - Currentr	   r!   r9   r"   z+Used, acceptable - Current result for ASIN rB   rL   r   r   r   r   used_acceptable  s   r+  c                 C   r  )z
    Retrieves the 365-day average 'Used - Acceptable' price from product stats.
    Corresponds to stats.avg365[22].
    Prices are in cents, converted to dollars. Returns '-' if data is unavailable or invalid.
    r4   rI   r	   r*  r-   r{   z:: 'stats' object missing for used_acceptable_365_days_avg.z Used, acceptable - 365 days avg.r   z3 - used_acceptable_365_days_avg: stats.avg365 raw: r  z] for Used, acceptable: Nr   r!   r9   r"   z*Used, acceptable - 365 days avg. for ASIN r  r  r   rB   r   r  r  r   r  r  r  z4: Unexpected error in used_acceptable_365_days_avg: r  r(  r   r   r   used_acceptable_365_days_avg
  r   r,  c                 C   s"  |  di }|  dd}| ddgd }t|dkr|d nd}td| d	| d
t|  d| d| 
 |dksA|dkrUtd| dt| d|  ddiS zd|d d}td| d|  d|iW S  ty } ztd| dt	|  ddiW  Y d }~S d }~ww )Nr-   r4   rI   r8   r    r6      z!List Price - Current - raw value=r   r   z, stats_raw=r   r   z%No valid List Price - Current (value=r   r   zList Price - Currentr	   r!   r9   r"   z%List Price - Current result for ASIN rB   zlist_price failed for ASIN r   )r<   r-   r4   r8   r2   rV   r   r   r   r   
list_price8  s"   0 
r.  c              
   C   s   |  dd}z2|  di  dg }|r4t|dkr4|d dur4|d dkr4|d }|d d	}d
|iW S d
diW S  tttfyQ } z
d
diW  Y d}~S d}~ww )z
    Retrieves the 365-day average 'New' price from product stats.
    Formats the price to two decimal places. Returns '-' if data is unavailable.
    r4   rI   r-   r   r   Nr   r   r"   zNew - 365 days avg.r	   )r$   r&   r)   r*   r+   )r<   r4   avg365_pricesr   price_formattedr   r   r   r   new_365_days_avgK  s   (

r1  c                 C   s   |  di }|  dd}td| d | d}| d}td| d| d	|  |d u rB|d u rBtd| d
 ddiS t|trM|dkrM|nd}t|trZ|dkrZ|nd}|| }td| d| d| d| d	 dt|iS )Nr-   r4   rI   r{   z(: Calculating New Offer Count - Current.offerCountFBAofferCountFBMz: offerCountFBA = z, offerCountFBM = z`: Both offerCountFBA and offerCountFBM are missing. Returning '-' for New Offer Count - Current.zNew Offer Count - Currentr	   r   z*: New Offer Count - Current calculated as z (FBA: z, FBM: r   )r$   r   r%   r'   r   r   r   r,   )r<   r-   r4   offer_count_fbaoffer_count_fbmval_fbaval_fbmtotal_new_offersr   r   r   new_offer_count_currentq  s   

$r9  c                 C   F   |  di }|  dd}t|dddd}td| d	|  d
|iS )Nr-   r4   rI   r      Fr   r{   z9: New Offer Count - 365 days avg. from stats.avg365[11]: zNew Offer Count - 365 days avg.r$   r3   r   r   r<   r-   r4   countr   r   r   new_offer_count_365_days_avg  
   r?  c           
      C   sL  |  di }|  dd}td| d | d}| d}| d}td| d	| d
| d|  |d u sBt|trB|dk rRtd| d| d ddiS t|tr]|dkr]|nd}t|trj|dkrj|nd}|| }||k rtd| d| d| d ddiS || }	td| d|	 d| d| d| d dt|	iS )Nr-   r4   rI   r{   z): Calculating Used Offer Count - Current.r2  r3  totalOfferCountz: totalOfferCount = z, offerCountFBA_new = z, offerCountFBM_new = r   z): totalOfferCount is missing or invalid (z0). Returning '-' for Used Offer Count - Current.zUsed Offer Count - Currentr	   z: totalOfferCount (z&) is less than calculated new offers (zW). This might indicate inconsistent data. Returning '-' for Used Offer Count - Current.z+: Used Offer Count - Current calculated as z	 (Total: z, New FBA: z, New FBM: r   )r$   r   r%   r   r   r'   r   r,   )
r<   r-   r4   offer_count_fba_newoffer_count_fbm_newtotal_offer_countval_fba_newval_fbm_newcurrent_new_totalcalculated_used_offersr   r   r   used_offer_count_current  s&   


"*rI  c                 C   r:  )Nr-   r4   rI   r      Fr   r{   z8: Used Offer Count - 30 days avg. from stats.avg30[12]: zUsed Offer Count - 30 days avg.r<  r=  r   r   r   used_offer_count_30_days_avg  r@  rK  c                 C   r:  )Nr-   r4   rI   r   rJ  Fr   r{   z:: Used Offer Count - 180 days avg. from stats.avg180[12]: z Used Offer Count - 180 days avg.r<  r=  r   r   r   used_offer_count_180_days_avg  r@  rL  c                 C   r:  )Nr-   r4   rI   r   rJ  Fr   r{   z:: Used Offer Count - 365 days avg. from stats.avg365[12]: z Used Offer Count - 365 days avg.r<  r=  r   r   r   used_offer_count_365_days_avg  r@  rM  c              
   C   s   |  dd}z;|  di }|sddiW S | dg }d}t||kr=|| dur=|| d	kr=|| }|d
 }d|diW S ddiW S  tyN   ddi Y S  tyZ   ddi Y S  tyo } z
ddiW  Y d}~S d}~ww )zg
    Retrieves the 365-day average Buy Box price.
    The Buy Box price usually includes shipping.
    r4   rI   r-   zBuy Box - 365 days avg.r	   r      Nr   r   r"   )r$   r&   r)   r*   r   )r<   r4   r-   r   buy_box_avg_indexr   r   r   r   r   r   buy_box_365_days_avg  s(   
$
rP  c           	      C   s  |  dd}zs|  di }|std| d ddiW S | dg }td| d	|  d
}t||krg|| durgt|| ttfrg|| dkrg|| }|d }|d}td| d|  d|iW S td| d| d|  ddiW S  t	y   td| d|  di  dg   ddi Y S  t
y   td| d|  di  dg   ddi Y S  ty } ztd| dt|  ddiW  Y d}~S d}~ww )z
    Retrieves the average price of new 3rd party FBA offers over the last 365 days.
    Corresponds to stats.avg365[10].
    Prices are in cents, converted to dollars. Returns '-' if data is unavailable or invalid.
    r4   rI   r-   r{   z<: 'stats' object missing for new_3rd_party_fba_365_days_avg.z"New, 3rd Party FBA - 365 days avg.r	   r   z5 - new_3rd_party_fba_365_days_avg: stats.avg365 raw: r   Nr   r   r"   z-: New, 3rd Party FBA - 365 days avg. found: $zF: New, 3rd Party FBA - 365 days avg. not available or invalid (avg365[z]). avg365 array: zS: IndexError accessing avg365 for New, 3rd Party FBA - 365 days avg. avg365 array: zR: TypeError accessing avg365 for New, 3rd Party FBA - 365 days avg. avg365 array: z6: Unexpected error in new_3rd_party_fba_365_days_avg: r   )	r   r4   r-   r   fba_avg_indexr   r   r   r   r   r   r   new_3rd_party_fba_365_days_avg   s8   
6

&&rR  c                 C   r  )z
    Retrieves the 365-day average "Buy Box Used" price from product stats.
    Assumes index 32 in stats.avg365 corresponds to this value.
    Prices are in cents, converted to dollars. Returns '-' if data is unavailable or invalid.
    r4   rI   r	   r  r-   r{   z7: 'stats' object missing for buy_box_used_365_days_avg.zBuy Box Used - 365 days avg.r   z0 - buy_box_used_365_days_avg: stats.avg365 raw: r  z]: Nr   r!   r9   r"   z&Buy Box Used - 365 days avg. for ASIN r  r  r   rB   r   r  r  r   r  r  r  z1: Unexpected error in buy_box_used_365_days_avg: r  r(  r   r   r   buy_box_used_365_days_avg*  r   rS  c                 C   s*  |  dd}|  d}td| d|  t|ts+td| d| d dd	iS | d
}|du s?t|ttfr?|dk rOtd| d| d dd	iS z|d }d|d}td| d| d| d d|iW S  t	y } zt
d| d| dt| d dd	iW  Y d}~S d}~ww )z
    Retrieves the FBA Pick & Pack fee from product data.
    The fee is expected to be in cents and is converted to a dollar string.
    Returns '-' if the fee is not available or invalid.
    r4   rI   fbaFeesr{   z;: Attempting to get FBA Pick&Pack Fee. Raw 'fbaFees' dict: z6: 'fbaFees' is not a dictionary or is missing. Value: . Returning '-'.zFBA Pick&Pack Feer	   pickAndPackFeeNr   z>: 'pickAndPackFee' is missing from fbaFees, None, or invalid (). Returning '-'.r   r!   r"   z: FBA Pick&Pack Fee found:  (from raw )z&: Error formatting FBA Pick&Pack Fee (r   )r$   r   r%   r   r   r'   r   r   r   r   r   r,   )r   r4   fba_fees_data	fee_centsfee_dollarsformatted_feer   r   r   r   get_fba_pick_pack_feeW  s(   



"r^  c                 C   sB  |  dd}td| d d}d}d| v r*|  d}d}td| d	|  nyd
| v rA|  d
}d}td| d|  nbd| v rt| d tr| d }d|v rg| d}d}td| d|  n<d
|v r~| d
}d}td| d|  n%d|v rt|d trd|d v r|d  d}d}td| d|  |durt|ttfr|dkrzt|dd}td| d| d| d| d	 d|iW S  ty } zt	d| d| d| d t
| d!	 dd"iW  Y d}~S d}~ww |du rtd| d# dd"iS td| d| d$| d% dd"iS )&a  
    Retrieves the Referral Fee Percentage from product data.
    This is speculative as the exact field is not confirmed in documentation.
    It might be in product_data.referralFeePercent or product_data.fbaFees.referralFeePercent.
    Returns '-' if not found or invalid.
    r4   rI   r{   z,: Attempting to get Referral Fee Percentage.Nz	Not foundreferralFeePercentagez"product_data.referralFeePercentagez0: Found precise referralFeePercentage directly: referralFeePercentzproduct_data.referralFeePercentz/: Found potential referralFeePercent directly: rT  z*product_data.fbaFees.referralFeePercentagez5: Found precise referralFeePercentage under fbaFees: z'product_data.fbaFees.referralFeePercentz4: Found potential referralFeePercent under fbaFees: referralFeer@   z(product_data.fbaFees.referralFee.percentzH: Found potential referralFeePercent under fbaFees.referralFee.percent: r   r"   r;   z!: Referral Fee Percentage found (r   rX  rY  zReferral Fee %z,: Error formatting Referral Fee Percentage (z) from rB   rU  r	   zG: Referral Fee Percentage not found with any known keys. Returning '-'.z) but value is invalid (rW  )r$   r   r%   r   r   r   r   r   r   r   r,   r'   )r   r4   referral_fee_valuer   fba_fees_dictr]  r   r   r   r   get_referral_fee_percentw  sT   



$
(
rd  c                 C   s   |  dd}td| d |  dg }|r:|D ] }| ddkr9| dd	d
kr9td| d ddi  S q|  di  dd	d
krStd| d ddiS td| d ddiS )zn
    Checks if shipping is included in the price for the Used - Current offer.
    Returns 'yes' or 'no'.
    r4   rI   r{   z9: Checking for shipping included in Used - Current offer.r   r   UsedshippingCostr    r   zB: Found a Used offer with shippingCost of 0, shipping is included.zShipping Includedyesr-   r  z9: Found buyBoxUsedShipping to be 0, shipping is included.z@: No indication of free shipping found for Used - Current offer.no)r$   r   r%   r   )r   r4   r   r   r   r   r   get_shipping_included  s   ri  c                 C   sd  |  dd}tt}|  d}|rt|ts$|d| d ddiS h d}g }|D ]-}t|ts4q,| d	}| d
}|du sF|du rGq,||v rYt|trY|dkrY|	| q,|si|
d| d ddiS t|}	zd|	d d}
|
d| d|
 dt| d d|
iW S  ty } z|d| d|	 dt|  ddiW  Y d}~S d}~ww )z
    Calculates the target buy price by finding the lowest current offer price for a book
    in 'Good' or better condition (Like New, Very Good, Good).
    r4   rI   r   r{   zM: 'offers' array is missing or not a list. Cannot calculate Target Buy Price.Target Buy Pricer	   >   r7   rN   r-  r   r   Nr   z[: No current offers found in 'Good' or better condition. Cannot determine Target Buy Price.r!   r9   r"   z!: Target Buy Price calculated as z from z eligible offer(s).z%: Error formatting Target Buy Price (r   )r$   logging	getLogger__name__r   r(   r'   r   r   appendr   r   r&   r   r   r,   )r<   r4   r   r   good_or_better_conditionseligible_offersr   r   r   min_price_centsr   r   r   r   r   target_buy_price  s>   





"
 rr  c              
   C   s6  |  dd}tt}|  d}|r*t|tr*|dvr*|d| d|  d|iS |d| d| d	 |  d
g }|rCt|tsP|d| d ddiS t	t
|d ddD ]*}|| }t|tr|dvr||d  }|d| d| d| d d|i  S qZ|d| d|r|d nd d ddiS )z
    Extracts the Buy Box Seller ID from the product data by checking the direct
    'buyBoxSellerId' field first, then falling back to the 'buyBoxSellerIdHistory'.
    r4   rI   r   )z-1z-2r{   z@: Found Buy Box Seller ID directly from 'buyBoxSellerId' field: zBuy Box Seller IDz1: Direct 'buyBoxSellerId' not found or invalid ('z%'). Checking 'buyBoxSellerIdHistory'.buyBoxSellerIdHistoryzN: 'buyBoxSellerIdHistory' not found or is not a list. Cannot determine seller.r	   r   r   r   z3: Found last valid Buy Box Seller ID from history: z (at timestamp rY  zC: Could not find a valid seller ID in the history. Last entry was 'r    r   r   )r$   rk  rl  rm  r   r,   r   r(   r'   ranger&   )r<   r4   r   	seller_idr   r   potential_seller_id	timestampr   r   r   get_buy_box_seller_id  s(   

$rx  c           
      C   s  ddl m} tt}| dd}t| dd}|| dd}|dks)|dkr-ddiS z*t|d	d
}t|d	d
}|dkrGddiW S || | d }d|ddiW S  t	t
fy }	 z|d| d| d| d|	  ddiW  Y d}	~	S d}	~	ww )z
    Calculates the profit margin based on Target Buy Price and Expected Peak Sell Price.
    This function depends on other functions for its inputs.
    r   )get_expected_peak_pricer4   rI   rj  r	   zExpected Peak PricezProfit Margin %r!   rk   r   r9   r:   r;   r{   z+: Could not calculate profit margin. Buy: 'z
', Sell: 'z
'. Error: N)stable_calculationsry  rk  rl  rm  r$   rr  r   r   r   r*   r   )
r<   ry  r   r4   buy_price_strsell_price_str	buy_price
sell_pricemarginr   r   r   r   profit_margin_percent&  s&   

"r  c                 C   s  |  dd}|  di }d}td| d | dg }|ryt|dkry|d }|d	urit|ttfri|dkrizd
|d d}W d|iS  tyh } ztd| d| d|  W Y d	}~d|iS d	}~ww t	d| d| d d|iS t	d| d d|iS )z_
    Retrieves the 180-day average Amazon (New) price.
    Corresponds to stats.avg180[0].
    r4   rI   r-   r	   z Amazon - 180 days avg. for ASIN z$: Attempting to use stats.avg180[0].r   r   Nr!   r9   r"   r   rB   z/: Invalid or missing price at stats.avg180[0] (r   z): stats.avg180 array is empty or missing.zAmazon - 180 days avg.)
r$   r   r%   r&   r   r   r   r   r   r'   )r<   r4   r-   r   avg180_arrayr   r   r   r   r   amazon_180_days_avgI  s(   &r  )r   )r   F)Trequestsrk  rl  rm  r   retryingr   r   r   pytzr   stable_dealsr   jsonr   rR   r   r   API_HEADERSr3   rA   rF   rG   rM   rW   r\   re   rf   rh   rj   rt   ru   rx   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r  r  r#  r%  r'  r)  r+  r,  r.  r1  r9  r?  rI  rK  rL  rM  rP  rR  rS  r^  rd  ri  rr  rx  r  r  r   r   r   r   <module>   s   


"


T


,	C

(&>/C?G5.4.&
$


&*- =3%#