Spaces:
Sleeping
Sleeping
| # app.py | |
| import io | |
| import os | |
| from pathlib import Path | |
| import pandas as pd | |
| import streamlit as st | |
| # быстрый режим по умолчанию | |
| os.environ.setdefault("FAST_MODE", "1") | |
| # импорт основного пайплайна | |
| from src.predict import pipeline_infer | |
| # --- Конфигурация страницы --- | |
| st.set_page_config(page_title="Русский как иностранный – автооценка", layout="centered") | |
| st.title("Автооценка устных ответов (RFL • CatBoost + ruSBERT)") | |
| st.caption("Загрузите CSV входного формата и получите файл с колонками pred_score и pred_score_rounded.") | |
| # --- Информация о формате --- | |
| with st.expander("Формат входного CSV", expanded=False): | |
| st.markdown( | |
| """ | |
| Обязательные столбцы: | |
| - **№ вопроса** (1..4) | |
| - **Текст вопроса** | |
| - **Транскрибация ответа** | |
| - *(опционально)* **Оценка экзаменатора** — если есть, её не трогаем, добавим предсказания рядом. | |
| Разделитель — `;`, кодировка — UTF-8 (автоопределяется). | |
| """ | |
| ) | |
| # --- Пример шаблона CSV --- | |
| with st.expander("📄 Скачать шаблон CSV"): | |
| demo = pd.DataFrame({ | |
| "№ вопроса": [1, 2], | |
| "Текст вопроса": ["<p>Добро пожаловать...</p>", "<p>Опишите свой день...</p>"], | |
| "Транскрибация ответа": ["Здравствуйте! Я приехал...", "Мой день начинается с..."], | |
| "Оценка экзаменатора": [None, None], | |
| }) | |
| st.dataframe(demo) | |
| buf_tmpl = io.BytesIO() | |
| demo.to_csv(buf_tmpl, index=False, sep=";", encoding="utf-8-sig") | |
| st.download_button("⬇ Скачать шаблон CSV", buf_tmpl.getvalue(), "template.csv", "text/csv") | |
| # --- Функция загрузки и нормализации --- | |
| required = ["№ вопроса", "Текст вопроса", "Транскрибация ответа"] | |
| aliases = { | |
| "номер вопроса": "№ вопроса", | |
| "вопрос": "Текст вопроса", | |
| "текст задания": "Текст вопроса", | |
| "транскрибация": "Транскрибация ответа", | |
| "транскрипт": "Транскрибация ответа", | |
| "ответ": "Транскрибация ответа", | |
| } | |
| def load_and_normalize_csv(raw_bytes: bytes) -> pd.DataFrame: | |
| import io | |
| for sep in [";", ",", "\t"]: | |
| try: | |
| df = pd.read_csv(io.BytesIO(raw_bytes), sep=sep, engine="python") | |
| # убрать возможные артефакты Git-конфликтов | |
| if not df.empty and str(df.columns[0]).startswith("<<<"): | |
| text = raw_bytes.decode("utf-8", errors="ignore") | |
| lines = [ln for ln in text.splitlines() if not ln.startswith(("<<<", "===", ">>>"))] | |
| df = pd.read_csv(io.StringIO("\n".join(lines)), sep=sep, engine="python") | |
| # нормализация имён колонок | |
| rename_map = {} | |
| for c in list(df.columns): | |
| key = str(c).strip().lower() | |
| if key in aliases: | |
| rename_map[c] = aliases[key] | |
| if rename_map: | |
| df = df.rename(columns=rename_map) | |
| return df | |
| except Exception: | |
| continue | |
| raise ValueError("Не удалось прочитать CSV. Проверьте разделитель (';' или ',') и кодировку UTF-8.") | |
| # --- Основной интерфейс --- | |
| uploaded = st.file_uploader("Загрузите CSV", type=["csv"]) | |
| slow = st.toggle("Медленный режим", value=False, help="Выключите для быстрой оценки (точность ≈ прежняя).") | |
| run = st.button("Посчитать") | |
| if uploaded and run: | |
| try: | |
| raw = uploaded.read() | |
| df_in = load_and_normalize_csv(raw) | |
| # проверка обязательных колонок | |
| missing = [c for c in required if c not in df_in.columns] | |
| if missing: | |
| st.error(f"❌ В файле нет обязательных колонок: {missing}. Проверь заголовки и разделитель ';'.") | |
| st.dataframe(df_in.head()) | |
| st.stop() | |
| # сохраняем временно | |
| tmp_in = Path("data/api_tmp/tmp_input.csv") | |
| tmp_in.parent.mkdir(parents=True, exist_ok=True) | |
| df_in.to_csv(tmp_in, index=False, sep=";", encoding="utf-8-sig") | |
| # режим скорости | |
| os.environ["FAST_MODE"] = "0" if slow else "1" | |
| tmp_out = Path("data/api_tmp/tmp_output.csv") | |
| with st.spinner("Считаем..."): | |
| pipeline_infer(tmp_in, tmp_out) | |
| df_out = pd.read_csv(tmp_out, sep=";", encoding="utf-8-sig") | |
| st.success("✅ Готово!") | |
| st.dataframe(df_out.head(20), use_container_width=True) | |
| buf = io.BytesIO() | |
| df_out.to_csv(buf, index=False, sep=";", encoding="utf-8-sig") | |
| st.download_button("⬇ Скачать результат (CSV)", data=buf.getvalue(), file_name="predicted.csv", mime="text/csv") | |
| except Exception as e: | |
| st.exception(e) | |
| # --- Подвал --- | |
| st.markdown("---") | |
| st.caption("Модель: CatBoost Q1..Q4 + ruSBERT. Быстрый режим = FAST_MODE=1.") | |