Ë
    8Jj·  ã                  óÎ   — d Z ddlmZ ddlZddlZddlmZ ddlmZm	Z	 ddl
Z
ddlmZ ddlmZ ddlmZ dd	lmZmZ  ej*                  e«      Zd
Zdd„Zdd„Zdd„Zdd„Zddd„Zy)z9Track A: research existing live blogs from blog_list_url.é    )ÚannotationsN)ÚAny)ÚurljoinÚurlparse)Úsettings)Útimezone)ÚBlogSettings)Úclaude_jsonÚclaude_json_objecti€8 c                ó¦   — t        t        dd«      }t        j                  | |ddi¬«      }|j	                  «        |j
                  xs dd t         S )NÚBLOG_FETCH_TIMEOUTé   z
User-AgentzShopifyBlogBot/1.0)ÚtimeoutÚheadersÚ )ÚgetattrÚdjango_settingsÚrequestsÚgetÚraise_for_statusÚtextÚHTML_MAX_CHARS)Úurlr   Úresps      úJ/var/www/html/shopify_to_digistore/blogs/services/blog_research_service.pyÚ_fetch_htmlr      sQ   € Ü”oÐ';¸RÓ@€GÜ�<‰<ØØØÐ3Ð4ô€Dð
 	×ÑÔØ�I‰IŠO˜˜_œnÐ-Ð-ó    c           
     óØ   — d}d|› d| d t          › dt        t        dd«      › d�}	 t        ||d¬	«      S # t        j
                  t        f$ r}t        j                  d
|«       ‚ d }~ww xY w)Na+  You extract blog post links from an HTML blog listing page. Return ONLY a valid JSON array of objects. Each object has keys: title, url, snippet (optional string). Use absolute URLs. Only blog article pages, not tags/categories/pagination. Keep titles short; escape any double quotes inside strings.z
Base URL: ú

HTML:
z

Return at most ÚBLOG_RESEARCH_MAX_DISCOVERr   z posts.gš™™™™™É?)Útemperaturezdiscover_posts JSON failed: %s)	r   r   r   r
   ÚjsonÚJSONDecodeErrorÚ
ValueErrorÚloggerÚwarning)Ú	list_htmlÚbase_urlÚsystemÚuserÚexcs        r   Ú_discover_postsr,   !   s�   € ð	Fð ð �X�Jð Ø˜Oœ^Ð,Ð-ð .Ü!¤/Ð3OÐQSÓTÐUÐU\ð	^ð 	ð
Ü˜6 4°SÔ9Ð9øÜ× Ñ ¤*Ð-ò Ü�‰Ð7¸Ô=Øûðús   ¦4 ´A)ÁA$Á$A)c                óP   — d}d|› d| d t          › �}t        ||dd¬«      }||d<   |S )Nz­You analyze a single blog post HTML page. Return ONLY one JSON object with: title, summary (2-3 sentences), main_topics (array of 3-8 keyword strings), angle (short phrase).zURL: r   iÜ  g333333Ó?)Ú
max_tokensr!   r   )r   r   )Ú	post_htmlr   r)   r*   Úitems        r   Ú_summarize_postr1   5   sG   € ð	Lð ð
 �3�%�{ 9¨_¬nÐ#=Ð">Ð?€DÜ˜f d°tÈÔM€DØ€Dˆ�KØ€Kr   c           
     ó²  — ddl m} | j                  xs dj                  «       }|sg S |j	                  |d¬«      }|j                  «       }g }|D ]„  }|j                  d«      xs d}|j                  d«      xs d}|j                  d«      }	|r
|› d	|› d
|› �nd}
|
s	|	r|› d|	› �}
|j                  ||
|j                  d«      xs dd d g dddœ«       Œ† |S )Nr   )ÚShopifyBlogServicer   é2   )ÚlimitÚhandleÚtitleÚidz/blogs/z
/articles/z/admin/articles/Úsummary_htmliô  Úshopify_api)r7   r   ÚsummaryÚmain_topicsÚangleÚsource)Ú#blogs.services.shopify_blog_servicer3   Úshopify_blog_idÚstripÚlist_articlesÚ	_base_urlr   Úappend)Úsettings_objr3   Úblog_idÚarticlesÚbaseÚoutÚar6   r7   Úart_idr   s              r   Ú_shopify_fallback_inventoryrL   A   sø   € ÝFà×+Ñ+Ò1¨r×8Ñ8Ó:€GÙØˆ	Ø!×/Ñ/°¸rÐ/ÓB€HØ×'Ñ'Ó)€DØ
€CÛˆØ—‘�x“Ò& BˆØ—‘�g“Ò$ "ˆØ—‘�t“ˆÙ=C���g˜g˜Y j°°Ñ9ÈˆÙ‘vØ�FÐ*¨6¨(Ð3ˆCØ�
‰
àØØŸE™E .Ó1Ò7°R¸¸#Ð>Ø!Ø&Ø'ñõ		
ð ð" €Jr   c           
     óÚ  — | xs t        j                  «       } | j                  xs dj                  «       }|s+d| _        | j                  ddg¬«       | j                  xs g S g }g }	 t        |«      }t        ||«      }t        t        dd«      }|d | D ]æ  }|j                  d«      xs dj                  «       }|sŒ)|j                  d	«      r0t        |«      }	t        |	j                  › d
