
    i{                        d Z ddlZddlZddl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mZ ddlmZmZmZ ddlmZ ddlmZmZmZ dd	lmZ dd
lmZ ddlmZ ddlZ e	e       jB                  dz  Z"dZ# ejH                  d      Z% G d de&e      Z' G d de&e      Z( G d de&e      Z) G d de      Z* G d de      Z+ G d de      Z, G d de      Z- G d de      Z. G d d e      Z/ G d! d"e      Z0e G d# d$             Z1 e1       Z2d%ee&e	f   fd&Z3d'e&d%ee   fd(Z4d)ed%e'fd*Z5d+ee   d%e*fd,Z6d)ed-e7d'e&d%e-fd.Z8d'e&d/ed%ee   fd0Z9d1e&d'e&d2e7fd3Z:d1e&d'e&d2e7fd4Z; ed5d6d78      Z<e<j{                  ed9gd:d9gd9g;       e<j}                  d<      d=        Z?e<j}                  d>      d?        Z@e<j}                  d@ee+   A      dB        ZAe<j}                  dC      	 	 	 	 	 	 dgd'e&dDee'   dEeBdFeBdGee&   dHe7dIe7fdJ       ZCe<j}                  dK      d'e&d-e7fdL       ZDe<j}                  dM      d'e&d-e7fdN       ZEe<j}                  dO      d'e&d-e7fdP       ZFe<j}                  dQ      dhdReGfdS       ZHe<j}                  dT      d1e&fdU       ZIe<j                  dM      d'e&d-e7dVefdW       ZKe<j                  dO      d'e&d-e7dVefdX       ZLe<j                  dY      dZee/   dVefd[       ZMe<j}                  d\      d'e&d-e7fd]       ZNe<j}                  d^      d'e&d-e7fd_       ZOePd`k(  r4 eQda        eQdbe"         eQdce#         ej                  e<dddef       yy)ia  
Video Pipeline Mission Control - API Server

Serves pipeline data to the dashboard frontend.
Provides endpoints for:
- Channel listing with stats
- Opportunity browsing and filtering
- Production package details
- Job triggering (research, script generation)
    N)datetime)Path)AnyDictListOptional)	dataclassfieldasdict)Enum)FastAPIHTTPExceptionBackgroundTasks)CORSMiddleware)JSONResponse)	BaseModeloutputzhttp://127.0.0.1:8100z'analysis_opportunities_([a-z_]+)\.json$c                        e Zd ZdZdZdZdZdZy)Stage
DISCOVEREDANALYZED
RESEARCHEDSCRIPTEDPRODUCTION_READYN)__name__
__module____qualname__r   r   r   r   r        4/home/sietch6/trending-topics-pipeline/api_server.pyr   r   ,   s    JHJH)r   r   c                       e Zd ZdZdZy)JobTypeRESEARCHSCRIPTN)r   r   r   r#   r$   r   r   r    r"   r"   3   s    HFr   r"   c                       e Zd ZdZdZdZdZy)	JobStatusPENDINGRUNNING	COMPLETEDFAILEDN)r   r   r   r'   r(   r)   r*   r   r   r    r&   r&   7   s    GGIFr   r&   c                   J    e Zd ZU eed<   eed<   eed<   eed<   eed<   eed<   y)ChannelStatstotal
high_score
researchedscriptedready	avg_scoreN)r   r   r   int__annotations__floatr   r   r    r,   r,   >   s!    JOOMJr   r,   c                   @    e Zd ZU eed<   eed<   dZee   ed<   eed<   y)ChannelidnameNdescriptionstats)r   r   r   strr4   r:   r   r,   r   r   r    r7   r7   F   s     G
I!%K#%r   r7   c                   J    e Zd ZU eed<   eed<   eed<   eed<   eed<   eed<   y)ScoreBreakdownscroll_stop_powercompletion_probabilityshare_save_potentialdemand_signalvisual_potentialevergreen_potentialN)r   r   r   r3   r4   r   r   r    r>   r>   L   s&    r   r>   c                       e Zd ZU eed<   eed<   eed<   eed<   eed<   eed<   eed<   dZe	e   ed	<   dZ
e	e   ed
<   dZe	e   ed<   g Zee   ed<   dZe	e   ed<   dZe	e   ed<   y)OpportunitySummaryr8   index
channel_idtitlepremisestageweighted_scoreNverdictresearch_word_countscript_scorehooksscoreslast_updated)r   r   r   r<   r4   r3   r   r5   rM   r   rN   rO   rP   r   rQ   r>   rR   r   r   r    rF   rF   T   s|    GJOJLL!GXc]!)-#-$(L(5/(E49'+FH^$+"&L(3-&r   rF   c                   |    e Zd ZU eed<   eed<   eed<   eed<   eed<   eed<   eed<   dZe	e
   ed	<   dZe	e   ed
