
    =Gj2                         d Z ddlZddlZddlmZmZmZ  ej                  e      Z e ed            Z	 G d d      Z
y)u  
AdsAttributionService — atribui gasto de Product Ads a vendas individuais.

Por que este modelo: o ML entrega gasto agregado por item x dia (nunca por
pedido). A atribuição em camadas é a aproximação mais honesta possível:

  Nível 1 — custo do item NO DIA da venda ÷ unidades do item vendidas no dia.
  Nível 2 — custo do item em dias SEM venda no período ÷ unidades do item
            vendidas no período (residual; garante que o custo não evapora).
  Órfão   — custo de itens com gasto e ZERO vendas no período. Não pertence a
            nenhuma venda; é devolvido separado (orphan_cost) para que o lucro
            total do período continue verdadeiro.

Fonte primária: tabela ml_ads_item_daily (populada pelo sync diário/backfill).
Se o período inclui HOJE (dia ainda sem sync), faz top-up via API live com o
mesmo cache de 15 min que o dashboard já usava.
    N)datetime	timedeltatimezone)hoursc                   "    e Zd Zd Z	 ddZd Zy)AdsAttributionServicec                     || _         y )N)db)selfr   s     J/var/www/hypershopcomercio.com.br/hyper-ai/app/services/ads_attribution.py__init__zAdsAttributionService.__init__   s	        c                    ddl m} | j                  j                  |      j	                  |j
                  |k\  |j
                  |k        j                         }i }d}	d}
t               }|D ]  }t        |j                  xs d      }|j                  |j                  |j
                  fd      |z   ||j                  |j
                  f<   |	|z  }	|
t        |j                  xs d      z  }
|j                  |j
                          t        j                  t               j                         }|r||cxk  r|k  rn n||vr| j#                  |      D ]  }|j                  d      }|st        |j                  d      xs d      }t        |j                  d      xs d      }|dk  r|dk  r^|dkD  r!|j                  ||fd      |z   |||f<   |	|z  }	|
|z  }
|j                  |        |syi }i }|j%                         D ]D  \  \  }}}|j                  ||fd      }|dkD  r||z  |||f<   -|j                  |d      |z   ||<   F i }d}|j%                         D ]*  \  }}|j                  |d      }|dkD  r	||z  ||<   &||z  }, ||t'        |d      t'        |	d      t'        |
d      t)        |      d	S )
u  
        start_date / end_date: datetime.date (dias no fuso BR, inclusivos)
        item_day_sold_qty: {(item_id, date): qty} vendas válidas por item x dia
        item_period_sold_qty: {item_id: qty} vendas válidas por item no período

        Retorna None quando não há NENHUM dado de Ads no período (caller decide
        fallback), ou um dict com os mapas de atribuição.
        r   )MlAdsItemDailyg        item_idcostamountN   )per_unit_dayper_unit_fallbackorphan_cost
total_costtotal_ads_revenuedays_with_data)app.models.ml_ads_item_dailyr   r   queryfilterdateallsetfloatr   getr   revenueaddr   nowTZ_BR_fetch_today_liveitemsroundlen)r   
start_dateend_dateitem_day_sold_qtyitem_period_sold_qtytop_up_today_liver   rowsitem_day_costr   r   dates_with_datarr   today_brr   r   r   residual_item_costdqty_dayr   r   
qty_periods                           r   buildzAdsAttributionService.build    s    	@ww}}^,33:-8+
 #% 	
 
% 	(A1%D1>1B1BAIIqvvCVX[1\_c1cM199aff-.$Jqyy~A!66'	( <<&++-x!C8!CXgHg++H5 .%%	*QUU6]/a0quuX3!4191!89F9J9JGU]K^`c9dgk9kM7H"56$&J!V+!##H-.  "/"5"5"7 	ZLWa$'++WaL!<G{-1G^gq\*.@.D.DWc.RUY.Y"7+	Z /557 	$MGT-11'1=JA~-1J->!'*t#	$ )!2 a0
A.!&'8!!<!/2
 	
r   c                    	 ddl m} ddlm} |j	                  d      }d| d| }| j
                  j                  |      j                  |      j                         }|rw|j                  rk	 t        j                  |j                        }t        j                         j                         |j                  dd      z
  d	k  r|j                  d
g       S 	  || j
                        }|j#                  d||d      }	|	r~|s% ||d      }| j
                  j%                  |       t        j&                  t        j                         j                         |	d      |_	        | j
                  j)                          |	xs g S # t         $ r Y w xY w# t         $ r$}
t*        j-                  d|
        g cY d}
~
S d}
~
ww xY w)z@Busca o gasto de hoje na API (cache de 15 min em system_config).r   )SystemConfig)MeliApiServicez%Y-%m-%d
ads_cache__)key	timestampi  dataNT)fastcache)r@   group)rA   rB   z*Ads today live top-up failed (non-fatal): )app.models.system_configr<   app.services.meli_apir=   strftimer   r   	filter_byfirstvaluejsonloadsr   r&   rA   r#   	Exceptionget_ads_performancer%   dumpscommitloggerwarning)r   r5   r<   r=   d_str	cache_keysc
cached_objmelirB   es              r   r(   z'AdsAttributionService._fetch_today_liven   s}   	=<%%j1E$UG1UG4I|,6696EKKMBbhh!%BHH!5J 002Z^^KQR5SSWZZ)~~fb99 [
 "$''*D++D%T+JD%)7CBGGKKO::HLLN4L4L4NX\&]^ :2 !   	NNGsKLI	sD   A,F /A(F B-F 	FF FF 	GF=7G=GN)T)__name__
__module____qualname__r   r:   r(    r   r   r	   r	      s     !%L
\r   r	   )__doc__rL   loggingr   r   r   	getLoggerrZ   rR   r'   r	   r]   r   r   <module>ra      sE   "   2 2			8	$$%p pr   