|	j                   › �|«      }	 t        |«      }
t#        |
|«      }|j                  d«      s|j                  d«      r|d   |d<   |j                  d«      r|j                  d«      s|d   |d<   |j%                  |«       Œè |st-        | «      }|r|j%                  d«       || _        t3        j4                  «       | _        dj9                  |d d «      | _        | j                  g d¢¬«       |S # t&        $ rv}t(        j+                  d||«       |j%                  |› d|› �«       |j                  d«      r-|j%                  |d   ||j                  d«      xs dg ddœ«       Y d }~�ŒÒd }~ww xY w# t&        $ rq}t(        j/                  d«       |j%                  t1        |«      «       	 t-        | «      }n*# t&        $ r}|j%                  d|› �«       Y d }~nd }~ww xY wY d }~�ŒFd }~ww xY w)Nr   zblog_list_url is not setÚexternal_blog_inventory_errorÚ
updated_at)Úupdate_fieldsÚBLOG_RESEARCH_MAX_POSTSé   r   Ú/z://r7   Úsnippetr;   zpost fetch failed url=%s: %sz: Úlisting_only)r7   r   r;   r<   r=   z6HTML scrape found no posts; used Shopify API fallback.zblog list research failedzShopify fallback failed: z; é   )Úexternal_blog_inventoryÚ"external_blog_inventory_updated_atrN   rO   )r	   ÚloadÚblog_list_urlrA   rN   ÚsaverW   r   r,   r   r   r   Ú
startswithr   r   ÚschemeÚnetlocr1   rD   Ú	Exceptionr%   r&   rL   Ú	exceptionÚstrr   ÚnowrX   Újoin)rE   rZ   Ú	inventoryÚerrorsr'   Ú
discoveredÚ	max_postsÚentryÚraw_urlÚparsedr/   r;   r+   Úfb_excs                 r   Úrefresh_external_blog_inventoryrl   ^   sÉ  € ØÒ6¤<×#4Ñ#4Ó#6€LØ!×/Ñ/Ò5°2×<Ñ<Ó>€MÙØ5OˆÔ2Ø×ÑÐ)HÈ,Ð(WÐÔXØ×3Ñ3Ò9°rÐ9à&(€IØ€Fð-@Ü Ó.ˆ	Ü$ Y°Ó>ˆ
ÜœOÐ-FÈÓKˆ	à 
 Ó+ˆEØ—y‘y Ó'Ò-¨2×4Ñ4Ó6ˆGÙØØ×!Ñ! #Ô&Ü! -Ó0�Ü! V§]¡] O°3°v·}±}°oÐ"FÈÓP�ðÜ'¨Ó0�	Ü)¨)°WÓ=�Ø—{‘{ 7Ô+°·	±	¸'Ô0BØ',¨W¡~�G˜GÑ$Ø—9‘9˜YÔ'°·±¸IÔ0FØ).¨yÑ)9�G˜IÑ&Ø× Ñ  Õ)ð ,ñ: Ü3°LÓAˆIÙØ—‘ÐVÔWð ,5€LÔ(Ü6>·l±l³n€LÔ3Ø15·±¸6À"À1¸:Ó1F€LÔ.Ø×Ñò
ð ô ð ÐøôM ò Ü—‘Ð=¸wÈÔLØ—‘  	¨¨C¨5Ð1Ô2Ø—9‘9˜WÔ%Ø×$Ñ$à%*¨7¡^Ø#*Ø',§y¡y°Ó';Ò'A¸rØ+-Ø%3ñôÿùð	ûô& ò @Ü×ÑÐ4Ô5Ø�‰”c˜#“hÔð	@Ü3°LÓA‰IøÜò 	@Ø�M‰MÐ5°f°XÐ>×?Ñ?ûð	@ÿüð@úso   Á)BI0 ÄA<G.Å="I0 Ç.	I-Ç7A+I(É"I0 É(I-É-I0 É0	K*É9/K%Ê)J5Ê4K%Ê5	KÊ>KËK%ËKËK%Ë%K*)r   ra   Úreturnra   )r'   ra   r(   ra   rm   zlist[dict[str, str]])r/   ra   r   ra   rm   zdict[str, Any])rE   r	   rm   zlist[dict[str, Any]])N)rE   zBlogSettings | Nonerm   z
list[dict])Ú__doc__Ú
__future__r   r"   ÚloggingÚtypingr   Úurllib.parser   r   r   Údjango.confr   r   Údjango.utilsr   Úblogs.modelsr	   Úblogs.services.anthropic_utilsr
   r   Ú	getLoggerÚ__name__r%   r   r   r,   r1   rL   rl   © r   r   Ú<module>rz      sW   ðÙ ?å "ã Û Ý ß *ã Ý 3Ý !å %ß Jà	ˆ×	Ñ	˜8Ó	$€à€ó.óó(	óõ:Er   