<   y)Jobr8   typerH   opportunity_indextarget_namestatus
started_atNduration_secondserror)r   r   r   r<   r4   r"   r3   r&   rZ   r   r5   r[   r   r   r    rT   rT   c   sE    G
MOO(,huo,E8C=r   rT   c                   "    e Zd ZU eed<   eed<   y)ResearchRequestrH   rV   Nr   r   r   r<   r4   r3   r   r   r    r]   r]   n       Or   r]   c                   "    e Zd ZU eed<   eed<   y)ScriptRequestrH   rV   Nr^   r   r   r    ra   ra   r   r_   r   ra   c            
           e Zd ZU  ee      Zeeef   ed<    ee	j                        Ze	j                  ed<   dZeed<   dededed	ed
ef
dZddededee   fdZded
ee   fdZd
ee   fdZdded
ee   fdZy)
JobTracker)default_factoryjobs_lockr   _counterjob_typerH   rV   rW   returnc                 \   | j                   5  | xj                  dz  c_        d| j                   dt        t        j                                }|||||t        j
                  t        j                         j                         d d d	| j                  |<   |cd d d        S # 1 sw Y   y xY w)N   job__)	r8   rU   rH   rV   rW   rX   rY   rZ   r[   )
rf   rg   r3   timer&   r'   r   now	isoformatre   )selfrh   rH   rV   rW   job_ids         r    
create_jobzJobTracker.create_job   s    ZZ 	MMQMDMM?!C		,<+=>F (%6*#++&lln668$(
!DIIf 	 	 	s   BB""B+Nrr   rX   r[   c                    | j                   5  || j                  v r|| j                  |   d<   |r|| j                  |   d<   |t        j                  t        j                  fv rZt        j                  | j                  |   d         }t        j                         |z
  j                         | j                  |   d<   d d d        y # 1 sw Y   y xY w)NrX   r[   rY   rZ   )	rf   re   r&   r)   r*   r   fromisoformatro   total_seconds)rq   rr   rX   r[   starteds        r    update_statuszJobTracker.update_status   s    ZZ 	g".4		&!(+16DIIf%g.i1193C3CDD&44TYYv5F|5TUG=E\\^g=U<d<d<fDIIf%&89	g 	g 	gs   B1CCc                 8    | j                   j                  |      S N)re   get)rq   rr   s     r    get_jobzJobTracker.get_job   s    yy}}V$$r   c                     | j                   j                         D cg c]*  }|d   t        j                  t        j                  fv s)|, c}S c c}w )NrX   )re   valuesr&   r'   r(   )rq   js     r    get_active_jobszJobTracker.get_active_jobs   s@    99++-ga8ARART]TeTe@f1fgggs   *AAlimitc                 Z    t        | j                  j                         d d      }|d | S )Nc                     | d   S )NrY   r   xs    r    <lambda>z,JobTracker.get_recent_jobs.<locals>.<lambda>   s
    q r   Tkeyreverse)sortedre   r~   )rq   r   sorted_jobss      r    get_recent_jobszJobTracker.get_recent_jobs   s,    TYY--/5NX\]6E""r   rz   )   )r   r   r   r
   dictre   r   r<   r4   	threadingLockrf   rg   r3   r"   rs   r&   r   rx   r|   r   r   r   r   r   r    rc   rc   z   s    !$7D$sDy/7!)..AE9>>AHc7  PS be jm "gC g g8C= g%c %htn %hd h#S #$t* #r   rc   ri   c                      i } t         j                  d      D ][  }t        j                  |j                        }|s%|j                  d      }t        j                  d|j                        rW|| |<   ] | S )z Find all channel analysis files.zanalysis_opportunities_*.jsonrk   z_\d{8}_\d{6}\.json$)
