
    siJ                        d Z ddlZddlZddlmZmZ ddlmZ ddlmZm	Z	m
Z
mZ  ee      j                  dz  dz  Zd Z	 	 	 d0d	ed
edededef
dZ	 	 	 d1dedededefdZd	ede
eee	f      fdZ	 d2d	ededeee	f   dedef
dZ	 d2d	ededeeee	f      dedee   f
dZ	 d3dededeee	f   fdZ	 d3dedededeee	f   fd Z	 	 	 d1ded!ed"eee	f   d#eee	f   d$eee	f   f
d%Z	 	 	 d1deded!ed"eee	f   d#eee	f   d$eee	f   fd&Z	 d3ded'ee   deeee	f      fd(Z	 	 d4d	ed)ed'ee   deeee	f      fd*Zdede
eee	f      fd+Z	 	 	 d5d	eded)edeeee	f      fd,Z	 	 	 	 d6d	eded-ed)edeeee	f      f
d.Z d	edeee	f   fd/Z!y)7z
Database operations for opportunities table.

This module handles all DB interactions for persisting Layer 3/3.5/4 analysis results.
Designed to be imported into analyze.py and video_generation.py.
    N)datetimetimezone)Path)DictAnyOptionalListoutputztrends_history.dbc                  d   t        j                  t              } | j                         }|j	                  d       |j                         r| j                          yt        t              j                  dz  dz  }|j                         r|j                         }|j                  d      D ]  }|j                  d      }|D cg c]5  }|j                         s|j                         j                  d      r4|7 }}dj                  |      j                         }|sv	 |j	                  |        | j'                          t)        d
       nt+        d|       | j                          yc c}w # t         j                   $ r&}	dt#        |	      j%                         vr Y d	}	~	d	}	~	ww xY w)z=Run the opportunities table migration if not already applied.z`
        SELECT name FROM sqlite_master
        WHERE type='table' AND name='opportunities'
    F
migrationsz001_add_opportunities_table.sql;
z--zduplicate columnNz7[DB] Migration 001 applied: opportunities table createdzMigration file not found: T)sqlite3connectDB_PATHcursorexecutefetchonecloser   __file__parentexists	read_textsplitstrip
startswithjoinOperationalErrorstrlowercommitprintFileNotFoundError)
connr   migration_pathmigration_sql	statementlinesl
code_linesclean_statementes
             :/home/sietch6/trending-topics-pipeline/db_opportunities.pyrun_migrationr.      sk   ??7#D[[]F NN  	 

 (^**\9<]]N&002&,,S1 	IOOD)E%*[aggi	@T@TUY@Z![J["ii
399;ONN?3	 	GH"<^<L MNNJJL! \ // )Q? @s*   E1E1:E1#E66F/	F**F/
channel_iditems_analyzedclusters_formedlayer2_passedreturnc                 @   t        j                  t              }|j                         }t	        j
                  t        j                        j                         }|j                  d| ||||f       |j                  }|j                          |j                          |S )zQ
    Create a new channel_analysis_runs record.

    Returns the new run ID.
    z
        INSERT INTO channel_analysis_runs
        (channel_id, run_date, items_analyzed, clusters_formed, layer2_passed, status)
        VALUES (?, ?, ?, ?, ?, 'running')
    )r   r   r   r   r   nowr   utc	isoformatr   	lastrowidr!   r   )r/   r0   r1   r2   r$   r   r5   run_ids           r-   create_analysis_runr:   >   s~     ??7#D[[]F
,,x||
$
.
.
0C
NN  c>?M	J	L FKKMJJLM    r9   opportunities_foundstatuserror_messagec                    t        j                  t              }|j                         }g }g }|"|j	                  d       |j	                  |       |w|j	                  d       |j	                  |       |dv rQ|j	                  d       |j	                  t        j                  t        j                        j                                |"|j	                  d       |j	                  |       |rF|j	                  |        |j                  ddj                  |       d	|       |j                          |j                          y)
z0Update an existing channel_analysis_runs record.Nzopportunities_found = ?z
status = ?)	completedfailedzcompleted_at = ?zerror_message = ?z!UPDATE channel_analysis_runs SET z, z WHERE id = ?)r   r   r   r   appendr   r5   r   r6   r7   r   r   r!   r   )r9   r<   r=   r>   r$   r   updatesparamss           r-   update_analysis_runrE   [   s    ??7#D[[]FGF&01)*|$f,,NN-.MM(,,x||4>>@A *+m$f/		'0B/C=Q	
 	JJLr;   c                     t        j                  t              }t         j                  |_        |j                         }|j                  d| f       |j                         }|j                          |rt        |      S y)z/Get the most recent analysis run for a channel.zo
        SELECT * FROM channel_analysis_runs
        WHERE channel_id = ?
        ORDER BY id DESC LIMIT 1
    N)
