| | import streamlit as st |
| | import os |
| | import json |
| | import clickhouse_connect |
| |
|
| | |
| | |
| | |
| | CH_PASS = os.getenv("CH_PASS") |
| | CH_DATABASE = os.getenv("CH_DATABASE", "topic_tables") |
| | APP_PASSWORD = os.getenv("APP_PASSWORD") |
| |
|
| | |
| | if not CH_PASS: |
| | st.error("CH_PASS environment variable not found. Please set it before running the app.") |
| | st.stop() |
| |
|
| | if not APP_PASSWORD: |
| | st.error("APP_PASSWORD environment variable not found. Please set it before running the app.") |
| | st.stop() |
| |
|
| | |
| | |
| | |
| | def get_clickhouse_client(): |
| | """Create a new ClickHouse client.""" |
| | return clickhouse_connect.get_client( |
| | host='td6vvza14q.us-east-2.aws.clickhouse.cloud', |
| | user='internal_tool_builder', |
| | password=CH_PASS, |
| | secure=True, |
| | connect_timeout=30, |
| | send_receive_timeout=60 |
| | ) |
| |
|
| | |
| | BRANDS = ["drumeo", "pianote", "guitareo", "singeo"] |
| | CONTENT_TYPES = ["song", "lesson"] |
| |
|
| | |
| | |
| | |
| | st.set_page_config(page_title="Custom Topics Manager", layout="wide") |
| |
|
| | |
| | |
| | |
| | |
| | if "authenticated" not in st.session_state: |
| | st.session_state.authenticated = False |
| |
|
| | |
| | if not st.session_state.authenticated: |
| | st.title("Custom Topics Manager") |
| | st.subheader("Please enter the password to continue") |
| |
|
| | |
| | with st.form("login_form"): |
| | password_input = st.text_input("Password", type="password") |
| | submit_button = st.form_submit_button("Login") |
| |
|
| | if submit_button: |
| | if password_input == APP_PASSWORD: |
| | st.session_state.authenticated = True |
| | st.success("Authentication successful!") |
| | st.rerun() |
| | else: |
| | st.error("Incorrect password. Please try again.") |
| |
|
| | st.stop() |
| |
|
| | |
| | st.title("Custom Topics Manager") |
| |
|
| | |
| | st.sidebar.header("Settings") |
| | brand = st.sidebar.selectbox("Select Brand", BRANDS, index=0) |
| | content_type = st.sidebar.selectbox("Select Content Type", CONTENT_TYPES, index=0) |
| |
|
| | |
| | st.sidebar.divider() |
| | if st.sidebar.button("π Logout"): |
| | st.session_state.authenticated = False |
| | st.rerun() |
| |
|
| | |
| | st.sidebar.divider() |
| | st.sidebar.caption(f"ClickHouse password: {'β' if CH_PASS else 'β'}") |
| | st.sidebar.caption(f"Database: {CH_DATABASE}") |
| |
|
| | |
| | |
| | |
| | |
| | content_type_suffix = f"{content_type}s" |
| | table_name = f"{CH_DATABASE}.TOPIC_GROUPS_{brand.upper()}_{content_type_suffix.upper()}" |
| |
|
| | try: |
| | with st.spinner(f"Loading topics for {brand} ({content_type}s)..."): |
| | |
| | ch_client = get_clickhouse_client() |
| |
|
| | |
| | query = f""" |
| | SELECT group_id, brand, content_type, title, item_ids, created_at, |
| | accepted, suggested_title, reviewer_comments |
| | FROM {table_name} |
| | ORDER BY created_at DESC |
| | """ |
| | result = ch_client.query(query) |
| |
|
| | |
| | topics = [] |
| | for row in result.result_rows: |
| | topics.append({ |
| | "group_id": row[0], |
| | "brand": row[1], |
| | "content_type": row[2], |
| | "title": row[3], |
| | "item_ids": row[4], |
| | "created_at": str(row[5]), |
| | "accepted": row[6], |
| | "suggested_title": row[7], |
| | "reviewer_comments": row[8] |
| | }) |
| |
|
| | st.success(f"Loaded {len(topics)} topics from {table_name}") |
| |
|
| | |
| | |
| | |
| | with st.spinner("Loading content titles..."): |
| | |
| | all_content_ids = set() |
| | for topic in topics: |
| | try: |
| | item_ids = json.loads(topic['item_ids']) |
| | all_content_ids.update(item_ids) |
| | except: |
| | pass |
| |
|
| | |
| | content_titles = {} |
| | if all_content_ids: |
| | |
| | ids_list = list(all_content_ids) |
| | ids_str = ",".join([f"'{id}'" for id in ids_list]) |
| |
|
| | dim_content_query = f""" |
| | SELECT CONTENT_ID, CONTENT_TITLE |
| | FROM snowflake_synced_tables.DIM_CONTENT |
| | WHERE CONTENT_ID IN ({ids_str}) |
| | """ |
| |
|
| | try: |
| | content_result = ch_client.query(dim_content_query) |
| | for row in content_result.result_rows: |
| | content_titles[str(row[0])] = row[1] |
| |
|
| | st.success(f"Loaded titles for {len(content_titles)} content items") |
| | except Exception as e: |
| | st.warning(f"Could not load content titles: {e}") |
| | st.info("Continuing without titles...") |
| |
|
| | |
| | |
| | |
| | st.header(f"Topics for {brand.capitalize()} - {content_type.capitalize()}s") |
| |
|
| | |
| | search_query = st.text_input("Search topics by title", "") |
| |
|
| | |
| | filtered_topics = [] |
| | for topic in topics: |
| | if search_query.lower() in topic["title"].lower(): |
| | filtered_topics.append(topic) |
| |
|
| | if not filtered_topics: |
| | st.warning("No topics match your search query.") |
| | else: |
| | st.write(f"Showing {len(filtered_topics)} of {len(topics)} topics") |
| |
|
| | |
| | for topic in filtered_topics: |
| |
|
| | with st.expander(f"**{topic['title']}** ({topic['group_id']})", expanded=False): |
| | col1, col2 = st.columns([1, 3]) |
| |
|
| | with col1: |
| | st.write("**Metadata:**") |
| | st.write(f"- Brand: `{topic['brand']}`") |
| | st.write(f"- Content Type: `{topic['content_type']}`") |
| | st.write(f"- Created: `{topic['created_at']}`") |
| |
|
| | |
| | try: |
| | item_ids = json.loads(topic['item_ids']) |
| | st.write(f"- **Items:** {len(item_ids)}") |
| | except: |
| | item_ids = [] |
| | st.error("Error parsing item IDs") |
| |
|
| | with col2: |
| | st.write("**Content Items:**") |
| | if item_ids: |
| | |
| | for i, item_id in enumerate(item_ids, 1): |
| | title = content_titles.get(str(item_id), "Title not found") |
| | st.write(f"{i}. **{title}**") |
| | st.write(f" `ID: {item_id}`") |
| | else: |
| | st.write("No items found") |
| |
|
| | |
| | st.divider() |
| | st.write("**Reviewer Feedback:**") |
| |
|
| | feedback_col1, feedback_col2 = st.columns([1, 2]) |
| |
|
| | with feedback_col1: |
| | |
| | accepted = st.checkbox( |
| | "β Accept this topic", |
| | value=topic['accepted'] if topic['accepted'] is not None else False, |
| | key=f"accept_{topic['group_id']}" |
| | ) |
| |
|
| | with feedback_col2: |
| | |
| | suggested_title = st.text_input( |
| | "Suggested alternate title (optional)", |
| | value=topic['suggested_title'] if topic['suggested_title'] else "", |
| | key=f"title_{topic['group_id']}" |
| | ) |
| |
|
| | |
| | reviewer_comments = st.text_area( |
| | "Comments (optional)", |
| | value=topic['reviewer_comments'] if topic['reviewer_comments'] else "", |
| | height=100, |
| | key=f"comments_{topic['group_id']}" |
| | ) |
| |
|
| | |
| | if st.button("πΎ Save Feedback", key=f"save_{topic['group_id']}"): |
| | try: |
| | |
| | update_client = get_clickhouse_client() |
| |
|
| | |
| | update_query = f""" |
| | ALTER TABLE {table_name} |
| | UPDATE |
| | accepted = %(accepted)s, |
| | suggested_title = %(suggested_title)s, |
| | reviewer_comments = %(reviewer_comments)s |
| | WHERE group_id = %(group_id)s |
| | """ |
| |
|
| | update_client.command(update_query, parameters={ |
| | "accepted": accepted, |
| | "suggested_title": suggested_title if suggested_title else None, |
| | "reviewer_comments": reviewer_comments if reviewer_comments else None, |
| | "group_id": topic['group_id'] |
| | }) |
| |
|
| | st.success("β Feedback saved!") |
| | st.rerun() |
| | except Exception as e: |
| | st.error(f"Error saving feedback: {e}") |
| |
|
| | except Exception as e: |
| | st.error(f"Error loading data from ClickHouse: {e}") |
| | st.info(f"Troubleshooting steps:") |
| | st.markdown(f""" |
| | 1. **Verify the table exists**: Check that `{table_name}` exists in ClickHouse |
| | 2. **Check authentication**: Make sure your CH_PASS environment variable is set correctly |
| | 3. **Verify database**: Confirm the database `{CH_DATABASE}` exists |
| | 4. **Test connection**: Try connecting to ClickHouse directly to verify credentials |
| | |
| | If the table doesn't exist or is empty, you can: |
| | - Run `python transfer.py` to copy data from HuggingFace to ClickHouse |
| | - Or run `python gemini_topics_clickhouse.py` to generate new topics directly to ClickHouse |
| | |
| | (Make sure to set `brand = "{brand}"` and `content_type = "{content_type}"` in the script first) |
| | """) |
| |
|