OUTPUT_DIRglobCHANNEL_FILE_PATTERNmatchr9   groupresearch)channelsfiler   rH   s       r    get_channel_filesr      sh    H ?@ ,$**4995QJ993TYY?'+$, Or   rH   c                     t         d|  dz  }|j                         sy	 t        |d      5 }t        j                  |      cddd       S # 1 sw Y   yxY w# t        j
                  $ r Y yw xY w)z&Load opportunities data for a channel.analysis_opportunities_.jsonNr)r   existsopenjsonloadJSONDecodeError)rH   	file_pathfs      r    load_channel_datar      sm    6zl%HHI)S! 	 Q99Q<	  	  	  s.   A A	A AA A A-,A-oppc                 n   | j                  d      rt        j                  S | j                  d      s| j                  d      rt        j                  S | j                  d      s| j                  d      rt        j                  S | j                  d      t        j
                  S t        j                  S )z/Determine the pipeline stage of an opportunity.production_readyscript
evaluationresearch_completedresearch_report_contentrL   )r{   r   r   r   r   r   r   )r   s    r    determine_stager      s    
ww!"%%%
wwxCGGL1~~
ww#$0I(J
ww ,~~r   opportunitiesc                    t        |       }t        d | D              }t        d | D              }t        d | D              }t        d | D              }| D cg c]&  }|j                  d      s|j                  dd      ( }}|rt        |      t        |      z  nd}t        |||||t	        |d            S c c}w )	z,Calculate stats for a list of opportunities.c              3   R   K   | ]  }|j                  d       xs ddk\  sd ! yw)rL   r   g      @rk   Nr{   .0os     r    	<genexpr>z$get_channel_stats.<locals>.<genexpr>   s'     W1!%%8H2I2NQSV1VQWs   ''c              3   f   K   | ])  }|j                  d       s|j                  d      s&d + yw)r   r   rk   Nr   r   s     r    r   z$get_channel_stats.<locals>.<genexpr>   s+     q17K1LPQPUPUVoPpQq   '11c              3   f   K   | ])  }|j                  d       s|j                  d      s&d + yw)r   r   rk   Nr   r   s     r    r   z$get_channel_stats.<locals>.<genexpr>   s%     VquuX!%%BU1Vr   c              3   D   K   | ]  }|j                  d       sd  yw)r   rk   Nr   r   s     r    r   z$get_channel_stats.<locals>.<genexpr>   s     FaAEE2D,EFs     rL   r      )r-   r.   r/   r0   r1   r2   )lensumr{   r,   round)	r   r-   r.   r/   r0   r1   r   rQ   r2   s	            r    get_channel_statsr      s    EWWWJqqqJVmVVHF=FFE2?[Q155IYCZaee$a([F[-3Fc&k)I	1%  \s   B;/B;rG   c                    | j                  di       }d}|rt        t        |j                  d      t              r"|j                  di       j                  dd      n|j                  dd      t        |j                  d      t              r"|j                  di       j                  dd      n|j                  dd      t        |j                  d      t              r"|j                  di       j                  dd      n|j                  dd      t        |j                  d      t              r"|j                  di       j                  dd      n|j                  dd      t        |j                  d	      t              r"|j                  d	i       j                  dd      n|j                  d	d      t        |j                  d
      t              r"|j                  d
i       j                  dd      n|j                  d
d            }| j                  dg       }g }|D ]  }t        |t              r|j                  |       %t        |t              s6|j                  d      xs% |j                  d      xs |j                  dd      }|sq|j                  |        | j                  d      }	|	r*t        |	t              r|	g|D cg c]
  }||	k7  s	| c}z   }| j                  d      }
