Ë
    ÿËÇjœ.  ã            	      ó”  — d Z ddlmZ ddlmZ ddlmZ ddlmZ ddlZ	 ddl	m
Z
mZmZ  ed«      Z ed«      Z edddddde¬«      j!                  e«      Z edddddde¬«      j!                  e«      ZdZdZdZ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d&d!„Z d&d"„Z!d-d.d#„Z"	 d-	 	 	 d/d$„Z#y# e$ r	 dZ
d	Zd
ZY Œœw xY w)0aÉ  
Modulo "Leads de Trafego - Setembro/2026".

Filtra contatos GHL de setembro/2026 que vieram de trafego pago
(Meta/Google/paid/ads via utm_source, source nativo, attributions ou tags).

Expoe duas funcoes publicas:
  - filtrar_leads(contatos) -> list[dict]  (leads normalizados prontos pra UI)
  - buscar_observacao(contact_id, headers) -> dict  (ultima nota [VENDEDORA...])
  - salvar_observacao(contact_id, texto, user, headers) -> dict  (cria nota nova)
é    )Úannotations)Údatetime)ÚZoneInfo)ÚIterableN)ÚUTM_FIELD_SOURCEÚUTM_FIELD_CAMPAIGNÚUTM_FIELD_MEDIUMÚysHWPzyR0uZ2nd8D9sbiÚH16g1x7tRA8OoHnqiTdpÚHiabk9sp0zIkgLjUYJjRzAmerica/Sao_PauloÚUTCiê  é	   é   )Útzinfoé   é   é;   )ÚfacebookÚ	instagramÚigÚfbÚmetaÚpaidÚads)Útrafegozmeta-adszfacebook-adszinstagram-adsÚpagoz
[VENDEDORAz$https://services.leadconnectorhq.comc                ór   — | sy 	 t        j                  | j                  dd«      «      S # t        $ r Y y w xY w)NÚZz+00:00)r   ÚfromisoformatÚreplaceÚ	Exception©Úvs    ú=/opt/mia/workspace/clientes/px3lab/dashboard/leads_trafego.pyÚ	_parse_dtr%   7   s;   € ÙØðÜ×%Ñ% a§i¡i°°XÓ&>Ó?Ð?øÜò Ùðús   …$* ª	6µ6c                óN   — t        | t        «      r| xs dj                  «       S dS )NÚ )Ú
isinstanceÚstrÚlowerr"   s    r$   Ú_lowerr+   @   s!   € Ü *¨1¬cÔ 2ˆAŠG��?‰?ÓÐ:¸Ð:ó    c                óŽ   — i }| j                  d«      xs g D ]*  }|j                  d«      }|sŒ|j                  d«      ||<   Œ, |S )NÚcustomFieldsÚidÚvalue©Úget)ÚcÚoutÚcfÚcids       r$   Ú_custom_fields_mapr7   D   sL   € Ø
€CØ�e‰e�NÓ#Ò) rò 'ˆØ�f‰f�T‹lˆÚØ—v‘v˜g“ˆC�ŠHð'ð €Jr,   c                óx   — | sydj                  d„ t        | «      D «       «      }|syt        |«      dv rd|z   }|S )zFSo digitos; prefixa 55 se nao tiver codigo pais e tiver 10-11 digitos.r'   c              3  óB   K  — | ]  }|j                  «       sŒ|–— Œ y ­w©N)Úisdigit)Ú.0Úchs     r$   ú	<genexpr>z"_sanitize_phone.<locals>.<genexpr>Q   s   è ø€ Ò;˜B¨b¯j©j­l”RÑ;ùs   ‚˜)é
   é   Ú55)Újoinr)   Úlen)ÚrawÚdigitss     r$   Ú_sanitize_phonerF   M   s@   € áØØ�W‰WÑ;¤# c£(Ô;Ó;€FÙØÜ
