+
    iJ                     >   R t ^ RIt^ RIHtHtHt ^RIHtHt Rt	Rt
RtRtRt^t^
t^t^#t^tR R	 ltR,R
 R lltR R ltR-R R lltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR  R! ltR" R# lt R$ R% lt!R& R' lt"R( R) lt#R* R+ lt$R# ).z.Popularity-aware scoring for last30days skill.N)ListOptionalUnion)datesschema?      ?333333?皙?c                F    V ^8  d   QhR\         \        ,          R\        /# )   xreturn)r   intfloat)formats   "Q/Users/bowang/.openclaw/workspace/skills/last30days-official/scripts/lib/score.py__annotate__r      s      (3- E     c                H    V e   V ^ 8  d   R# \         P                  ! V 4      # )z1Safe log1p that handles None and negative values.g        )mathlog1p)r   s   &r   
log1p_safer      s    yAE::a=r   c                    V ^8  d   QhR\         \        P                  ,          R\         \        ,          R\         \        ,          /# )r   
engagementtop_comment_scorer   )r   r   
Engagementr   r   )r   s   "r   r   r   "   s>     J J**+J}J e_Jr   c                T   V f   R# V P                   f   V P                  f   R# \        V P                   4      p\        V P                  4      pV P                  ;'       g    R^
,          p\        V4      pRV,          RV,          ,           RV,          ,           RV,          ,           # )u:  Compute raw engagement score for Reddit item.

Formula: 0.50*log1p(score) + 0.35*log1p(num_comments) + 0.05*(upvote_ratio*10) + 0.10*log1p(top_comment_score)

The 10% comment quality weight rewards posts where the community engaged deeply
— a highly upvoted top comment means the thread sparked real discussion.
N      ?ffffff?皙?g?)scorenum_commentsr   upvote_ratio)r   r   r!   commentsratiotop_cmts   &&    r   compute_reddit_engagement_rawr'   "   s     J$;$;$Cz''(E*112H$$++r1E*+G%<$/)D5L84'>IIr   c                p    V ^8  d   QhR\         \        P                  ,          R\         \        ,          /# r   r   r   r   r   r   r   )r   s   "r   r   r   ;   s.     J J&2C2C)D JRW Jr   c                X   V f   R# V P                   f   V P                  f   R# \        V P                   4      p\        V P                  4      p\        V P                  4      p\        V P                  4      pRV,          RV,          ,           RV,          ,           RV,          ,           # )zCompute raw engagement score for X item.

