
    HE^j3                     r   d dl Z d dlZd dlmZmZmZmZ d dlmZ d dl	m
Z
 d dlmZ d dlmZ 	 d dlmZmZ dZd"d
ededededededefdZd
eddfdZdee   ddfdZdedee   fdZdee   fdZdee   deeef   fdZdeddfdZdeddfdZ dede!de!ddfdZ"dee   d eddfd!Z#y# e$ r d	ZY }w xY w)#    N)OptionalListDictAny)settings)_log)get_db_connection)get_product_cpu_gpu)webpushWebPushExceptionTFsubscription_idendpointp256dhauthpayloadupdate_last_usedreturnc           
      &   t         j                  rt        sy|||dd}	 t        |t	        j
                  |      t         j                  dt         j                  idt        t         j                        dd       |rt        | g       y	# t        $ rY}t        d
|  d|        |j                  2|j                  j                  dv rt        d|  d       t        |        Y d}~yd}~wt         $ r}t        d|  d|        Y d}~yd}~ww xY w)a  Send web push notification using pywebpush.

    update_last_used=False lets a caller that's about to send many of these in
    one dispatch run (notify_new_listing/back_in_stock/price_drop/notify_batch,
    all loop over every matching subscription) skip the connection-per-send
    churn here and batch a single UPDATE ... WHERE id IN (...) via
    _bump_last_used() after the loop instead (fable audit B-NOT-1). The one-off
    /api/push/test caller keeps the default True.F)r   r   )r   keyssubhigh)UrgencyTTL
   )subscription_infodatavapid_private_keyvapid_claimsheaderstimeoutTz [Push] WebPushException for sub : N)i  i  z[Push] Subscription z is dead/expired. Pruning...z,[Push] Unexpected error sending push to sub )r   PUSH_ENABLEDHAS_PYWEBPUSHr   jsondumpsVAPID_PRIVATE_KEYVAPID_SUBJECTstrPUSH_TTL_SECONDS_bump_last_usedr   r   responsestatus_code_prune_subscription	Exception)	r   r   r   r   r   r   r   exes	            (/home/container/backend/notifier/push.py	send_pushr2      s       
/G$&88x-- "8445 	
 _-. //@2$GH;;"r{{'>'>*'L''88TUV0 ;O;LBqcRSs%   A*B 	DAC))D5DDc                 4   t               }	 |j                         }|j                  d| f       |j                          t	        d|         |j                          y # t
        $ r}t	        d|  d|        Y d }~0d }~ww xY w# |j                          w xY w)Nz+DELETE FROM push_subscriptions WHERE id = ?z[Push] Deleted subscription z%[Push] Failed to delete subscription r!   )r	   cursorexecutecommitr   r.   close)r   connr4   r0   s       r1   r-   r-   A   s    DDFXY+O+<=> 	

  M4_4ERsKLLM 	

s*   AA 	B'A=8B =BB Bsubscription_idsc                    | syt               }	 |j                         }t        j                  d      }dj	                  d | D              }|j                  d| d|g|        |j                          |j                          y# t        $ r$}t        dt        |        d|        Y d}~9d}~ww xY w# |j                          w xY w)	zSingle batched UPDATE for a whole dispatch run's worth of successful
    sends, instead of one connection-open+UPDATE+commit per send (fable audit
    B-NOT-1).Nz%Y-%m-%d %H:%M:%S,c              3       K   | ]  }d   yw?N .0_s     r1   	<genexpr>z"_bump_last_used.<locals>.<genexpr>W        >-=-=   z9UPDATE push_subscriptions SET last_used = ? WHERE id IN ()z#[Push] Error bumping last_used for  subscription(s): )r	   r4   timestrftimejoinr5   r6   r.   r   lenr7   )r9   r8   r4   now_strplaceholdersr0   s         r1   r*   r*   M   s     D-- 34xx>-=>>G~UVW('(	
 	 	

  a237G3H2II[\][^_``a 	

s*   A&B 	B3B.)B6 .B33B6 6Cproduct_codec                 p   t               }	 |j                         }|j                  d| f       |j                         }|D cg c]  }t	        |       c}|j                          S c c}w # t        $ r*}t        d|        g cY d }~|j                          S d }~ww xY w# |j                          w xY w)Na  
            SELECT s.id, s.endpoint, s.p256dh, s.auth, 
                   s.notify_price_drops, s.notify_new_listings, s.notify_back_in_stock,
                   s.notify_watchlist_only, s.min_drop_percent,
                   (SELECT 1 FROM subscription_watchlist w 
                    WHERE w.subscription_id = s.id AND w.product_code = ?) as is_in_watchlist
            FROM push_subscriptions s
        z%[Push] Error querying subscriptions: r	   r4   r5   fetchalldictr7   r.   r   )rN   r8   r4   rowsrr0   s         r1   _get_matching_subscriptionsrU   b   s    D  _	  !%&AQ&
 	

 ' 4QC89	

	 	