|
s-| j                  d      rt        | d   j                               }
d}| j                  d      r| d   j                  d      }t        | d| ||| j                  d| j                  dd            | j                  d| j                  dd            t        |       | j                  dd      | j                  d      |
||dd |      S c c}w )z.Convert raw opportunity dict to summary model.rQ   Nr?   scorer   r@   rA   rB   rC   rD   )r?   r@   rA   rB   rC   rD   hook_candidatesopening_hookhooksource_post_title rN   r   r   composite_score-suggested_titlecluster_labelUntitledrJ   r:   rL   rM      )r8   rG   rH   rI   rJ   rK   rL   rM   rN   rO   rP   rQ   )
r{   r>   
isinstancer   r<   appendr   splitrF   r   )r   rG   rH   rQ   score_breakdown	raw_hooksrP   h	hook_textr   rN   rO   s               r    opportunity_to_summaryr      sD   WWXr"FO(U_`f`j`jk~`  BF  VGfjj)<bAEEgqQ  MS  MW  MW  Xk  mn  Mo_ijpjtjt  vN  kO  QU  `V6::.F#K#O#OPWYZ#[  \b  \f  \f  g  AB  \C[eflfpfp  rH  gI  KO  \P,BB!G!K!KGUV!W  V\  V`  V`  aw  yz  V{MWX^XbXbcrXsuyMz&**_b9==gqI  AG  AK  AK  L[  ]^  A_S]^d^h^hi{^|  C  TDVZZ(:B?CCGQO  JP  JT  JT  Ug  ij  JkYcdjdndn  pE  eF  HL  ZM