Formula: 0.55*log1p(likes) + 0.25*log1p(reposts) + 0.15*log1p(replies) + 0.05*log1p(quotes)
Nr
   r   333333?r    )likesrepostsr   repliesquotes)r   r-   r.   r/   r0   s   &    r   compute_x_engagement_rawr1   ;   s    
 J$6$6$>z''(E++,G++,G
))*F%<$.(4'>9D6MIIr   c                h    V ^8  d   QhR\         \        ,          R\        R\         \        ,          /# )r   valuesdefaultr   )r   r   )r   s   "r   r   r   N   s)      T%[ 5 $u+ r   c                   V  Uu. uF
  q"f   K  VNK  	  ppV'       g   V  Uu. uF
  q"f   TM^2NK  	  up# \        V4      p\        V4      pWT,
          pV^ 8X  d   V  Uu. uF
  q"f   ^2M^2NK  	  up# . pV  FA  pVf   VP                  R4       K  W$,
          V,          ^d,          pVP                  V4       KC  	  V# u upi u upi u upi )zNormalize a list of values to 0-100 scale.

Args:
    values: Raw values (None values are preserved)
    default: Default value for None entries

Returns:
    Normalized values
N)minmaxappend)	r3   r4   vvalidmin_valmax_val	range_valresult
normalizeds	   &&       r   normalize_to_100r@   N   s     01QQE06<=f9",f==%jG%jG!IA~178AiR'88F9MM$;)3s:JMM*%  M' 1= 9s   B=B=CCc                    V ^8  d   QhR\         \        P                  ,          R\         \        P                  ,          /# r   itemsr   )r   r   
RedditItem)r   s   "r   r   r   o   s0     > >d6#4#45 >$v?P?P:Q >r   c           
     X   V '       g   V # . pV  FY  pRpVP                   '       d   VP                   ^ ,          P                  pVP                  \        VP                  V4      4       K[  	  \        V4      p\        V 4       EF#  w  rR\        VP                  ^d,          4      p\        P                  ! VP                  4      pWE,          e   \        WE,          4      pM\        p\        P                  ! VVVR7      Vn        \         V,          \"        V,          ,           \$        V,          ,           p	W,          f   V	\&        ,          p	VP(                  R8X  d   V	^,          p	MVP(                  R8X  d
   V	^,          p	\+        ^ \-        ^d\        V	4      4      4      Vn        EK&  	  V # )zpCompute scores for Reddit items.

Args:
    items: List of Reddit items

Returns:
    Items with updated scores
N	relevancerecencyr   lowmed)top_commentsr!   r8   r'   r   r@   	enumerater   rG   r   recency_scoredateDEFAULT_ENGAGEMENTr   	SubScoressubsWEIGHT_RELEVANCEWEIGHT_RECENCYWEIGHT_ENGAGEMENTUNKNOWN_ENGAGEMENT_PENALTYdate_confidencer7   r6   )
rC   eng_rawitemtop_cmt_scoreeng_normalizedi	rel_score	rec_score	eng_scoreoveralls
   &         r   score_reddit_itemsr`   o   sl     G --a066M4T__mTU	  &g.NU#,-	 ''		2	 (N-.I*I $$ 
	 y(Y&'	)* 	 :11G 5(qLG!!U*qLGCS\23
K $N Lr   c                    V ^8  d   QhR\         \        P                  ,          R\         \        P                  ,          /# rB   )r   r   XItem)r   s   "r   r   r      s,     9 9fll+ 9V\\0B 9r   c           
        V '       g   V # V  Uu. uF  p\        VP                  4      NK  	  pp\        V4      p\        V 4       EF#  w  rA\	        VP
                  ^d,          4      p\        P                  ! VP                  4      pW4,          e   \	        W4,          4      pM\        p\        P                  ! VVVR7      Vn        \        V,          \        V,          ,           \        V,          ,           pW$,          f   V\         ,          pVP"                  R8X  d   V^,          pMVP"                  R8X  d
   V^,          p\%        ^ \'        ^d\	        V4      4      4      Vn        EK&  	  V # u upi )zfCompute scores for X items.

Args:
    items: List of X items

Returns:
    Items with updated scores
rF   rI   rJ   )r1   r   r@   rL   r   rG   r   rM   rN   rO   r   rP   rQ   rR   rS   rT   rU   rV   r7   r6   r!   	rC   rX   rW   rZ   r[   r\   r]   r^   r_   s	   &        r   score_x_itemsre      sE     FKKUT'8UGK &g.NU#,-	 ''		2	 (N-.I*I $$ 
	 y(Y&'	)* 	 :11G 5(qLG!!U*qLGCS\23
K $N LY Ls   E/c                p    V ^8  d   QhR\         \        P                  ,          R\         \        ,          /# r)   r*   )r   s   "r   r   r      s*     9 9x8I8I/J 9xX] 9r   c                   V f   R# V P                   f   V P                  f   R# \        V P                   4      p\        V P                  4      p\        V P                  4      pRV,          RV,          ,           RV,          ,           # )u   Compute raw engagement score for YouTube item.

Formula: 0.50*log1p(views) + 0.35*log1p(likes) + 0.15*log1p(comments)
Views dominate on YouTube — they're the primary discovery signal.
Nr   r   r,   viewsr-   r   r"   r   ri   r-   r$   s   &   r   compute_youtube_engagement_rawrk      s     J$4$4$<z''(Ez''(E*112H%<$,&88r   c                    V ^8  d   QhR\         \        P                  ,          R\         \        P                  ,          /# rB   )r   r   YouTubeItem)r   s   "r   r   r      s0     % %tF$6$67 %DASAS<T %r   c           
     z   V '       g   V # V  Uu. uF  p\        VP                  4      NK  	  pp\        V4      p\        V 4       F  w  rA\	        VP
                  ^d,          4      p\        P                  ! VP                  4      pW4,          e   \	        W4,          4      pM\        p\        P                  ! VVVR7      Vn        \        V,          \        V,          ,           \        V,          ,           pW$,          f   V\         ,          p\#        ^ \%        ^d\	        V4      4      4      Vn        K  	  V # u upi )znCompute scores for YouTube items.

Uses same weight structure as Reddit/X (relevance + recency + engagement).
rF   )rk   r   r@   rL   r   rG   r   rM   rN   rO   r   rP   rQ   rR   rS   rT   rU   r7   r6   r!   rd   s	   &        r   score_youtube_itemsrp      s   
 KPQ54-doo>5GQ%g.NU#,-	''		2	(N-.I*I$$ 
	 y(Y&'	)* 	 :11GCS\23
1 $4 L; R   D8c                p    V ^8  d   QhR\         \        P                  ,          R\         \        ,          /# r)   r*   )r   s   "r   r   r   '  s*     9 9hv7H7H.I 9hW\o 9r   c                   V f   R# V P                   f   V P                  f   R# \        V P                   4      p\        V P                  4      p\        V P                  4      pRV,          RV,          ,           RV,          ,           # )u   Compute raw engagement score for TikTok item.

Formula: 0.50*log1p(views) + 0.30*log1p(likes) + 0.20*log1p(comments)
Views dominate on TikTok — they're the primary discovery signal.
Nr   r	   皙?rh   rj   s   &   r   compute_tiktok_engagement_rawru   '  rl   r   c                    V ^8  d   QhR\         \        P                  ,          R\         \        P                  ,          /# rB   )r   r   
TikTokItem)r   s   "r   r   r   :  s0     % %d6#4#45 %$v?P?P:Q %r   c           
     z   V '       g   V # V  Uu. uF  p\        VP                  4      NK  	  pp\        V4      p\        V 4       F  w  rA\	        VP
                  ^d,          4      p\        P                  ! VP                  4      pW4,          e   \	        W4,          4      pM\        p\        P                  ! VVVR7      Vn        \        V,          \        V,          ,           \        V,          ,           pW$,          f   V\         ,          p\#        ^ \%        ^d\	        V4      4      4      Vn        K  	  V # u upi )zlCompute scores for TikTok items.

Uses same weight structure as YouTube (relevance + recency + engagement).
rF   )ru   r   r@   rL   r   rG   r   rM   rN   rO   r   rP   rQ   rR   rS   rT   rU   r7   r6   r!   rd   s	   &        r   score_tiktok_itemsry   :  s   
 JOP%$,T__=%GP%g.NU#,-	''		2	(N-.I*I$$ 
	 y(Y&'	)* 	 :11GCS\23
1 $4 L; Qrq   c                p    V ^8  d   QhR\         \        P                  ,          R\         \        ,          /# r)   r*   )r   s   "r   r   r   b  s,     9 9&:K:K1L 9QYZ_Q` 9r   c                   V f   R# V P                   f   V P                  f   R# \        V P                   4      p\        V P                  4      p\        V P                  4      pRV,          RV,          ,           RV,          ,           # )u   Compute raw engagement score for Instagram item.

