| import streamlit as st |
| from database import get_hash, upsert_data |
|
|
|
|
| @st.cache_resource |
| def load_css(): |
| """Загрузка CSS стилей""" |
| with open("assets/styles.css") as f: |
| return f.read() |
|
|
| def render_sidebar(localS, locale, all_langs, moods, all_tags): |
| """Рендеринг боковой панели с фильтрами""" |
| with st.sidebar: |
| st.markdown( |
| """ |
| []( |
| https://huggingface.co/datasets/loim/ru_fantasy_characters) |
| """ |
| ) |
|
|
| def on_language_change(): |
| if st.session_state.lang_select != st.session_state.lang: |
| localS.setItem("lang", st.session_state.lang_select) |
|
|
| st.selectbox( |
| locale['filter_lang'], |
| all_langs, |
| index=all_langs.index(st.session_state.lang), |
| on_change=on_language_change, |
| key="lang_select" |
| ) |
|
|
| st.markdown("---") |
|
|
| st.title(locale['filter_title']) |
| search_query = st.text_input(locale['filter_search'], "") |
| sort_option = st.selectbox( |
| locale['filter_sort_title'], |
| locale['filter_sort'] |
| ) |
| absurdity_range = st.slider(locale['filter_absurd'], 0, 10, (0, 10)) |
| selected_mood = st.selectbox(locale['filter_mood'], moods) |
| selected_tags = st.multiselect(locale['filter_tags'], all_tags) |
| |
| return { |
| "search_query": search_query, |
| "sort_option": sort_option, |
| "absurdity_range": absurdity_range, |
| "selected_mood": selected_mood, |
| "selected_tags": selected_tags |
| } |
|
|
| @st.cache_data |
| def generate_rpg_card(locale, row): |
| """Генерация RPG-карточки персонажа""" |
| return f""" |
| {locale['rpt_card_world']} {row['world']} |
| |
| {locale['rpt_card_name']} {row['name']} |
| {locale['rpt_card_desc']} {row['description']} |
| {locale['rpt_card_story']} {row['short_story']} |
| {locale['rpt_card_style']} {row['style']} |
| |
| {locale['rpt_card_msg']} {row['first_message']} |
| """ |
|
|
| |
| @st.cache_data |
| def render_character_card(locale, row): |
| with st.container(): |
| st.markdown("---") |
| st.markdown(f"### {row['name']}") |
| st.caption(f"*{row['short_story']}*") |
| |
| col1, col2 = st.columns([1, 3]) |
| with col1: |
| st.markdown(f"**{locale['char_world']}** {row['world']}") |
| st.markdown(f"**{locale['char_mood']}** `{row['mood']}`") |
| st.markdown(f"**{locale['char_absurd']}** `{row['absurdity']}/10`") |
| st.markdown(f"**{locale['char_tags']}** `{' '.join(row['tags'].split())}`") |
| |
| with col2: |
| st.markdown(f"**{locale['char_desc']}** {row['description']}") |
| st.markdown(f"**{locale['char_style']}** *{row['style']}*") |
| |
| with st.expander(locale['char_rpg_card'], expanded=False): |
| rpg_card = generate_rpg_card(locale, row) |
| st.code(rpg_card, language="markdown") |
|
|
| def render_character_rating(locale, database, localS, row): |
| current_hash = row['hash'] |
|
|
| rating_data, _ = get_hash(database, "rating", current_hash) |
| rating_data = rating_data[1][0] if rating_data[1] else {"hash": current_hash, "likes": 0, "dislikes": 0} |
|
|
| |
| if current_hash in st.session_state.rated and (rating_data['likes'] != 0 or rating_data['dislikes'] != 0): |
| |
| st.write(locale["rating_text"].format(rating_data['likes'], rating_data['dislikes'])) |
| else: |
| |
| if st.button(locale["rating_like"], key=f"like_{current_hash}"): |
| rating_data["likes"] += 1 |
| upsert_data(database, "rating", rating_data) |
| st.session_state.rated.append(current_hash) |
| localS.setItem("rated", st.session_state.rated) |
| st.rerun() |
| |
| if st.button(locale["rating_dislike"], key=f"dislike_{current_hash}"): |
| rating_data["dislikes"] += 1 |
| upsert_data(database, "rating", rating_data) |
| st.session_state.rated.append(current_hash) |
| localS.setItem("rated", st.session_state.rated) |
| st.rerun() |
|
|
| def feedback_system(locale, database, localS): |
| if 'feedback_enabled' not in st.session_state: |
| st.session_state.feedback_enabled = False |
|
|
| if st.session_state.feedback_enabled: |
| st.header(locale["feedback_header"]) |
| rating = st.slider(locale["feedback_rating"], 1, 5, 3) |
| comment = st.text_area(locale["feedback_comment_label"], help=locale["feedback_comment_help"]) |
| if st.button(locale["feedback_send_btn"]): |
| if comment.strip() == "" and rating == 3: |
| st.warning(locale["feedback_warn"]) |
| else: |
| upsert_data(database, "feedback", {"text": comment.strip(), "rating": rating}) |
| st.success(locale["feedback_suc"]) |
| else: |
| if st.button(locale["feedback_quest"]): |
| st.session_state.feedback_enabled = True |
| st.rerun() |
|
|
| def render_main_content(locale, database, localS, filtered_df): |
| feedback_system(locale, database, localS) |
|
|
| """Рендеринг основного контента с пагинацией""" |
| st.title("🧙 Characters Explorer") |
| |
| if len(filtered_df) == 0: |
| st.warning(locale['main_not_found']) |
| else: |
| |
| if 'page' not in st.session_state: |
| st.session_state.page = 0 |
| |
| |
| per_page = 5 |
| total_pages = max(1, (len(filtered_df) // per_page) + (1 if len(filtered_df) % per_page else 0)) |
| if st.session_state.page >= total_pages: |
| st.session_state.page = 0 |
|
|
| |
| col1, col2, _ = st.columns([2, 3, 5]) |
| with col1: |
| st.selectbox( |
| locale['main_counter'], |
| options=[5, 10, 20], |
| index=0, |
| key='per_page', |
| on_change=lambda: st.session_state.update(page=0) |
| ) |
| with col2: |
| st.number_input( |
| locale['main_page'], |
| min_value=0, |
| max_value=total_pages-1, |
| value=st.session_state.page, |
| key='page_input', |
| format="%d", |
| on_change=lambda: setattr(st.session_state, 'page', st.session_state.page_input) |
| ) |
| |
| |
| start_idx = st.session_state.page * st.session_state.per_page |
| end_idx = min((st.session_state.page + 1) * st.session_state.per_page, len(filtered_df)) |
| |
| |
| st.markdown(locale['main_found'].format(len(filtered_df), st.session_state.page, total_pages-1)) |
| |
| |
| for idx in range(start_idx, end_idx): |
| row = filtered_df.iloc[idx] |
| render_character_card(locale, row) |
| render_character_rating(locale, database, localS, row) |
|
|
| st.markdown("---") |
| |
| |
| if total_pages > 1: |
| cols = st.columns(4) |
| with cols[0]: |
| if st.button(locale['pages_first'], disabled=(st.session_state.page == 0)): |
| st.session_state.page = 0 |
| st.rerun() |
| with cols[1]: |
| if st.button(locale['pages_before'], disabled=(st.session_state.page == 0)): |
| st.session_state.page -= 1 |
| st.rerun() |
| with cols[2]: |
| if st.button(locale['pages_next'], disabled=(st.session_state.page >= total_pages-1)): |
| st.session_state.page += 1 |
| st.rerun() |
| with cols[3]: |
| if st.button(locale['pages_last'], disabled=(st.session_state.page == total_pages-1)): |
| st.session_state.page = total_pages-1 |
| st.rerun() |