sA   7A- A(A- (A- -	B 6BB B# B  B# #B5c                  l   t               } 	 | j                         }|j                  d       |j                         }|D cg c]  }t	        |       c}| j                          S c c}w # t        $ r*}t        d|        g cY d }~| j                          S d }~ww xY w# | j                          w xY w)Nz
            SELECT s.id, s.endpoint, s.p256dh, s.auth, 
                   s.notify_price_drops, s.notify_new_listings, s.notify_back_in_stock,
                   s.notify_watchlist_only, s.min_drop_percent
            FROM push_subscriptions s
        z)[Push] Error querying all subscriptions: rP   )r8   r4   rS   rT   r0   s        r1   _get_all_subscriptionsrW   v   s    D  	  !%&AQ&
 	

 ' 8<=	

	 	

sA   5A+ A&A+ &A+ +	B4BBB! BB! !B3c                    | si S t               }	 |j                         }dj                  d | D              }|j                  d| dt	        |              i }|j                         D ].  \  }}|j                  |t                     j                  |       0 ||j                          S # t        $ r6}t        dt        |        d|        i cY d}~|j                          S d}~ww xY w# |j                          w xY w)u   One query for every subscription's watchlist, grouped in Python — replaces
    notify_batch's previous per-subscription query inside its dispatch loop
    (fable audit B-NOT-1's N+1).r;   c              3       K   | ]  }d   ywr=   r?   r@   s     r1   rC   z+_get_watchlists_for_subs.<locals>.<genexpr>   rD   rE   z[SELECT subscription_id, product_code FROM subscription_watchlist WHERE subscription_id IN (rF   z%[Push] Error querying watchlists for rG   N)r	   r4   rJ   r5   listrQ   
setdefaultsetaddr7   r.   r   rK   )r9   r8   r4   rM   resultsub_idrN   r0   s           r1   _get_watchlists_for_subsr`      s     	Dxx>-=>>ijviwwxy!"	
 "$$*OO$5 FLfce,00> %6
 	

	  4S9I5J4KK]^_]`ab	

	 	

s0   BB, ,	C+5C&C+C. &C++C. .D productc                    t         j                  sy | j                  dd      }| j                  dd      }| j                  dd      }| j                  dd      }t        |       \  }}d	d
| d| d| d| d| 	|||d}t	        |      }g }	|D ]K  }
|
d   dk(  r|
d   s|
d   dk(  rt        |
d   |
d   |
d   |
d   |d      s8|	j                  |
d          M t        |	       y )NproductCoder>   productNameUnknown Product
finalPriceN/AsavePercentr   new_listingNew Product Listed is now available for  INR (-
%) | CPU:  | GPU: 
event_typetitlebodyrN   pricesave_percentnotify_watchlist_only   is_in_watchlistnotify_new_listingsidr   r   r   Fr   r   r"   getr
   rU   r2   appendr*   )ra   codenamers   savingcpugpur   subssent_idsr   s              r1   notify_new_listingr      s     ;;}c*D;;}&78DKKe,E[[*F"7+HC#%&.ugWVHJseS[\_[`aG 't,DH&'1,S9J5K$%*SYJXFWglmOOCI&  H    c                    t         j                  sy | j                  dd      }| j                  dd      }| j                  dd      }| j                  dd      }| j                  d	      }|rd
| dnd}t        |       \  }}dd| d| d| d| d| d| |||d}	t	        |      }
g }|
D ]K  }|d   dk(  r|d   s|d   dk(  rt        |d   |d   |d   |d   |	d      s8|j                  |d          M t        |       y )Nrc   r>   rd   re   rf   rg   rh   r   _restock_duration (Restocked after rF    back_in_stockProduct Back in Stock is back! Price: rl   %) | CPU: rn   ro   ru   rv   rw   notify_back_in_stockry   r   r   r   Frz   r{   )ra   r~   r   rs   r   restock_durbody_suffixr   r   r   r   r   r   s                r1   r   r      sC     ;;}c*D;;}&78DKKe,E[[*F++12K9D&{m15"K"7+HC%(&)%xr+hWZV[[cdgchiG 't,DH&'1,S9J5K%&!+SYJXFWglmOOCI&  Hr   	old_price	new_pricec                    t         j                  sy | j                  dd      }| j                  dd      }| j                  dd      }d}|dkD  rt        ||z
  |z  dz  d	      }t	        |       \  }}d