Formula: 0.50*log1p(views) + 0.30*log1p(likes) + 0.20*log1p(comments)
Views dominate on Instagram Reels — they're the primary discovery signal.
Nr   r	   rt   rh   rj   s   &   r    compute_instagram_engagement_rawr|   b  rl   r   c                    V ^8  d   QhR\         \        P                  ,          R\         \        P                  ,          /# rB   )r   r   InstagramItem)r   s   "r   r   r   u  s0     % %f&:&:!; %VEYEY@Z %r   c           
     z   V '       g   V # V  Uu. uF  p\        VP                  4      NK  	  pp\        V4      p\        V 4       F  w  rA\	        VP
                  ^d,          4      p\        P                  ! VP                  4      pW4,          e   \	        W4,          4      pM\        p\        P                  ! VVVR7      Vn        \        V,          \        V,          ,           \        V,          ,           pW$,          f   V\         ,          p\#        ^ \%        ^d\	        V4      4      4      Vn        K  	  V # u upi )znCompute scores for Instagram items.

Uses same weight structure as TikTok (relevance + recency + engagement).
rF   )r|   r   r@   rL   r   rG   r   rM   rN   rO   r   rP   rQ   rR   rS   rT   rU   r7   r6   r!   rd   s	   &        r   score_instagram_itemsr   u  s   
 MRSUT/@UGS%g.NU#,-	''		2	(N-.I*I$$ 
	 y(Y&'	)* 	 :11GCS\23
1 $4 L; Trq   c                p    V ^8  d   QhR\         \        P                  ,          R\         \        ,          /# r)   r*   )r   s   "r   r   r     s,     + +(6;L;L2M +RZ[`Ra +r   c                    V f   R# V P                   f   V P                  f   R# \        V P                   4      p\        V P                  4      pRV,          RV,          ,           # )zCompute raw engagement score for Hacker News item.

Formula: 0.55*log1p(points) + 0.45*log1p(num_comments)
Points are the primary signal on HN; comments indicate depth of discussion.
Nr
   r   )r!   r"   r   )r   pointsr$   s   &  r   !compute_hackernews_engagement_rawr     sZ     J$;$;$C