r   r   r   Rowrow_factoryr   r   r   r   dict)r/   r$   r   rows       r-   get_latest_analysis_runrK      sf    ??7#D{{D[[]F
NN  	 //
CJJL
Cyr;   analysis_run_idopportunityprompt_versionc                 8   t        j                  t              }|j                         }t	        j
                  t        j                        j                         }|j                  d      }|j                  dd      }|j                  dd      }	|j                  dt        |j                  dg                   }
|j                  dd      }|j                  dd	      }|j                  d
i       }|rt        j                  |      nd}g d}|D ci c]  }||v s||j                  |       }}|rt        j                  |      nd}|j                  d| ||||	|
||||||f       |j                  }|j                          |j!                          |S c c}w )z^
    Insert a new opportunity from Layer 3 analysis.

    Returns the new opportunity ID.
    
cluster_idtheme suggested_titletopic_counttopics_in_clusterverdictweighted_scorer   scoresNconfusion_analysisexisting_contentwhy_existing_failsmissing_angletarget_audienceopening_hookcritical_success_factor	structureverdict_reasoningrU   cluster_topicssourcesmerged_fromax  
        INSERT INTO opportunities (
            channel_id, analysis_run_id, cluster_id,
            theme, suggested_title, source_item_count, verdict, weighted_score,
            scores_json, analysis_json,
            research_status, video_gen_status,
            prompt_version, created_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', 'pending', ?, ?)
    )r   r   r   r   r   r5   r   r6   r7   getlenjsondumpsr   r8   r!   r   )r/   rL   rM   rN   r$   r   r5   rP   rQ   rS   source_item_countrV   rW   rX   scores_jsonanalysis_fieldskanalysis_dataanalysis_jsonopp_ids                       r-   insert_opportunityrq      st    ??7#D[[]F
,,x||
$
.
.
0C .JOOGR(E!oo&7<O#s;??K^`b;c7deooi,G __%5q9N __Xr*F(.$**V$DKO 5DXqqKGWQ**XMX1>DJJ}-DM
NN  	OZ 17N]	
 FKKMJJLM- Ys   	FFopportunitiesc                 l   t        j                  t              }|j                         }t	        j
                  t        j                        j                         }g }g d}|D ](  }	|	j                  d      }
|	j                  dd      }|	j                  dd      }|	j                  dt        |	j                  dg                   }|	j                  dd      }|	j                  d	d
      }|	j                  di       }|rt        j                  |      nd}|D ci c]  }||	v s||	j                  |       }}|rt        j                  |      nd}|j                  d| ||
|||||||||f       |j                  |j                         + |j!                          |j#                          |S c c}w )zj
    Insert multiple opportunities in a single transaction.

    Returns list of new opportunity IDs.
    rY   rP   rQ   rR   rS   rT   rU   rV   rW   r   rX   Na  
            INSERT INTO opportunities (
                channel_id, analysis_run_id, cluster_id,
                theme, suggested_title, source_item_count, verdict, weighted_score,
                scores_json, analysis_json,
                research_status, video_gen_status,
                prompt_version, created_at
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', 'pending', ?, ?)
        )r   r   r   r   r   r5   r   r6   r7   rf   rg   rh   ri   r   rB   r8   r!   r   )r/   rL   rr   rN   r$   r   r5   opp_idsrl   opprP   rQ   rS   rj   rV   rW   rX   rk   rm   rn   ro   s                        r-   insert_opportunities_batchrv      s    ??7#D[[]F
,,x||
$
.
.
0CGO  )WW\*
$''"3R8GGM3sww?RTV7W3XY'')R(!1152&,2djj(0?L118CGGAJLL5B

=1  ?$5wC	
	 	v''(;)> 	KKMJJLN/ Ms   	F1$F1opportunity_idresearch_statusresearch_datac                 V   t        j                  t              }|j                         }t	        j
                  t        j                        j                         }|rt        j                  |      nd}|j                  d|||| f       |j                          |j                          y)a  
    Update an opportunity with Layer 3.5 research results.

    Args:
        opportunity_id: DB ID of the opportunity
        research_status: 'completed', 'failed', or 'skipped'
        research_data: Dict containing research_report_content, metadata, etc.
    Nzz
        UPDATE opportunities
        SET research_status = ?, research_json = ?, updated_at = ?
        WHERE id = ?
    r   r   r   r   r   r5   r   r6   r7   rh   ri   r   r!   r   )rw   rx   ry   r$   r   r5   research_jsons          r-   update_opportunity_researchr}     s~     ??7#D[[]F