d| d| d| d| d| 	|||||d}	t        |      }
g }|
D ]X  }|d   dk(  r|d   s|d   dk(  r||d   xs dk  r't        |d   |d   |d   |d   |	d      sE|j                  |d          Z t        |       y )Nrc   r>   rd   re   rh   r   g        d      
price_dropzPrice Drop Alertz dropped to z
 INR (Was z INR) | CPU: rn   )rp   rq   rr   rN   rs   r   rt   drop_percentru   rv   rw   notify_price_dropsmin_drop_percentry   r   r   r   Frz   )	r   r"   r|   roundr
   rU   r2   r}   r*   )ra   r   r   r~   r   r   r   r   r   r   r   r   r   s                r1   notify_price_dropr      sO     ;;}c*D;;}&78D[[*F L1}i)3y@3FJ"7+HC"#&YKz)MRUQVV^_b^cd$	G 't,DH&'1,S9J5K#$)3129c:SYJXFWglmOOCI&  Hr   productsrp   c                 H   t         j                  r| sy t        |       }t               }|D cg c]  }|d   dk(  s|d    }}t	        |      }g }|D ]  }|dk(  r	|d   dk(  r|dk(  r	|d   dk(  r | }|d   dk(  rC|j                  |d   t                     }	| D 
cg c]  }
|
j                  d	      |	v s|
 }}
|smt        |      }|d
k  r|D ]  }
|
j                  d	d      }|
j                  dd      }|
j                  dd      }|
j                  dd      }t        |
      \  }}|dk(  rdd| d| d| d| d| 	|||d}n5|
j                  d      }|rd| dnd}dd| d| d| d| d| d| |||d}t        |d   |d   |d    |d!   |d"#      s|j                  |d           `|dk(  r| d$}| d%}n
| d&}| d%}| d'||d|d(}t        |d   |d   |d    |d!   |d"#      s|j                  |d           t        |       y c c}w c c}
w ))Nru   rv   ry   ri   rx   r   r   r   rc      r>   rd   re   rf   rg   rh   rj   rk   rl   rm   rn   ro   r   r   rF   r   r   r   r   r   r   r   r   Frz   z Products Listedz products added to stockz Products Back in Stock_batch)rp   rq   rr   rN   count)r   r"   rK   rW   r`   r|   r\   r
   r2   r}   r*   )r   rp   r   r   r   watchlist_sub_ids
watchlistsr   matching_products	watchlistpmatch_countr~   r   rs   r   r   r   r   r   r   rq   rr   s                          r1   notify_batchr   	  s     ME!#D /3Xdsc:Q6RVW6WTdX)*;<JH&3/D+E+J(S1G-HA-M %&'1,"s4y#%8I,4 ZHqm8LPY8YH Z$+,?&uu]C0uu],=>lE2}a0.q1S.&3!5#'&(>ugWVHT^_b^ccklokp q(,!&(.G #$%%(;"<KIT$6{m1"EZ\K&5!8#'&(9%xrR]Q^^fgjfkkstwsx y(,!&(.G SYJXFU\otuOOCI.7 '< ]*&-'78%&>?&-'>?%&>? ",F3 "$G TC
OS]CKQXkpqD	*} @ HI Y ![s   HHH7H)T)$r$   rH   typingr   r   r   r   backend.configr   backend.logging_configr   backend.db.connectionr	   backend.scraper.enrichmentr
   	pywebpushr   r   r#   ImportErrorintr(   rR   boolr2   r-   r*   rU   rW   r\   r`   r   r   floatr   r   r?   r   r1   <module>r      sZ     , , # ' 3 :3M/s /c /3 /c /TX /lp /  }A /b
 
 
d3i D *c d4j (T
 $tCy T#s(^ 2  > $  4  D%t % %% %D %NL4: L3 L4 Ly  Ms   
B, ,B65B6