(()F*112H&=4(?**r   c                    V ^8  d   QhR\         \        P                  ,          R\         \        P                  ,          /# rB   )r   r   HackerNewsItem)r   s   "r   r   r     0     % %$v'<'<"= %$vG\G\B] %r   c           
     z   V '       g   V # V  Uu. uF  p\        VP                  4      NK  	  pp\        V4      p\        V 4       F  w  rA\	        VP
                  ^d,          4      p\        P                  ! VP                  4      pW4,          e   \	        W4,          4      pM\        p\        P                  ! VVVR7      Vn        \        V,          \        V,          ,           \        V,          ,           pW$,          f   V\         ,          p\#        ^ \%        ^d\	        V4      4      4      Vn        K  	  V # u upi )zrCompute scores for Hacker News items.

Uses same weight structure as Reddit/X (relevance + recency + engagement).
rF   )r   r   r@   rL   r   rG   r   rM   rN   rO   r   rP   rQ   rR   rS   rT   rU   r7   r6   r!   rd   s	   &        r   score_hackernews_itemsr        
 NSTed0AeGT%g.NU#,-	''		2	(N-.I*I$$ 
	 y(Y&'	)* 	 :11GCS\23
1 $4 L; Urq   c                p    V ^8  d   QhR\         \        P                  ,          R\         \        ,          /# r)   r*   )r   s   "r   r   r     s,     , ,(6;L;L2M ,RZ[`Ra ,r   c                    V f   R# V P                   f   V P                  f   R# \        P                  ! V P                   ;'       g    ^ 4      p\        P                  ! V P                  ;'       g    ^ 4      pRV,          RV,          ,           # )zCompute raw engagement score for Polymarket item.

Formula: 0.60*log1p(volume) + 0.40*log1p(liquidity)
Volume is the primary signal (money flowing); liquidity indicates market depth.
Ng333333?g?)volume	liquidityr   r   )r   r   r   s   &  r   !compute_polymarket_engagement_rawr     ss      Z%9%9%AZZ
))..Q/F

://4415I&=4)+++r   c                    V ^8  d   QhR\         \        P                  ,          R\         \        P                  ,          /# rB   )r   r   PolymarketItem)r   s   "r   r   r     r   r   c           
     z   V '       g   V # V  Uu. uF  p\        VP                  4      NK  	  pp\        V4      p\        V 4       F  w  rA\	        VP
                  ^d,          4      p\        P                  ! VP                  4      pW4,          e   \	        W4,          4      pM\        p\        P                  ! VVVR7      Vn        \        V,          \        V,          ,           \        V,          ,           pW$,          f   V\         ,          p\#        ^ \%        ^d\	        V4      4      4      Vn        K  	  V # u upi )zqCompute scores for Polymarket items.

