
    i>                        d Z ddlZddlZddlZddlmZ ddlmZ ddlmZm	Z	m
Z
  ee      j                  dz  Zedz  Zedz  Zg d	Zd
ddZdefdZd ZdefdZdededefdZdedefdZde	fdZde
e   fdZd!dee   fdZedk(  rK ej<                  d      ZejA                  ded       ejC                         Z" ee"jF                          yy)"z
Generate dashboard data JSON from analysis results and database stats.
Supports channel filtering and includes channel metadata for the UI.

Run: python3 generate_dashboard_data.py [--channel CHANNEL_ID]
    N)Path)datetime)OptionalDictListoutputztrends_history.dbzdashboard_data.json)how_things_workbrain_is_lyingpower_worksmoney_trapsbody_is_weirdwhat_actually_happened )defaultc                    | }|D ]-  }||c S t        |t              r|j                  |      }+|c S  ||S t        |t              rdD ]  }||v s||   c S  t        |      S |S )z$Safely traverse nested dict/objects.)textvaluecontentdescriptionprimarymain)
isinstancedictgetstr)objr   keysvalkeyks         A/home/sietch6/trending-topics-pipeline/generate_dashboard_data.pysafe_getr"      s    
C ;Nc4 ''#,CN {#tO 	ACx1v	 3xJ    returnc                    | yt        | t              r| S t        | t              rg }| D ]  }t        |t              r|j                  d|        (t        |t              s9|j                  d      xs1 |j                  d      xs |j                  d      xs t        |      }|j                  d|         dj                  |      S t        | t              rg }| j                         D ]  \  }}|j                  dd	      j                         }t        |t              r?d
j                  d |dd D              }t        |      dkD  r=|dt        |      dz
   dz  }n't        |t              rt        |      }nt        |      }|j                  d| d|         dj                  |      S t        |       S )z@Format a potentially nested content field as readable HTML text.Nr   u   • r   r   r   z<br>_ z; c              3   8   K   | ]  }t        |      d d   y w)Nd   )r   ).0cs     r!   	<genexpr>z'format_content_field.<locals>.<genexpr>K   s     (K!Q(Ks      z (+z more)z<strong>z:</strong> z<br><br>)r   r   listappendr   r   joinitemsreplacetitlelenformat_content_field)	r   r1   itemr   partsr   r   labelcontent_texts	            r!   r5   r5   4   s   
{#s
#t 	,D$$tD6]+D$'xx'f488I+>f$((=BYf]`ae]ftD6]+	, {{5!!#tIIK 
	FLCKKS)//1E'4(#yy(Kwr{(KKw<!# c#g,*:);6$BBLGT*3G<"7|LL8E7+l^DE
	F u%%s8Or#   c                     | syt        |       } dD ]  }|| v s| d| j                  |      dz    c S  t        |       dkD  r| dd dz   S | S )z!Extract first sentence from text.r   )z. z! z? N      z...)r   indexr4   )r   ends     r!   get_first_sentencer?   W   sd    t9D! .$;,C1,--. "%TS4::d:r#   c                     dddddd} t         j                         s| S t        j                  t               }|j	                         }|j                  d       |j                         d   | d<   |j                  d       |j                         }|r-	 t        j                  |d         }|j                  d      | d	<   |j                          | S #  |d   r|d   d