ˆ6ƒ{�hÑØ˜‘ˆØ€Mr,   c           
     óø   — | sy| }|j                  d«      r`t        |«      dv rS|dd |dd |dd }}}t        |«      dk(  rd|› d	|› d
|dd › d|dd › �S t        |«      dk(  rd|› d	|› d
|dd › d|dd › �S d|z   S )z7Formata para exibicao: +55 (11) 99999-9999 se possivel.r'   rA   )é   é   Né   é   r   ú+z (z) é   ú-é   )Ú
startswithrC   )rE   ÚdÚpaisÚdddÚrestos        r$   Ú_format_phone_brrU   Y   s¸   € áØØ€AØ‡|�|�DÔœc !›f¨Ñ0Ø˜R˜a˜5 ! A a &¨!¨A¨B¨%�5ˆcˆÜˆu‹:˜Š?Ø�t�f˜B˜s˜e 2 e¨B¨Q i [°°%¸¸°)°Ð=Ð=Üˆu‹:˜Š?Ø�t�f˜B˜s˜e 2 e¨B¨Q i [°°%¸¸°)°Ð=Ð=Ø�‰7€Nr,   c                óH   ‡— t        | «      Š‰syt        ˆfd„|D «       «      S )NFc              3  ó&   •K  — | ]  }|‰v –— Œ
 y ­wr:   © )r<   Útokr#   s     €r$   r>   z _match_tokens.<locals>.<genexpr>k   ó   øè ø€ Ò*˜Cˆs�aŒxÑ*ùó   ƒ©r+   Úany)ÚvalorÚtokensr#   s     @r$   Ú_match_tokensr`   g   s#   ø€ Üˆu‹€AÙØÜÓ* 6Ô*Ó*Ð*r,   c                óv  ‡‡— | rt        | t        «      sy| D ]Ÿ  }t        |t        «      sŒt        |j	                  d«      xs |j	                  d«      xs d«      Št        |j	                  d«      xs |j	                  d«      xs d«      Št        ˆfd„|D «       «      st        ˆfd„|D «       «      sŒŸ y	 y)
zHAttribution nativo GHL (array `attributions`) com source batendo tokens.FÚ	utmSourceÚsessionSourcer'   Ú	utmMediumÚmediumc              3  ó&   •K  — | ]  }|‰v –— Œ
 y ­wr:   rX   )r<   rY   Úss     €r$   r>   z%_attribution_match.<locals>.<genexpr>w   rZ   r[   c              3  ó&   •K  — | ]  }|‰v –— Œ
 y ­wr:   rX   )r<   rY   Úms     €r$   r>   z%_attribution_match.<locals>.<genexpr>w   s   øè ø€ Ò1M¸s°#¸´(Ñ1Mùr[   T)r(   ÚlistÚdictr+   r2   r]   )Úattributionsr_   Úattri   rg   s      @@r$   Ú_attribution_matchrn   n   s�   ù€ áœz¨,¼Ô=ØØò ˆÜ˜#œtÔ$ØÜ�3—7‘7˜;Ó'ÒI¨3¯7©7°?Ó+CÒIÀrÓJˆÜ�3—7‘7˜;Ó'ÒB¨3¯7©7°8Ó+<ÒBÀÓCˆÜÓ* 6Ô*Ô*¬cÓ1MÀfÔ1MÕ.MÙðð r,   c                ó€   ‡— | sy| D �cg c]  }t        |«      ‘Œ }}|D ]  Št        ˆfd„|D «       «      sŒ y yc c}w )NFc              3  ó&   •K  — | ]  }|‰v –— Œ
 y ­wr:   rX   )r<   rY   Útags     €r$   r>   z_tags_match.<locals>.<genexpr>�   s   øè ø€ Ò,˜cˆs�cŒzÑ,ùr[   Tr\   )Útagsr_   ÚtÚtags_lowrq   s       @r$   Ú_tags_matchru   |   sJ   ø€ ÙØØ#'Ö(˜a”�q•	Ð(€HÐ(Øò ˆÜÓ, VÔ,Õ,Ùðð ùò	 )s   ‰;c           	     ó8  — dddddœ}t        «       }g }| xs g D �]<  }|j                  d«      }|r||v rŒt        |j                  d«      «      }|rt        |cxk  r	t        k  sn ŒNt        |«      }|j                  t        «      }|j                  t        «      }	|j                  t        «      }
|j                  d«      }|j                  d«      xs g }|j                  d«      xs g }g }t        |t        «      r|j                  d«       t        |t        «      r|j                  d«       t        |t        «      r|j                  d	«       t        |t        «      r|j                  d«       |s�Œ]|D ]  }||xx   d
z  cc<   Œ |j                  |«       |j                  d«      xs dj!                  «       }|j                  d«      xs dj!                  «       }|› d|› �j!                  «       }|s.|j                  d«      xs |j                  d«      xs d|dd › �}|j                  d«      }|sl|j                  d«      xs g }|rUt#        |t$        «      rE|d   }t#        |t&        «      r%|j                  d«      xs |j                  d«      }nt)        |«      }t+        |«      }|j                  i d|“d|“d|xs d“d|“d|rt-        |«      nd“d|rd|› �nd“d|j                  d«      xs d“d|j                  d«      xs d“dt/        |«      “d|xs d“d |	xs d“d!|
xs d“d|xs d“d|“d"|“d#d$t1        «       › d%|› �“«       �Œ? |j3                  d&„ d'¬(«       ||t        j5                  «       t        j5                  «       d)d*œd+œS ),a‹  
    Filtra contatos de setembro/2026 que vieram do trafego.

    Retorna:
      {
        "leads":  [ { ...normalizado... } ],
        "breakdown": { "utm_source": N, "source": N, "attribution": N, "tags": N },
        "janela": { "inicio_utc": iso, "fim_utc": iso }
      }

    Dedupe por contactId. Lead que bate em >1 regra conta nas N regras no
    breakdown, mas aparece 1x na lista.
    r   )Ú