Uses same weight structure as Reddit/X (relevance + recency + engagement).
rF   )r   r   r@   rL   r   rG   r   rM   rN   rO   r   rP   rQ   rR   rS   rT   rU   r7   r6   r!   rd   s	   &        r   score_polymarket_itemsr     r   rq   c                    V ^8  d   QhR\         \        P                  ,          R\         \        P                  ,          /# rB   )r   r   WebSearchItem)r   s   "r   r   r     s0     6 6f&:&:!; 6VEYEY@Z 6r   c           
        V '       g   V # V  F  p\        VP                  ^d,          4      p\        P                  ! VP                  4      p\
        P                  ! VV^ R7      Vn        \        V,          \        V,          ,           pV\        ,          pVP                  R8X  d   V\        ,          pMVP                  R8X  d   V\        ,          p\        ^ \        ^d\        V4      4      4      Vn        K  	  V # )a  Compute scores for WebSearch items WITHOUT engagement metrics.

Uses reweighted formula: 55% relevance + 45% recency - 15pt source penalty.
This ensures WebSearch items rank below comparable Reddit/X items.

Date confidence adjustments:
- High confidence (URL-verified date): +10 bonus
- Med confidence (snippet-extracted date): no change
- Low confidence (no date signals): -20 penalty

Args:
    items: List of WebSearch items

Returns:
    Items with updated scores
rF   highrI   )r   rG   r   rM   rN   r   rP   rQ   WEBSEARCH_WEIGHT_RELEVANCEWEBSEARCH_WEIGHT_RECENCYWEBSEARCH_SOURCE_PENALTYrV   WEBSEARCH_VERIFIED_BONUSWEBSEARCH_NO_DATE_PENALTYr7   r6   r!   )rC   rX   r\   r]   r_   s   &    r   score_websearch_itemsr     s    " ,-	 ''		2	 $$
	 '2$y01 	 	++ 6)//G!!U*00GCS\23
A D Lr   c                D   V ^8  d   QhR\         \        \        P                  \        P                  \        P
                  \        P                  \        P                  \        P                  \        P                  \        P                  3,          ,          R\         /# rB   )r   r   r   rD   rb   r   rn   rw   r~   r   r   )r   s   "r   r   r   J  s     (' ('d5!2!2FLL&BVBVX^XjXjlrl}l}  @F  @T  @T  V\  Vk  Vk  ms  mB  mB  "B  C  D ('  IM ('r   c                     R p\        WR7      # )zSort items by score (descending), then date, then source priority.

Args:
    items: List of items to sort

Returns:
    Sorted items
c                    V P                   ) pV P                  ;'       g    R p\        VP                  RR4      4      ) p\	        V \
        P                  4      '       d   ^ pM\	        V \
        P                  4      '       d   ^pM\	        V \
        P                  4      '       d   ^pM\	        V \
        P                  4      '       d   ^pMk\	        V \
        P                  4      '       d   ^pMH\	        V \
        P                  4      '       d   ^pM%\	        V \
        P                  4      '       d   ^pM^p\        V RR4      ;'       g    \        V RR4      pWWE3# )z
0000-00-00- titletext)r!   rN   r   replace
isinstancer   rD   rb   rn   rw   r~   r   r   getattr)rX   r!   rN   date_keysource_priorityr   s   &     r   sort_keysort_items.<locals>.sort_keyS  s    yy((LS"-.. dF--..Ofll++Of0011Of//00Of2233Of3344Of3344OO tWb)FFWT62-F77r   )key)sorted)rC   r   s   & r   
sort_itemsr   J  s    8> %&&r   )N)2   )%__doc__r   typingr   r   r   r   r   r   rR   rS   rT   r   r   r   r   r   rO   rU   r   r'   r1   r@   r`   re   rk   rp   ru   ry   r|   r   r   r   r   r   r   r    r   r   <module>r      s    4  ( (     "         J2J&B>B9x9&%P9&%P9&%P+$%P,$%P6r('r   