+@" E I I'ST U  SY  S]  S]  ^s  uv  Sw
 )2.IE (aLLO4 n-`v`!%%H[]_B`IY'( 77>*L
<5U!Ha<6G!!HH ''"78377+D#E!#&?"@"F"F"HI L
ww|<(,,->?\5'"gg'*)MN	377="#=>c"ww/3	"/!BQi  "Is   
OOopportunityc                     t         | z  }|j                         sy|j                  dd      }|j                  d      D ]  }	 t	        |d      5 }t        j                  |      }|j                  di       j                  d      |j                  d      k(  r|cddd       c S |j                  di       j                  d	d      j                         |j                         v r|cddd       c S 	 ddd        y# 1 sw Y   xY w#  Y xY w)
z8Load production package for an opportunity if it exists.Nr   r   z*.jsonr   input
cluster_idmetarJ   )r   r   r{   r   r   r   r   lower)rH   r   channel_dirrI   r   r   pkgs          r    load_production_packager     s   z)K OO-r2E  * 
		dC Aiil777B'++L9[__\=ZZ	 
 7762&**9b9??AU[[]R 
 S
  	s8   D	A
C=	D	$AC=%	D	2D	=D	D		Drr   rV   c                    ddl }t        j                  | t        j                         	 t        |      }|r|t        |j                  dg             k\  rt        d| d| d      |d   |   }|j                  d|j                  dd	            }|j                  t         d
|ddddd      }|j                  dk7  rt        d|j                         |j                         }|j                  d      }		 |j                  t         d|	 d      }
|
j                         }|j                  d      dk(  s|j                  d      r|j                  d|j                  di       j                  dd	            }||d<   d|d<   t        |j                               |d<   t!        j"                         j%                         |d<   t&        d| d z  }t)        |d!      5 }t        j*                  ||d"#       ddd       t        j                  | t        j,                         y|j                  d      d$k(  s|j                  d%      rt        |j                  d%d&            t/        j0                  d'       # 1 sw Y   xY w# t        $ r8}t        j                  | t        j2                  t5        |             Y d}~yd}~ww xY w)(z2Run research for an opportunity in the background.r   Nr   Opportunity not found: []r   rJ   r   z	/researchresearch_report   iX  )queryreport_typemax_resultstimeout)r   r      zResearch service error: rr   Tz
/research/r   rX   	completedsuccessreportresultr   r   rN   research_completed_atr   r   wr   indentfailedr[   zResearch failed   )requestsjob_trackerrx   r&   r(   r   r   r{   
ValueErrorpostRESEARCH_SERVICE_URLstatus_code	Exceptiontextr   r   r   ro   rp   r   r   dumpr)   rn   sleepr*   r<   )rr   rH   rV   r   datar   r   responser   research_idstatus_responserX   research_contentr   r   es                   r    run_research_jobr  5  s   fi&7&786D ,(C"0M,NN6zl!DUCVVWXYY?#$56)3779b+AB ==#$I.0!	  ! 	
 3&6x}}oFGGjj* &ll.B-C:k]+[eglhO$))+Fzz(#{2fjj6K#)::h

8R8P8T8TU]_a8b#c 1A-.,0()-01A1G1G1I-J)*/7||~/G/G/I+, '+B:,e)TT	)S) 1QIIdAa01 ))&)2E2EFzz(#x/6::g3F

74E FGGJJqM/ 1 1  D!!&)*:*:CFCCDs7   F7J
 !I>:,J
 'AJ
 >JJ
 
	K.KKc                    t         j                  | t        j                         	 ddl}|j
                  j                  dt        t        t              j                               ddlm} t        |      }|r|t        |j                  dg             k\  rt!        d| d| d      |d   |   }|j                  d      s|j                  d	      st!        d
       |||      }|j                  d      r|j                  d      |d<   |j                  d      |d<   |j                  di       j                  d      dk(  |d<   |j                  d      |d<   t#        j$                         j'                         |d<   t(        d| dz  }t+        |d      5 }	t-        j.                  ||	d       ddd       t         j                  | t        j0                         yt3        |j                  dd            # 1 sw Y   IxY w# t2        $ r8}
t         j                  | t        j4                  t        |
             Y d}
~
yd}
~
ww xY w)z;Run script generation for an opportunity in the background.r   N)#layer4_run_director_evaluator_chainr   r   r   r   r   r   z7Opportunity must be researched before script generation)rH   r   r   r   rM   r   r   all_iterationslayer4_completed_atr   r   r   r   r   r[   zScript generation failed)r   rx   r&   r(   syspathinsertr<   r   __file__parentvideo_generationr  r   r   r{   r   r   ro   rp   r   r   r   r   r)   r   r*   )rr   rH   rV   r
  r  r   r   r   r   r   r  s              r    run_script_jobr  s  s   fi&7&78&D3tH~4456H !,(C"0M,NN6zl!DUCVVWXYY?#$56 ww+,SWW=V5WVWW 5SZP::i "JJx0CM &

< 8C&,jjr&B&F&Fy&QUg&gC"#$*JJ/?$@C !)1)A)A)CC%& #'>zl%%PPIi% -		$!,- %%fi.A.ABFJJw0JKLL- -  D!!&)*:*:CFCCDs6   E4H G;3,H  H ;H H 	I.IIz"Video Pipeline Mission Control APIz4Backend API for the video content pipeline dashboardz1.0.0)rI   r:   version*T)allow_originsallow_credentialsallow_methodsallow_headersz/api/healthc                     K   ddl } d}	 | j                  t         dd      }|j                  dk(  rd}d	t	        j
                         j                         d|d
dS #  Y -xY ww)zSystem health check.r   Nofflinez/healthr   r   r   onlinehealthy)apiresearch)rX   	timestampservices)r   r{   r   r   r   ro   rp   )r   research_statusresps      r    health_checkr!    sx      O||34G<a|Hs"&O
 \\^--/'
 s   A%+A )A%A" A%z
/api/statsc                    K   t               } d}d}d}d}d}g }| j                         D ]  \  }}t        |      }	|	sd|	v s|	d   }
|t        |
      z  }|
D ]  }|j	                  d      |dz  }|j                  |d          |j	                  d      s|j	                  d      r|dz  }|j	                  d      s|j	                  d      r|dz  }|j	                  d	      s|dz  }  |rt        |      t        |      z  nd}t        |       |||rt        ||z  d
z  d      nd||rt        ||z  d
z  d      nd||rt        ||z  d
z  d      nd||rt        ||z  d
z  d      ndt        |d      dS w)zGet pipeline-wide statistics.r   r   rL   rk   r   r   r   r   r   d   r   )total_channelstotal_opportunitiesanalyzedanalyzed_pctr/   researched_pctr0   scripted_pctr   	ready_pctr2   )r   itemsr   r   r{   r   r   r   )r   r%  total_analyzedtotal_researchedtotal_scriptedtotal_ready
all_scoresrH   r   r   oppsr   r2   s                r    pipeline_statsr2    s     !"HNNKJ!)!1 %
I ,Ot+(D3t9, 	%77+,8"a'N%%c*:&;<77/0CGG<U4V$)$778$(="a'N77-.1$K	%%  6@J#j/1QI h-2"Pcn/BBSH!Lij&Tg% 03F F LaPmn"Pcn/BBSH!Lij'J]U;)<<sBAFcd9a( s   :E9E9B!E9$BE9z/api/channels)response_modelc            
        K   t               } g }t        | j                               D ]  \  }}t        |      }|sd|v s|d   }t	        |      }|j                  d|j                  d            }|j                  t        ||j                  dd      j                         ||              |j                  d d	       |S w)
z(List all channels with their statistics.r   channel_descriptionr:   rm    )r8   r9   r:   r;   c                 .    | j                   j                  S rz   )r;   r2   r   s    r    r   zlist_channels.<locals>.<lambda>  s    agg// r   Tr   )r   r   r+  r   r   r{   r   r7   replacerI   sort)r   r   rH   r   r   r1  r;   r:   s           r    list_channelsr:    s      !"HF!'(8!9 
I ,Ot+(D%d+E ((#8$((=:QRKMM'''S1779'	 " KK/K>Ms   9CCBCz(/api/channels/{channel_id}/opportunitiesrK   	min_score	max_scorer   r   offsetc           	        K   t        |       }|st        dd|        |j                  dg       }t        |      D 	
cg c]  \  }	}
t	        |
|	|        }}	}
|r|D cg c]  }|j
                  |k(  s| }}|D cg c]  }||j                  cxk  r|k  sn n| }}|rX|j                         }|D cg c]=  }||j                  j                         v s||j                  j                         v s<|? }}|j                  d d       t        |      }||||z    }| ||||D cg c]  }|j                          c}dS c c}
}	w c c}w c c}w c c}w c c}w w)	z0List opportunities for a channel with filtering.  Channel not found: r   detailr   c                     | j                   S rz   )rL   r   s    r    r   z$list_opportunities.<locals>.<lambda>1  s    !1!1 r   Tr   )rH   r-   r=  r   r   )r   r   r{   	enumerater   rK   rL   r   rI   rJ   r9  r   r   )rH   rK   r;  r<  r   r   r=  r   r   ir   	summariesssearch_lowerr-   s                  r    list_opportunitiesrI    sj     Z(D6I*4VWWHH_b1M KTTaJbc3'Q
;cIc  )>1QWW-=Q>	>%Tqa6F6F)S))STIT||~ )r1\QWW]]_-LP\`a`i`i`o`o`qPqQr	r NN14N@ 	NE&%0I !,56q!&&(6 ' d ?T s 7sS   >E" E	E" E5E9E"?EE!E"9=E7E;1E",EE"z'/api/opportunities/{channel_id}/{index}c                   K   t        |       }|st        dd|        |j                  dg       }|dk  s|t        |      k\  rt        dd|       ||   }t	        |||       }|j                         }|j                  di       |d<   |j                  d	      |d	<   |j                  d
      |d