utm_sourceÚsourceÚattributionrr   r/   Ú	dateAddedrx   rr   rl   rw   ry   r   Ú	firstNamer'   ÚlastNameú ÚcontactNameÚemailzLead Né   ÚphoneÚadditionalPhonesr0   Ú
contact_idÚnomeÚ	phone_rawÚphone_digitsÚphone_displayÚwa_urlzhttps://wa.me/Ú
date_addedÚdate_added_brÚutm_campaignÚ
utm_mediumÚreasonsÚ
linkia_urlz#https://app.linkia.app/v2/location/z/contacts/detail/c                ó,   — | j                  d«      xs dS )Nr‰   r'   r1   )Úrs    r$   ú<lambda>zfiltrar_leads.<locals>.<lambda>ê   s   € ˜1Ÿ5™5 Ó.Ò4°"€ r,   T©ÚkeyÚreversezSetembro/2026)Ú
inicio_utcÚfim_utcÚlabel)ÚleadsÚ	breakdownÚjanela)Úsetr2   r%   ÚJANELA_INICIO_UTCÚJANELA_FIM_UTCr7   r   r   r	   r`   ÚTOKENS_UTM_OU_SOURCEÚappendrn   ru   Ú
TOKENS_TAGÚaddÚstripr(   rj   rk   r)   rF   rU   Ú_fmt_data_brÚ_get_location_idÚsortÚ	isoformat)Úcontatosr™   Úseenr4   r3   r6   ÚdtÚcfsÚutm_source_valÚutm_campaign_valÚutm_medium_valÚ
source_valrr   rl   r�   r�   ÚfirstÚlastr„   r…   ÚaddlÚprimeiror†   s                          r$   Úfiltrar_leadsr³   ‰   sÜ  € ð  !¨A¸aÈÑK€IÜ“U€DØ€CàŠ^˜ó LˆØ�e‰e�D‹kˆÙ�c˜T‘kØä�q—u‘u˜[Ó)Ó*ˆÙÔ+¨rÔC´^ÔCØä  Ó#ˆØŸ™Ô!1Ó2ˆØŸ7™7Ô#5Ó6ÐØŸ™Ô!1Ó2ˆà—U‘U˜8“_ˆ
Ø�u‰u�V‹}Ò" ˆØ—u‘u˜^Ó,Ò2°ˆàˆÜ˜Ô)=Ô>Ø�N‰N˜<Ô(Ü˜Ô%9Ô:Ø�N‰N˜8Ô$Ü˜lÔ,@ÔAØ�N‰N˜=Ô)Ü�tœZÔ(Ø�N‰N˜6Ô"áÙàò 	ˆAØ�a‹L˜AÑŒLð	ð 	�‰�Œð —‘�{Ó#Ò) r×0Ñ0Ó2ˆØ—‘�jÓ!Ò' R×.Ñ.Ó0ˆØ�'˜˜4˜&Ð!×(Ñ(Ó*ˆÙØ—5‘5˜Ó'ÒN¨1¯5©5°«>ÒN¸uÀSÈÈ!ÀWÀIÐ=NˆDð —E‘E˜'“Nˆ	ÙØ—5‘5Ð+Ó,Ò2°ˆDÙœ
 4¬Ô.Ø ™7�Ü˜h¬Ô-Ø (§¡¨WÓ 5Ò N¸¿¹ÀgÓ9N‘Iä # H£�Iä& yÓ1ˆà�
‰
ð 
Ø˜#ð
à�Dð
ð ˜š bð
ð ˜Lð	
ð
 ¹|Ô-¨lÔ;ÐQSð
ð ¹˜ | nÑ5È2ð
ð �Q—U‘U˜7“^Ò) rð
ð ˜!Ÿ%™% Ó,Ò2°ð
ð œ\¨"Ó-ð
ð ˜.Ò.¨Bð
ð Ð,Ò2°ð
ð ˜.Ò.¨Bð
ð �jÒ& Bð
ð �Dð
ð �wð
ð  Ø5Ü#Ó%Ð&Ð&7¸°uð>ð#
ö 	ðqLð^ ‡H�HÑ4¸d€HÔCð Øä+×5Ñ5Ó7Ü%×/Ñ/Ó1Ø$ñ
ñð r,   c                ót   — | sy	 | j                  t        «      }|j                  d«      S # t        $ r Y yw xY w)Nr'   z%d/%m %H:%M)Ú
astimezoneÚTZ_BRÚstrftimer!   )Údt_utcÚdt_brs     r$   r£   r£   ÷   s>   € ÙØðØ×!Ñ!¤%Ó(ˆØ�~‰~˜mÓ,Ð,øÜò Ùðús   …%+ «	7¶7c                 ó2   — 	 ddl m}  | S # t        $ r Y yw xY w)Nr   ©ÚPX3_LOCATION_IDÚW7PGxpfbsFaEEUoQOtUb)Ú
config_ghlr¼   r!   r»   s    r$   r¤   r¤     s"   € ð&Ý.ØÐøÜò &Ù%ð&ús   ‚
 Š	•c                ó  — t         › d| › d�}	 t        j                  |||¬«      }	 |j                  «       }|j                  dk7  rd	d
|j                  › �|dœS 	 |j                  d«      xs g }|D �cg c]-  }|j                  d«      xs dj                  t        «      sŒ,|‘Œ/ }	}|	sdddœS |	j                  d„ d¬«       |	d   }
|
j                  d«      xs d}|}|j                  d«      }|dk7  r||dz   d j                  «       }d|
j                  d«      |||
j                  d«      |
j                  d«      dœdœS # t        $ r d|j
                  xs ddd i}Y �Œ,w xY w# t        $ r}d	d|› �dœcY d}~S d}~ww xY wc c}w )aŠ  
    GET /contacts/{id}/notes -> retorna a nota mais recente que comeca com
    NOTA_PREFIX_VENDEDORA (pra mostrar como rascunho na textarea).

    Retorna:
      {"ok": True, "nota": {"id": "...", "body": "...", "createdAt": "...",
                            "texto_puro": "..."}}
      ou {"ok": True, "nota": None} se nao houver nota de vendedora.
      ou {"ok": False, "erro": "..."}
    ú
/contacts/ú/notes)ÚheadersÚtimeoutrD   r'   Néô  éÈ   FúGHL ©ÚokÚerroÚdetalheúrequest_fail: ©rÈ   rÉ   ÚnotesÚbodyT©rÈ   Únotac                ó,   — | j                  d«      xs dS )NÚ	createdAtr'   r1   )Úns    r$   r‘   z#buscar_observacao.<locals>.<lambda>+  s   €  q§u¡u¨[Ó'9Ò'?¸R€ r,   r’   r   ú] éÿÿÿÿrJ   r/   rÒ   ÚuserId)r/   rÎ   Ú
texto_purorÒ   rÖ   )ÚGHL_BASEÚrequestsr2   Újsonr!   ÚtextÚstatus_coderP   ÚNOTA_PREFIX_VENDEDORAr¥   Úfindr¢   )rƒ   rÂ   rÃ   Úurlr�   ÚdataÚerÍ   rÓ   Úvendedora_notesrÐ   rÎ   r×   Ú
fechamentos                 r$   Úbuscar_observacaorä     s´  € ô ˆJ�j  ¨FÐ
3€Cð
;Ü�L‰L˜ g°wÔ?ˆð	1Ø—6‘6“8ˆDð �=‰=˜CÒØ¨4°·±¨Ð)?ÈDÑQÐQð  ð
 �H‰H�WÓÒ# €Eð öØ˜QŸU™U 6›]Ò0¨b×<Ñ<Ô=RÕSŠð€Oð ñ Ø DÑ)Ð)Ø×ÑÑ?ÈÐÔNØ˜1Ñ€DØ�8‰8�FÓÒ!˜r€Dà€JØ—‘˜4“€JØ�RÒØ˜* q™.˜/Ð*×0Ñ0Ó2ˆ
àà—(‘(˜4“.ØØ$ØŸ™ +Ó.Ø—h‘h˜xÓ(ñ
ñ	ð 	øô- ò 	1Ø˜AŸF™FšL b¨$¨3Ð/Ð0‹Dð	1ûô ò ;Ø ~°a°SÐ%9Ñ:Õ:ûð;üò
sK   ŽE' §E ·!E' Á3-FÂ!FÅE$Å E' Å#E$Å$E' Å'	FÅ0E=Å7FÅ=Fc                óò  — |xs dj                  «       }|sdddœS t        |«      dkD  r|dd }t        j                  t        «      }|j                  d«      }|xs dj                  «       xs d}t        › d	|› d	|› d
|› �}t        › d| › d�}	d|i}
	 t        j                  |	||
|¬«      }	 |j                  «       }|j                  dvrdd|j                  › �|dœS 	 |xs i j                  d«      xs |xs i }d|j                  d«      |j                  d«      xs ||j                  d«      |dœdœS # t        $ r d|j                  xs ddd i}Y Œšw xY w# t        $ r}dd|› �dœcY d}~S d}~ww xY w)zu
    Cria uma NOTA NOVA no contato (nao sobrescreve a anterior).

    Prefixo: [VENDEDORA nome 07/10/2026 14h32]
    r'   FÚtexto_vaziorÌ   i   Nz%d/%m/%Y %Hh%MÚ	vendedorar}   rÔ   rÀ   rÁ   rÎ   )rÂ   rÚ   rÃ   rD   rÄ   )rÅ   éÉ   rÆ   rÇ   rË   ÚnoteTr/   rÒ   )r/   rÎ   rÒ   r×   rÏ   )r¢   rC   r   Únowr¶   r·   rÝ   rØ   rÙ   ÚpostrÚ   r!   rÛ   rÜ   r2   )rƒ   Útextorç   rÂ   rÃ   Úagora_brÚcarimboÚnome_vendedorarÎ   rß   Úpayloadr�   rà   rá   rÐ   s                  r$   Úsalvar_observacaorñ   ?  s®  € ð Š[�b×ÑÓ!€EÙØ ]Ñ3Ð3Ü
ˆ5ƒz�DÒØ�e�t�ˆä�|‰|œEÓ"€HØ×ÑÐ 0Ó1€GØÒ. ;×5Ñ5Ó7ÒF¸;€NÜ#Ð$ A nÐ%5°Q°w°i¸rÀ%ÀÐI€DäˆJ�j  ¨FÐ
3€CØ�tˆn€Gð;Ü�M‰M˜# w°WÀgÔNˆð	1Ø—6‘6“8ˆDð �=‰= 
Ñ*àØ˜qŸ}™}˜oÐ.Øñð ð +ð ŠJ�B×Ñ˜FÓ#Ò3¨ª
°€Dàà—(‘(˜4“.Ø—H‘H˜VÓ$Ò,¨ØŸ™ +Ó.Øñ	
ñð øô ò 	1Ø˜AŸF™FšL b¨$¨3Ð/Ð0ŠDð	1ûô ò ;Ø ~°a°SÐ%9Ñ:Õ:ûð;úsB   ÂE Â-D7 Â= E Ä7EÅE ÅEÅE Å	E6Å$E1Å+E6Å1E6)r#   r)   )Úreturnr)   )r3   rk   rò   rk   )rE   r)   rò   r)   )r^   r)   r_   úIterable[str]rò   Úbool)rl   rj   r_   ró   rò   rô   )rr   rj   r_   ró   rò   rô   )r§   z
list[dict]rò   rk   )é   )rƒ   r)   rÂ   rk   rÃ   Úintrò   rk   )rƒ   r)   rì   r)   rç   r)   rÂ   rk   rÃ   rö   rò   rk   )$Ú__doc__Ú
__future__r   r   Úzoneinfor   Útypingr   rÙ   r¾   r   r   r	   r!   r¶   ÚTZ_UTCrµ   rœ   r�   rž   r    rÝ   rØ   r%   r+   r7   rF   rU   r`   rn   ru   r³   r£   r¤   rä   rñ   rX   r,   r$   ú<module>rü      s  ðñ
õ #å Ý Ý ã ð.÷ñ ñ 	Ð$Ó%€Ù	�%‹€ñ ˜T 1 a¨¨A¨q¸Ô?×JÑJÈ6ÓRÐ Ù˜$  2 r¨2¨r¸%Ô@×KÑKÈFÓS€ð TÐ ØM€
ð %Ð à1€óó;óó	óó+óóókó\ó&ô0ðh &(ð.Ø"ð.Ø,0ô.øðG	 ò .à-ÐØ/ÐØ-Òð	.ús    
B9 Â9CÃC