d nd| d	<   Y (xY w)z*Get pipeline statistics from the database.r   NeverzNo data yet)total_itemslast_collectionlast_analysis	new_itemssummaryz SELECT COUNT(*) FROM trend_itemsrB   z=SELECT run_date FROM collection_runs ORDER BY id DESC LIMIT 1%Y-%m-%d %H:%MrC   N   Unknown)DB_PATHexistssqlite3connectcursorexecutefetchoner   fromisoformatstrftimeclose)statsconnrN   last_rundts        r!   get_pipeline_statsrX   b   s     "  E >>??7#D[[]F NN56!??,Q/E- NNRS H	V''4B'){{3C'DE#$ 	JJLL		V;CA;x{3B'7IE#$s   ,C C'oppr=   c                    | j                  dg       }t        |t              r|rt        |      }nt               }| j                  d| j                  dg             }|D ]9  }t        |t              s|j                  d      s&|j                  |d          ; | j                  dg       D ]9  }t        |t              s|j                  d      s&|j                  |d          ; | j                  dd      }|dk  rt        |d	z        n
t        |      }| j                  d
i       }t        |t              rd|i}nt        |t              si }t        |d      xs t        |d      }	|j                  d|j                  dg             }
t        |
t              r5|
D cg c])  }t        |t              rt        |d      n
t        |      + }
}ng }
| j                  di       }| j                  di       }i }ddddddd}|j                         D ]o  \  }}|j                  |i       }t        |t              r)|j                  dd      |j                  dd      d||<   Qt        |t        t        f      sh|dd||<   q | j                  di       }|r|j                  d      s|j                  dg       }t        |      D cg c]  \  }}|j                  d d!|j                  d"|d#z                |j                  d$d      |j                  d%d      |j                  d&d      |j                  d'd      |j                  d(d      |j                  d)d      d* }}}|j                  d+d      |j                  d,d      |j                  d-g       |j                  d.g       d/}n| j                  d0g       }t        |t              rnt        |      D cg c]W  \  }}t        |d1      xs t        |d       xs d2|d#z    t        |d3      xs dt        |d'      xs t        |d4      xs dd5Y }}}ng }d6}i d7t        |      d8| j                  d8d9      d|d | j                  d:| j                  d;d<            d=t        | d=      xs dd|rt        |      nd>gd?| j                  d?d#      d@| j                  d@d      dAt        t        | dB            dCt        | dC      xs t        | dD      xs ddBt        | dB      xs ddE|	xs d|
r|
d6d ng dFdGt        |dH      xs t        |dI      xs dt        |dJ      xs dt        |dK      xs dt        |dL      xs ddMdNt        | j                  dNd            dOt        | j                  dOd            dPt        | dQ      xs ddRt        | dS      xs d|||dTS c c}w c c}}w c c}}w )Uz3Transform analysis opportunity to dashboard format.sourcestopics_in_clustercluster_topicssourcemerged_fromweighted_scorer         confusion_analysisprimary_questionr   secondary_questions	secondaryquestiontarget_audiencescoresDemandGapExplainability	EvergreenBreadthCompetition)demand_signalcontent_gapexplainabilityevergreen_potentialaudience_breadthcompetition_gapscore	rationaler   )rv   	reasoningvideo_structurelegacysectionsr3   zSection section_numberr;   	timestampduration_secondspurposer   visualshook_to_next)sectionr}   r~   r   r   r   r   total_durationretention_strategykey_retention_momentsshare_triggers)r   r   r   r   	structurer   zPart durationr   )r   r}   r   NidverdictUNKNOWNsuggested_titletopicUntitledthemeunknowntopic_countchannelone_lineverdict_reasoningmissing_angleangle	confusion)rd   re   audience
the_momentmomentclick_triggerwhat_they_knowwhat_they_think_is_wrong)r   r   r   what_is_wrongexisting_contentwhy_existing_failshookopening_hookcsfcritical_success_factor)r   
video_meta
dimensions)r   r   r.   setr   addintr   r"   r1   float	enumerater?   r5   )rY   r=   r[   topicstm	raw_score	score_100r   	primary_qsecondary_qsqr   ri   r   dimension_namesr   r8   dimry   r{   isr   r   raw_structures                             r!   transform_opportunityr      sM    ggi$G'4 Wg,%,cgg6F.KL 	)A!T"quuXAhK(	) + 	)A!T"quuXAhK(	)
 (!,I'0A~IN#3y>I ,b1I)S!'3		4(	$67Y8Iy;YI==!6	kSU8VWL,%\hiWX:a3FJ/CPQFRii ww("-H WWXr"FJ!**%(O &++- @
Ujjb!c4 !, WW["5!Ju c5\**-B ?Ju@ gg/4O228<"&&z26 "(+
 1 55HQUU;KQqS5Q4R*STUU;3$%EE*<a$@55B/55B/55B/ !nb 9
	 
 .112BBG"1"5"56JB"O%4%8%89PRT%U-112BBG	

 R0mT* &m4 Aq	  (95^!W9M^SXYZ[\Y\X]Q^!)!Z!8!>B'95Y!]9SYWYI  I
c%j3779i0 	 	*CGGGZ,HI	
 	#w'-2 	G4=) 	sww}a0 	3779b) 	&x5H'IJ 	#7W8C;QWUW 	Xc+>?E2 	 )R7C<#3
  	"8\:`hxQY>Z`^`%h@FB&x1ABHb%h0JKQr	
!, 	09KR1PQ-. 	2377;OQS3TU/0 	n-312 	x67=234   9 G jB
,s   ".WBW"AW%analysis_timec                    | rt         j                         syt        j                  t               }|j	                         }	 t        j                  | j                  dd            }|j                  d      j                  d      }|j                  d|f       |j                         d   }|j                          |S #  | }Y >xY w)z.Count items collected since the last analysis.r   Z+00:00N)tzinfoz%Y-%m-%dT%H:%M:%Sz7SELECT COUNT(*) FROM trend_items WHERE collected_at > ?)rJ   rK   rL   rM   rN   r   rQ   r2   rR   rO   rP   rS   )r   rU   rN   analysis_dtanalysis_strcounts         r!   count_items_since_analysisr     s     0??7#D[[]F%,,]-B-B3-QR"**$*7@@ATU NNA	 OOa EJJLL%$s   AC   Cc            
         	 ddl m} m} i } |        j                         D ]W  \  }} ||      }||j                  |j
                  |j                  |j                  |j                         |d   |d   d||<   Y |S # t        $ r i cY S w xY w)z.Load channel configurations for the dashboard.r   )get_enabled_channelsget_channel_stats