<   |j                  d      |d<   |j                  d      |d<   |j                  dg       |d<   |j                  d      r5|d   t        |d   j                               |j                  d      d|d<   |j                  d      r|d   |d<   |j                  d      r|d   |d<   |j                  d      r|d   |d<   |j                  d      r|d   |d<   t        | |      }|r'd|j                  d      |j                  d      d|d<   |S w)z=Get full opportunity details including script and evaluation.r?  r@  rA  r   r   r   rQ   full_scorescore_revealfirst_frame	structuretarget_audiencer   	all_hooksr   r   content
word_countcompleted_atr  r   r   r  
iterationsnarration_scriptTr   )	availabler   rV  production_package)r   r   r{   r   r   r   r   r   )rH   rG   r   r   r   summaryr   r   s           r    get_opportunityrZ  ?  s     Z(D6I*4VWWHH_b1MqyES//6MeW4UVV

C$S%<G ||~H "ggh3H]!ggm4H]!ggm4H]GGK0H["%''*;"<HGG$5r:H[ ww()45c";<BBDEGG$;< 
 wwx ] ww|!$\!2 ww !$%5!6 ww!"'*+='>#$ "*c
2C
GGFO #(: ;*
%& Os   GGz0/api/opportunities/{channel_id}/{index}/researchc                 X  K   t        |       }|st        dd|        |j                  dg       }|dk  s|t        |      k\  rt        dd|       ||   }|j                  d      st        dd      |d   t        |d   j	                               |j                  d	      d
S w)z(Get research content for an opportunity.r?  r@  rA  r   r   r   r   zResearch not availabler   rQ  )r   r   r{   r   r   rH   rG   r   r   r   s        r    get_opportunity_researchr]  {  s      Z(D6I*4VWWHH_b1MqyES//6MeW4UVV

C77,-4LMM 01#78>>@A 78 s   B(B*z./api/opportunities/{channel_id}/{index}/scriptc                   K   t        |       }|st        dd|        |j                  dg       }|dk  s|t        |      k\  rt        dd|       ||   }|j                  d      st        dd      |d   |j                  d	      |j                  d
      |j                  d      |j                  dd      dS w)z-Get script and evaluation for an opportunity.r?  r@  rA  r   r   r   r   Script not availabler   rV  r  r   F)r   r   rV  r  r   )r   r   r{   r   r\  s        r    get_opportunity_scriptr`    s      Z(D6I*4VWWHH_b1MqyES//6MeW4UVV

C7784JKK h-ggl+GG$67''"23GG$6> s   B>C z	/api/jobsactive_onlyc                 n   K   | rt         j                         }d|iS t         j                         }d|iS w)z
List jobs.re   )r   r   r   )ra  re   s     r    	list_jobsrc    s?      **, D> **,D>s   35z/api/jobs/{job_id}c                 V   K   t         j                  |       }|st        dd      |S w)zGet job status.r?  zJob not foundrA  )r   r|   r   )rr   jobs     r    r|   r|     s-      

f
%CODDJs   ')background_tasksc                   K   t        |       }|st        dd|        |j                  dg       }|dk  s|t        |      k\  rt        dd|       ||   }|j                  d      s|j                  d      rd	d
dS |j                  d|j                  dd            dd }t        j                  t        j                  | ||      }|j                  t        || |       |ddS w)z$Trigger research for an opportunity.r?  r@  rA  r   r   r   r   r   zAlready researchedskipped)messagerX   r   rJ   UnknownN2   rw   rr   rX   )
r   r   r{   r   r   rs   r"   r#   add_taskr  rH   rG   rf  r   r   r   rW   rr   s           r    trigger_researchro    s      Z(D6I*4VWWHH_b1MqyES//6MeW4UVV

C ww#$0I(J/9EE ''+SWWY	-JKCRPK##G$4$4j%UF .
EJ	22s   C'C)c                   K   t        |       }|st        dd|        |j                  dg       }|dk  s|t        |      k\  rt        dd|       ||   }|j                  d      s|j                  d      st        d	d
      |j                  d|j                  dd            dd }t        j                  t        j                  | ||      }|j                  t        || |       |ddS w)z-Trigger script generation for an opportunity.r?  r@  rA  r   r   r   r   r   i  z&Must research before generating scriptr   rJ   rj  Nrk  rw   rl  )
r   r   r{   r   r   rs   r"   r$   rm  r  rn  s           r    trigger_scriptrq    s      Z(D6I*4VWWHH_b1MqyES//6MeW4UVV

C 77'(9R1S4\]] ''+SWWY	-JKCRPK##GNNJ{SF nfj%H	22s   C/C1z/api/batch/researchr   c                 |  K   g }| D ]#  }t        |j                        }|s|j                  dg       }|j                  dk  s|j                  t	        |      k\  rV||j                     }|j                  d      s|j                  d      r|j                  d|j                  dd            dd	 }t
        j                  t        j                  |j                  |j                  |      }|j                  t        ||j                  |j                         |j                  |       & |t	        |      d
S w)z,Trigger research for multiple opportunities.r   r   r   r   r   rJ   rj  Nrk  )job_idscount)r   rH   r{   rV   r   r   rs   r"   r#   rm  r  r   )	r   rf  rs  reqr   r   r   rW   rr   s	            r    batch_researchrv    s     G  0"5  1$(=(=]AS(SC112 77'(CGG4M,Ngg/I1NOPSQST''(8(8#..#J_J_alm!!"2FCNNCLaLabv%( W66s   D:D<z8/api/opportunities/{channel_id}/{index}/export/narrationc                 &  K   t        |       }|st        dd|        |j                  dg       }|dk  s|t        |      k\  rt        dd|       ||   }|j                  d      st        dd      |d   }g }|j                  d	g       D ]?  }|j                  d
g       D ](  }|j                  d      s|j	                  |d          * A dj                  |      }	|j                  dd      |	t        |	j                               dS w)z&Export narration script as plain text.r?  r@  rA  r   r   r   r   r_  beatssegments	voiceoverz

r   r   )rI   	narrationrS  )r   r   r{   r   r   joinr   )
rH   rG   r   r   r   r   narration_linesbeatsegmentr{  s
             r    export_narrationr    s&     Z(D6I*4VWWHH_b1MqyES//6MeW4UVV

C7784JKK ]FO

7B' =xx
B/ 	=G{{;'&&w{';<	==
 O,I *J7)//+, s   B8D;ADz6/api/opportunities/{channel_id}/{index}/export/packagec                   K   t        |       }|st        dd|        |j                  dg       }|dk  s|t        |      k\  rt        dd|       ||   }| |j                  d      |j                  d      |j                  d	      t	        j
                         j                         d
|j                  dg       |j                  d      |j                  d      |j                  d      dd}|j                  d      r%|d   t        |d   j                               d|d<   |j                  d      r|d   |d<   |j                  d      r|d   |d<   |S w)zExport full production package.r?  r@  rA  r   r   r   r   rJ   rL   )channelrI   rJ   rL   exported_atr   rQ   rL  rN  )rP   rQ   rL  rN  )r   r   r   )rR  rS  r  r   r   )r   r   r{   r   r   ro   rp   r   )rH   rG   r   r   r   packages         r    export_packager  3  sf     Z(D6I*4VWWHH_b1MqyES//6MeW4UVV

C
 "WW./wwy)!gg&67#<<>335
 WW.3ggh'77=1-	
G  ww()45c";<BBDE


 wwxM
ww| #L 1Ns   EE__main__z.Starting Video Pipeline Mission Control API...zOutput directory: zResearch service: z0.0.0.0i   )hostport)Nr   r   Nr#  r   )F)S__doc__r   osr   
subprocessr   rn   r   pathlibr   typingr   r   r   r   dataclassesr	   r
   r   enumr   fastapir   r   r   fastapi.middleware.corsr   fastapi.responsesr   pydanticr   uvicornr  r  r   r   compiler   r<   r   r"   r&   r,   r7   r>   rF   rT   r]   ra   rc   r   r   r   r   r   r3   r   r   r  r  appadd_middlewarer{   r!  r2  r:  r5   rI  rZ  r]  r`  boolrc  r|   r   ro  rq  rv  r  r  r   printrunr   r   r    <module>r     s  	  	 	      , , 0 0  ; ; 2 *   (^""X-
.  "rzz"LM *C *c4 T 9 i Y ' '	 ) 	 i I  (# (# (#T l
4T	? 

# 
(4. 

 
% 
T$Z L (6 6S 6c 6FX 6p $ 8D> 4<DS <Dc <Dc <D|*D3 *DC *DC *D` 
.F   %%%    * ) )V g7 82 	34 " **E?* * 	*
 SM* * * 5*X 	239c 9# 9 49v 	;<s 3  =* 	9:S   ;6    	#   
<=3s 33 3/ 3 >32 
:;3S 3 3 3 <32 
 74#8 7O 7 !7< 	CDs 3  E@ 	AB)S ) ) C)^ z	
:;	zl
+,	34
56GKK)$/	 r   