,,x||
$
.
.
0C1>DJJ}-DM
NN  =#~	>	@ 	KKMJJLr;   rP   c                 X   t        j                  t              }|j                         }t	        j
                  t        j                        j                         }|rt        j                  |      nd}|j                  d|||| |f       |j                          |j                          y)z
    Update opportunity research by cluster_id (for when we don't have DB ID).

    This is used when research runs after initial Layer 3 insert.
    Nz
        UPDATE opportunities
        SET research_status = ?, research_json = ?, updated_at = ?
        WHERE analysis_run_id = ? AND cluster_id = ?
    r{   )rL   rP   rx   ry   r$   r   r5   r|   s           r-   &update_opportunity_research_by_clusterr   5  s     ??7#D[[]F
,,x||
$
.
.
0C1>DJJ}-DM
NN  =#
	K	M 	KKMJJLr;   video_gen_statusvideo_conceptsproduction_specasset_specsc           	         t        j                  t              }|j                         }t	        j
                  t        j                        j                         }|rt        j                  |      nd}|rt        j                  |      nd}	|rt        j                  |      nd}
|j                  d|||	|
|| f       |j                          |j                          y)a6  
    Update an opportunity with Layer 4 video generation results.

    Args:
        opportunity_id: DB ID of the opportunity
        video_gen_status: 'completed', 'failed', or 'skipped'
        video_concepts: Layer 4a output
        production_spec: Layer 4b output
        asset_specs: Layer 4c output
    Nz
        UPDATE opportunities
        SET video_gen_status = ?,
            video_concepts_json = ?,
            production_spec_json = ?,
            asset_specs_json = ?,
            updated_at = ?
        WHERE id = ?
    r{   )rw   r   r   r   r   r$   r   r5   concepts_json	spec_jsonassets_jsons              r-   update_opportunity_video_genr   U  s    " ??7#D[[]F
,,x||
$
.
.
0C2@DJJ~.dM/>

?+DI-8$**[)dK
NN  M9k3	WY 	KKMJJLr;   c           
         t        j                  t              }|j                         }t	        j
                  t        j                        j                         }|rt        j                  |      nd}	|rt        j                  |      nd}
|rt        j                  |      nd}|j                  d||	|
||| |f       |j                          |j                          y)zT
    Update opportunity video gen by cluster_id (for when we don't have DB ID).
    Na  
        UPDATE opportunities
        SET video_gen_status = ?,
            video_concepts_json = ?,
            production_spec_json = ?,
            asset_specs_json = ?,
            updated_at = ?
        WHERE analysis_run_id = ? AND cluster_id = ?
    r{   )rL   rP   r   r   r   r   r$   r   r5   r   r   r   s               r-   'update_opportunity_video_gen_by_clusterr   }  s     ??7#D[[]F
,,x||
$
.
.
0C2@DJJ~.dM/>

?+DI-8$**[)dK
NN  M9k3Yc	df 	KKMJJLr;   verdict_filterc                    t        j                  t              }t         j                  |_        |j                         }d}| g}|r7dj                  dt        |      z        }|d| dz  }|j                  |       |dz  }|j                  ||       |j                         }|j                          |D cg c]  }t        |       c}S c c}w )z3Get all opportunities from a specific analysis run.z5SELECT * FROM opportunities WHERE analysis_run_id = ?,?z AND verdict IN ()z ORDER BY weighted_score DESC)r   r   r   rG   rH   r   r   rg   extendr   fetchallr   rI   )	rL   r   r$   r   queryrD   placeholdersrowsrJ   s	            r-   get_opportunities_for_runr     s    
 ??7#D{{D[[]FCEFxxc.&9 9:$\N!44n%	,,E