item_countrD   )r   name
short_namer   colorr[   r   rD   )
channel_configr   r   r1   r   r   r   r   get_all_sourcesImportError)r   r   channelsch_idchrT   s         r!   load_channels_configr     s    J-/557 	IE2%e,E mm!~~--/#L1!&!7	HUO	  	s   A:A= =B
Bc                      g } t         D ]1  }t        d| dz  }|j                         s!| j                  |       3 t        dz  }|j                         r| s| j                  |       | S )a  
    Find all channel-specific opportunity files.

    Returns files like:
    - analysis_opportunities_how_things_work.json
    - analysis_opportunities_brain_is_lying.json

    But NOT timestamped backups like:
    - analysis_opportunities_how_things_work_20260212_123456.json
    analysis_opportunities_z.jsonzanalysis_opportunities.json)CHANNEL_IDS
OUTPUT_DIRrK   r/   )files
channel_idpathgeneric_paths       r!   get_channel_opportunity_filesr   2  sm     E! 
5j\GG;;=LL  ==LU\"Lr#   channel_filterc           	      $   t        d       | rt        d|         t               }t        d|d   dd       t               }|rt        dt        |              t	               }t        dt        |              |D ]  }t        d	|j
                           g }d
}ddddd}i }d}	|D ]  }
t        |
d      5 }t        j                  |      }d
d
d
       j                  d      }|j                  d      xs |j                  d      }|r	|||kD  r|}|j                  dg       }| r|| k7  r|rt        |      ||<   |D ]k  }|r|j                  d      s||d<   t        ||	      }|j                  d      s|r||d<   |j                  |       |	dz  }	|d   }||v s_||xx   dz  cc<   m 
 |j                  d d       t        |      D ]  \  }}t        |      |d<    t        dt        |              |j                         D ]  \  }}t        d	| d|         |r:	 t!        j"                  |j%                  dd            }|j'                  d      |d<   |rt)        |      nd|d!<   t        |       d"g}dD ]4  }||   dkD  s|j                  ||    d#|j%                  d$d#              6 d%j+                  |      |d&<   t        d'|d&           d
}| r	| |v r||    }||||d(}t        t,        d)      5 }t        j.                  ||d*+       d
d
d
       t        d,t,                t        d-t0         d.       t        d/t0                y
# 1 sw Y   xY w#  |r|d
d nd |d<   Y xY w# 1 sw Y   bxY w)0z?Generate dashboard data, merging all channel opportunity files.zGenerating dashboard data...z  Channel filter: z  Database: rB   ,z itemsz  Channels configured: z  Opportunity files found: z    - Nr   )HIGH_PRIORITYWORTH_MAKINGCONDITIONALSKIPrr   r}   analysis_timestampopportunitiesr   r;   r   c                 &    | j                  dd      S )Nrv   r   )r   )xs    r!   <lambda>zmain.<locals>.<lambda>  s    QUU7A%6 r#   T)r   reverser   z  Total opportunities: z: r   r   rG   rD   rH   rI   rE   z opportunitiesr'   r&   z, rF   z  Summary: )rT   r   r   current_channelw   )indentz
Dashboard data written to: zOpen: file://z/dashboard.htmlz.  Or: python3 -m http.server 8000 --directory )printrX   r   r4   r   r   openjsonloadr   r   r/   sortr   r   r1   r   rQ   r2   rR   r   r0   DASHBOARD_DATA_PATHdumpr   )r   rT   r   	opp_filesfr   r   verdict_countschannel_opp_countsglobal_indexopp_filedatafile_channel_idfile_analysis_timeraw_oppsrY   transformedvr   r   r   rW   r7   r   dashboard_datas                            r!   r   r   L  s   	
()">"234  E	L}-a0
78 $%H'H78 ./I	'I'7
89 !qvvh ! MM'(!AWXYNL %'(C  	 A99Q<D	  ((<0!XXk2Tdhh?S6T $(:](J 288OR0 o? 25h-/ 	'Cswwy'9!0I/\BK??9-/)8I&  -ALI&AN"q!Q&!	'-%'P 6E M* 3FD	 
#C$6#7
89*002 )uugRw'() 	X''(=(=c8(LMB%'[[1A%BE/" GT3MBYZE+ M"#>23EE G!q LLN1-.a		#s0C/DEFG yy'E)	Ki()
*+ O.H4">2 &*	N 
!3	' /1		.!A./ 
)*=)>
?@	M*_
56	::,
GHm	  	 l	X;H]3B%7iE/"4/ /s$   =M%09M2 N%M/	2NN__main__zGenerate dashboard data)r   z	--channelzFilter to a specific channel)typehelp)r   )N)$__doc__argparser   rL   pathlibr   r   typingr   r   r   __file__parentr   rJ   r   r   r"   r   r5   r?   r   rX   r   r   r   r   r   r   __name__ArgumentParserparseradd_argument
parse_argsargsr    r#   r!   <module>r     s9        ' '(^""X-

*
* #88  "$ *   F;D D}t }C }D }@c c .d .tDz 4sI# sIl z$X$$1JKF
+  
 D% r#   