From d2fbee3a12840ceaddcc016435a0d087f8fbf53c Mon Sep 17 00:00:00 2001 From: Ivan Budakov Date: Mon, 19 Oct 2026 18:17:07 +0300 Subject: [PATCH] git add . && git commit -m --- app/assistant.py | 103 ++++++++++++++++++++++++++++++++++++ app/main.py | 15 +++++- app/templates/schedule.html | 71 +++++++++++++++++++++++++ 3 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 app/assistant.py diff --git a/app/assistant.py b/app/assistant.py new file mode 100644 index 0000000..e70051c --- /dev/null +++ b/app/assistant.py @@ -0,0 +1,103 @@ +import re +from datetime import datetime, timezone, timedelta +from sqlmodel import Session, select +from .models import Event, GlobalStatus +from .db import engine + +def get_response(text: str) -> str: + """Принимает вопрос, определяет интент и возвращает ответ.""" + text = text.lower().strip() + + with Session(engine) as db_session: + # Получаем текущий статус + current_status = db_session.exec(select(GlobalStatus)).first() + + # --- ИНТЕНТ: ГДЕ СЕЙЧАС / СТАТУС --- + if re.search(r'где|статус|сейчас', text): + if current_status.status == 'on_place': + return "Никита Сергеевич сейчас на месте (в аудитории)." + elif current_status.status == 'delayed': + return f"Никита Сергеевич опаздывает на {current_status.delay_minutes} мин. Причина: {current_status.message or 'не указана'}." + elif current_status.status == 'away': + return "Никита Сергеевич вне вуза." + elif current_status.status == 'cancelled': + return f"Занятия отменены! {current_status.message or ''}" + else: + return "Никита Сергеевич сейчас свободен / вне расписания." + + # --- ИНТЕНТ: ОПАЗДЫВАЕТ? --- + if re.search(r'опаздыва|задержива', text): + if current_status.status == 'delayed': + return f"Да, опаздывает на {current_status.delay_minutes} мин. Причина: {current_status.message or 'не указана'}." + else: + return "По текущему статусу, он не опаздывает." + + # --- ИНТЕНТ: КОГДА СЛЕДУЮЩАЯ ПАРА / БЛИЖАЙШЕЕ --- + if re.search(r'когда|следующ|ближайш', text): + now = datetime.now(timezone.utc) + next_event = db_session.exec( + select(Event) + .where(Event.is_public == True) + .where(Event.start_time >= now) + .order_by(Event.start_time) + .limit(1) + ).first() + + if next_event: + local_time = next_event.start_time.strftime('%d.%m в %H:%M') + return f"Ближайшее событие: {next_event.title} ({local_time}). Место: {next_event.location or 'не указано'}." + else: + return "В ближайшее время событий в расписании нет." + + # --- ИНТЕНТ: РАСПИСАНИЕ НА СЕГОДНЯ --- + if re.search(r'сегодня|седня', text): + # Берем начало сегодняшнего дня и конец сегодняшнего дня + now = datetime.now(timezone.utc) + start_of_day = now.replace(hour=0, minute=0, second=0, microsecond=0) + end_of_day = start_of_day + timedelta(days=1) + + events = db_session.exec( + select(Event) + .where(Event.is_public == True) + .where(Event.start_time >= start_of_day) + .where(Event.start_time < end_of_day) + .order_by(Event.start_time) + ).all() + + if not events: + return "На сегодня событий в расписании нет." + + res = "Расписание на сегодня:\n" + for e in events: + res += f"• {e.start_time.strftime('%H:%M')} - {e.title} ({e.location or 'без места'})\n" + return res + + # --- ИНТЕНТ: РАСПИСАНИЕ НА НЕДЕЛЮ --- + if re.search(r'недел|7 дней', text): + now = datetime.now(timezone.utc) + end_of_week = now + timedelta(days=7) + + events = db_session.exec( + select(Event) + .where(Event.is_public == True) + .where(Event.start_time >= now) + .where(Event.start_time <= end_of_week) + .order_by(Event.start_time) + ).all() + + if not events: + return "На ближайшую неделю событий нет." + + res = "Расписание на неделю:\n" + for e in events[:5]: # Показываем первые 5, чтобы не спамить + res += f"• {e.start_time.strftime('%d.%m %H:%M')} - {e.title}\n" + if len(events) > 5: + res += "...и другие события (смотрите на странице)." + return res + + # --- ИНТЕНТ: КОНТАКТЫ --- + if re.search(r'связаться|контакт|телеграм|почта|кабинет', text): + return "Связаться с Никитой Сергеевичем можно по почте: freezer.nik@gmail.com (или в Telegram)." + + # --- ЕСЛИ НИЧЕГО НЕ ПОНЯЛ --- + return "Извините, я пока не понимаю этот вопрос. Попробуйте спросить: 'Где сейчас?', 'Когда следующая пара?', 'Что сегодня?' или 'Как связаться?'." diff --git a/app/main.py b/app/main.py index 18f6ffb..ec8139d 100644 --- a/app/main.py +++ b/app/main.py @@ -1,6 +1,6 @@ from flask import Flask, render_template, jsonify, redirect, url_for, request, session from .db import init_db, engine -from . import config, auth +from . import config, auth, assistant from sqlmodel import Session, select from .models import User, Event, GlobalStatus from datetime import datetime, timezone @@ -156,6 +156,18 @@ def public_schedule(): return render_template("schedule.html", status=current_status, events=events) +# --- АССИСТЕНТ: API --- +@app.route("/api/assistant", methods=["POST"]) +def api_assistant(): + data = request.get_json() + if not data or "message" not in data: + return jsonify({"error": "Нет сообщения"}), 400 + + user_message = data["message"] + reply = assistant.get_response(user_message) + + return jsonify({"reply": reply}) + # --- РАСПИСАНИЕ: АДМИН-ПАНЕЛЬ --- @app.route("/admin/schedule") @require_role(["admin", "professor"]) @@ -200,7 +212,6 @@ def add_event(): description = request.form.get("description") is_public = request.form.get("is_public") == "on" - # Парсим дату из input type="datetime-local" start_time = datetime.strptime(start_str, "%Y-%m-%dT%H:%M").replace(tzinfo=timezone.utc) end_time = datetime.strptime(end_str, "%Y-%m-%dT%H:%M").replace(tzinfo=timezone.utc) diff --git a/app/templates/schedule.html b/app/templates/schedule.html index 5610d01..879bda8 100644 --- a/app/templates/schedule.html +++ b/app/templates/schedule.html @@ -7,6 +7,7 @@ @@ -67,6 +82,62 @@ {% else %}

Пока нет запланированных событий.

{% endfor %} + + +
+
🤖 Ассистент расписания
+
+
Привет! Я могу подсказать статус Никиты Сергеевича, его расписание или контакты. Спроси меня о чем-нибудь.
+
+
+ + +
+
+ + +