NN5&!??DJJL!%&#DI&&&s   8Climitc                 @    t        |       }|sg S t        |d   |      S )z=Get opportunities from the latest analysis run for a channel.id)r   )rK   r   )r/   r   r   
latest_runs       r-   get_latest_opportunitiesr     s.     )4J	$4% r;   c                    t        j                  t              }t         j                  |_        |j                         }|j                  d| f       |j                         }|j                          |rBt        |      }dD ]0  }|j                  |      s	 t        j                  ||         ||<   2 |S y# t        j                  $ r Y Kw xY w)z&Get a single opportunity by its DB ID.z(SELECT * FROM opportunities WHERE id = ?)rk   ro   r|   video_concepts_jsonproduction_spec_jsonasset_specs_jsonN)r   r   r   rG   rH   r   r   r   r   rI   rf   rh   loadsJSONDecodeError)rw   r$   r   rJ   result
json_fields         r-   get_opportunity_by_idr     s    ??7#D{{D[[]F
NN=?PQ
//
CJJL
c^ 	Jzz*%)-F:4F)GF:&		  ++ s   B77CCc                    t        j                  t              }t         j                  |_        |j                         }d}g }| r|dz  }|j                  |        |r|dz  }|j                  |       |dz  }|j                  |       |j                  ||       |j                         }|j                          |D cg c]  }t        |       c}S c c}w )z4Get opportunities that haven't had research run yet.z=SELECT * FROM opportunities WHERE research_status = 'pending' AND channel_id = ? AND analysis_run_id = ?  
        ORDER BY
            CASE verdict
                WHEN 'HIGH_PRIORITY' THEN 1
                WHEN 'WORTH_MAKING' THEN 2
                WHEN 'CONDITIONAL' THEN 3
                ELSE 4
            END,
            weighted_score DESC
        LIMIT ?
    r   r   r   rG   rH   r   rB   r   r   r   rI   )	r/   rL   r   r$   r   r   rD   r   rJ   s	            r-   "get_opportunities_needing_researchr     s     ??7#D{{D[[]FKEF&&j!++o& 
 
 
E MM%
NN5&!??DJJL!%&#DI&&&s   ?Crequire_researchc                    t        j                  t              }t         j                  |_        |j                         }d}g }|r|dz  }| r|dz  }|j                  |        |r|dz  }|j                  |       |dz  }|j                  |       |j                  ||       |j                         }|j                          |D 	cg c]  }	t        |	       c}	S c c}	w )z-Get opportunities that need video generation.z>SELECT * FROM opportunities WHERE video_gen_status = 'pending'z" AND research_status = 'completed'r   r   r   r   )
r/   rL   r   r   r$   r   r   rD   r   rJ   s
             r-   #get_opportunities_needing_video_genr     s     ??7#D{{D[[]FLEF55&&j!++o&	 
 
E MM%
NN5&!??DJJL!%&#DI&&&s   Cc                 $   t        j                  t              }|j                         }|j	                  d| f       |j                         d   }|j	                  d| f       t        |j                               }|j	                  d| f       t        |j                               }|j	                  d| f       t        |j                               }|j	                  d| f       |j                         d   }|j                          |||||dS )z-Get statistics for a channel's opportunities.z7SELECT COUNT(*) FROM opportunities WHERE channel_id = ?r   zw
        SELECT verdict, COUNT(*)
        FROM opportunities
        WHERE channel_id = ?
        GROUP BY verdict
    z
        SELECT research_status, COUNT(*)
        FROM opportunities
        WHERE channel_id = ?
        GROUP BY research_status
    z
        SELECT video_gen_status, COUNT(*)
        FROM opportunities
        WHERE channel_id = ?
        GROUP BY video_gen_status
    z?SELECT COUNT(*) FROM channel_analysis_runs WHERE channel_id = ?)total_opportunities
by_verdictby_research_statusby_video_gen_statusanalysis_runs)	r   r   r   r   r   r   rI   r   r   )r/   r$   r   totalr   by_researchby_video
runs_counts           r-   get_channel_opportunity_statsr   @  s   ??7#D[[]F NNA	 OOa E NN 
  foo'(J NN 
  v()K NN 
  FOO%&H NNI	 "1%JJJL  % )'# r;   )r   r   r   )NNN)v3)N)2   N)NN
   )NNTr   )"__doc__rh   r   r   r   pathlibr   typingr   r   r   r	   r   r   r   r.   r   intr:   rE   rK   rq   rv   r}   r   r   r   r   r   r   r   boolr   r    r;   r-   <module>r      s     '  , ,
x.

(
*-@
@&^ 	  	
 	>  $	$$$ $ 	$N c3h0H 8 	999 c3h9 	9
 	9@ 	::: S#X': 	:
 
#Y:H %) S>D %)	  S>	F &*&*"&%%% cN% #s(^	%
 c3h%X &*&*"&  cN	
 #s(^ c3hP !%''I' 
$sCx.'8  $ I 
$sCx.	 # (4S>2J 2 '''''' '' 
$sCx.	''V !	*'*'*' *' 	*'
 
$sCx.*'b6c 6d38n 6r;   