File size: 8,854 Bytes
bc291ca
28a7230
 
 
 
bfdae58
28a7230
25033b2
bfdae58
bc291ca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bfdae58
 
 
 
 
25033b2
 
bc291ca
25033b2
 
bc291ca
28a7230
25033b2
 
28a7230
 
25033b2
bc291ca
25033b2
 
 
bc291ca
bfdae58
 
 
25033b2
 
bc291ca
bfdae58
25033b2
bc291ca
 
 
 
 
 
 
 
 
 
 
25033b2
bc291ca
 
25033b2
 
bc291ca
 
bfdae58
 
 
 
bc291ca
 
25033b2
bc291ca
bfdae58
25033b2
bfdae58
 
 
25033b2
bc291ca
 
bfdae58
 
bc291ca
28a7230
bfdae58
 
28a7230
bfdae58
 
28a7230
 
 
 
bc291ca
 
 
28a7230
 
 
bfdae58
28a7230
bc291ca
 
 
 
28a7230
bc291ca
 
 
 
 
 
 
28a7230
bfdae58
bc291ca
 
bfdae58
 
 
 
bc291ca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# 1. Импорты (все библиотеки)
import streamlit as st
from openai import OpenAI, BadRequestError
from pydantic import BaseModel
import requests
import logging
import os
from dotenv import load_dotenv


# 2. Первая команда Streamlit (ДОЛЖНА БЫТЬ ПЕРВОЙ)
st.set_page_config(page_title="Генератор планов уроков", layout="wide")

# Вот этот волшебный CSS:
st.markdown("""
<style>
    /* Полностью отключаем running статус */
    [data-testid="stStatusWidget"] {
        display: none !important;
    }
    /* Фикс для полей ввода */
    .stTextInput, .stSlider {
        opacity: 1 !important;
    }
</style>
""", unsafe_allow_html=True)

# Отключаем реактивность формы
st.session_state.setdefault("form_values", {
    "subject": "",
    "textbook": "",
    "students": 10
})

# 4. Настройка логирования
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[logging.StreamHandler()]
)
logger = logging.getLogger(__name__)

# 5. Загрузка переменных окружения
load_dotenv()
API_KEY = os.getenv("API_KEY")
BASE_URL = os.getenv("BASE_URL")

if not API_KEY:
    logger.error("API_KEY не найден в переменных окружения")
    st.error("Ошибка: API_KEY не найден. Проверьте файл .env")
    st.stop()

# 6. Инициализация клиента OpenAI
client = OpenAI(api_key=API_KEY, base_url=BASE_URL)


# 7. Pydantic модель
class TextbookInfo(BaseModel):
    subject: str = ""
    textbook_name: str = ""


# 8. Функция загрузки изображения
def upload_to_catbox(file_bytes):
    try:
        with st.spinner("Загрузка изображения на сервер..."):
            response = requests.post(
                "https://catbox.moe/user/api.php",
                files={"fileToUpload": file_bytes},
                data={"reqtype": "fileupload"}
            )
            response.raise_for_status()
            url = response.text.strip()
            if not url.startswith("https://files.catbox.moe/"):
                raise ValueError("Некорректный URL")
            return url
    except Exception as e:
        logger.error(f"Ошибка загрузки: {e}")
        st.error("Не удалось загрузить изображение")
        return None


# 9. Инициализация состояний
if "textbook_data" not in st.session_state:
    st.session_state.textbook_data = TextbookInfo()
if "image_url" not in st.session_state:
    st.session_state.image_url = None
if "form_data" not in st.session_state:
    st.session_state.form_data = {"subject": "", "textbook": "", "students": 10}

# 10. Основной интерфейс (все st. команды после этого пункта)
left_col, right_col = st.columns([2, 3])

with left_col:
    st.header("Загрузка учебника")
    uploaded_file = st.file_uploader("Выберите изображение страницы", type=["jpg", "png", "jpeg"])

    if uploaded_file and st.session_state.image_url is None:
        image_url = upload_to_catbox(uploaded_file.getvalue())
        if image_url:
            st.session_state.image_url = image_url
            st.success("Изображение загружено!")

            with st.spinner("Анализ изображения..."):
                try:
                    response = client.chat.completions.create(
                        model="gpt-4o-mini",
                        messages=[
                            {
                                "role": "user",
                                "content": [
                                    {"type": "image_url", "image_url": {"url": image_url}},
                                    {"type": "text", "text": """Определи предмет и учебник. Ответь строго в формате:
                                    Предмет: [название]
                                    Учебник: [название]"""}
                                ]
                            }
                        ]
                    )

                    if response.choices:
                        text = response.choices[0].message.content
                        subject = text.split("Предмет:")[1].split("\n")[0].strip() if "Предмет:" in text else ""
                        textbook = text.split("Учебник:")[1].strip() if "Учебник:" in text else ""

                        st.session_state.textbook_data = TextbookInfo(
                            subject=subject,
                            textbook_name=textbook
                        )
                        st.session_state.force_form_update = True  # Важная строка!
                        st.session_state.form_data["subject"] = subject
                        st.session_state.form_data["textbook"] = textbook

                except Exception as e:
                    logger.error(f"Ошибка анализа: {e}")
                    st.error("Ошибка при анализе изображения")

    if st.session_state.image_url:
        st.subheader("Данные учебника")

        # Критически важный блок - обновляем form_values при первом показе или изменении textbook_data
        if 'form_values' not in st.session_state or st.session_state.get('force_form_update', True):
            st.session_state.form_values = {
                "subject": st.session_state.textbook_data.subject,
                "textbook": st.session_state.textbook_data.textbook_name,
                "students": 10
            }
            st.session_state.force_form_update = False

        with st.form(key="lesson_form"):
            # Поля ввода
            subject = st.text_input(
                "Предмет",
                value=st.session_state.form_values["subject"],
                key="form_subject"
            )

            textbook_name = st.text_input(
                "Название учебника",
                value=st.session_state.form_values["textbook"],
                key="form_textbook"
            )

            num_students = st.slider(
                "Количество учеников",
                1, 40, st.session_state.form_values["students"],
                key="form_students"
            )

            submitted = st.form_submit_button("Создать план урока")

            if submitted:
                st.session_state.form_values.update({
                    "subject": subject,
                    "textbook": textbook_name,
                    "students": num_students
                })

                if not subject or not textbook_name:
                    st.warning("Заполните все поля")
                else:
                    with st.spinner("Генерация плана..."):
                        try:
                            response = client.chat.completions.create(
                                model="gpt-4o-mini",
                                messages=[
                                    {
                                        "role": "system",
                                        "content": "Ты опытный учитель. Создай план урока на основе представленного материала"
                                    },
                                    {
                                        "role": "user",
                                        "content": [
                                            {"type": "image_url", "image_url": {"url": st.session_state.image_url}},
                                            {"type": "text", "text": f"""
                                            Предмет: {subject}
                                            Учебник: {textbook_name}
                                            Учеников: {num_students}
                                            """}
                                        ]
                                    }
                                ]
                            )
                            right_col.markdown(response.choices[0].message.content)
                        except Exception as e:
                            st.error(f"Ошибка: {str(e)}")

    with right_col:
        st.header("План урока")
        if not st.session_state.image_url:
            st.info("Загрузите изображение учебника")