mirror of
https://github.com/Faridik/Carolyn.git
synced 2026-07-08 05:11:52 +00:00
Много изменений:
- исправлена проблема кэша - в рассылке можно отправлять файлы и фото - отпечаток оценок обновляется при запросе
This commit is contained in:
parent
f74b970c2b
commit
a2ad18ca8c
@ -4,12 +4,11 @@ from werkzeug.exceptions import *
|
||||
from manager import *
|
||||
import flask
|
||||
import logging
|
||||
import uuid
|
||||
|
||||
logging.basicConfig()
|
||||
LOG = logging.getLogger(__name__)
|
||||
LOG.setLevel(logging.DEBUG)
|
||||
SUPER_USER = (420, 228)
|
||||
ADMIN = "admin"
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
app.db = None
|
||||
@ -40,11 +39,11 @@ def availability(func):
|
||||
def superuser(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
god_mode = flask.request.args.get("god_mode", False)
|
||||
god_mode = flask.request.args.get("god_mode", str(False))
|
||||
if god_mode.lower() != "true":
|
||||
tg_id = flask.request.args.get("tg_id", -1)
|
||||
student: Student = app.db.get_student_by_tg_id(tg_id)
|
||||
if student.number not in SUPER_USER:
|
||||
if student.group_id != ADMIN:
|
||||
raise Forbidden(f"Доступ запрещен для студента {student.name}")
|
||||
result = func(*args, **kwargs)
|
||||
return result
|
||||
@ -79,6 +78,7 @@ def grades():
|
||||
dict(
|
||||
subjects=student.subjects,
|
||||
assignments=student.assignments,
|
||||
fingerprint=student.fingerprint,
|
||||
)
|
||||
)
|
||||
|
||||
@ -92,10 +92,11 @@ def fingerprint():
|
||||
app.db.set_assignments_for_student(student)
|
||||
return flask.jsonify(
|
||||
dict(
|
||||
fingerprint=uuid.uuid3(uuid.NAMESPACE_DNS, "%s" % student.assignments),
|
||||
fingerprint=student.fingerprint,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@app.route("/student")
|
||||
@availability
|
||||
def student():
|
||||
@ -104,12 +105,13 @@ def student():
|
||||
student = app.db.get_student_by_tg_id(tg_id)
|
||||
return flask.jsonify(dict(student=student))
|
||||
|
||||
|
||||
@app.route("/students")
|
||||
@availability
|
||||
@superuser
|
||||
def broadcast(*args, **kwargs):
|
||||
"""Выполняет рассылку студентам."""
|
||||
sub_only = flask.request.args.get("sub_only", False)
|
||||
sub_only = flask.request.args.get("sub_only", str(False))
|
||||
if sub_only.lower() == "true":
|
||||
return flask.jsonify(app.db.get_all_groups_only_sub_students())
|
||||
else:
|
||||
|
||||
@ -47,8 +47,6 @@ def timed_lru_cache(seconds: int, maxsize: int = 128):
|
||||
if datetime.utcnow() >= func.expiration:
|
||||
func.cache_clear()
|
||||
func.expiration = datetime.utcnow() + func.lifetime
|
||||
# if args[1] in NON_CACHED_RANGES:
|
||||
# func.cache_clear()
|
||||
print(*args)
|
||||
return func(*args, **kwargs)
|
||||
|
||||
@ -118,10 +116,17 @@ class Manager:
|
||||
service = build("sheets", "v4", credentials=creds)
|
||||
self.sheet = service.spreadsheets()
|
||||
|
||||
@timed_lru_cache(seconds=150)
|
||||
def get_values(
|
||||
self, range_name: str = "StudentList"
|
||||
) -> list: # TODO: change subject
|
||||
def get_values(self, range_name: str = "StudentList") -> list:
|
||||
if range_name in NON_CACHED_RANGES:
|
||||
return self._get_values(range_name)
|
||||
else:
|
||||
return self._cached_get_values(range_name)
|
||||
|
||||
@timed_lru_cache(minutes=25)
|
||||
def _cached_get_values(self, range_name):
|
||||
return self._get_values(range_name)
|
||||
|
||||
def _get_values(self, range_name: str = "StudentList") -> list:
|
||||
"""Получить значения с таблицы.
|
||||
|
||||
Returns:
|
||||
@ -256,8 +261,7 @@ class Manager:
|
||||
assignment_value = list(map(toFloat, assignment_values[student.number - 1]))
|
||||
notes_range, n_row = row[ASSIGNMENT_NOTES_RANGES].split(",")
|
||||
note = self.get_values(notes_range)[student.number - 1][int(n_row)]
|
||||
note = "Замечаний по работе нет." if note == "-" \
|
||||
else f"Замечания:\n{note}"
|
||||
note = "Замечаний по работе нет." if note == "-" else f"Замечания:\n{note}"
|
||||
student.add_assignment(
|
||||
Assignment(
|
||||
name=row[ASSIGNMENT_NAMES],
|
||||
|
||||
@ -1,12 +1,19 @@
|
||||
from .assignment import Assignment
|
||||
import uuid
|
||||
|
||||
|
||||
class Student(dict):
|
||||
"""Позволяет выполнять операции над студентами."""
|
||||
|
||||
def __init__(self, number: int, name: str, group_id: str,
|
||||
tg_id: int = None, subjects: list = None,
|
||||
is_subbed: bool = False):
|
||||
def __init__(
|
||||
self,
|
||||
number: int,
|
||||
name: str,
|
||||
group_id: str,
|
||||
tg_id: int = None,
|
||||
subjects: list = None,
|
||||
is_subbed: bool = False,
|
||||
):
|
||||
self.number: int = number
|
||||
self.name: str = name
|
||||
self.group_id: str = group_id
|
||||
@ -14,6 +21,7 @@ class Student(dict):
|
||||
self.subjects = subjects
|
||||
self.assignments = []
|
||||
self.is_subbed = is_subbed
|
||||
self.fingerprint = ""
|
||||
|
||||
dict.__init__(
|
||||
self,
|
||||
@ -22,18 +30,20 @@ class Student(dict):
|
||||
group_id=self.group_id,
|
||||
tg_id=self.tg_id,
|
||||
assignments=self.assignments,
|
||||
is_subbed=self.is_subbed
|
||||
fingerprint=self.fingerprint,
|
||||
is_subbed=self.is_subbed,
|
||||
)
|
||||
|
||||
def add_assignment(self, assignment: Assignment):
|
||||
self.assignments.append(assignment)
|
||||
|
||||
@property
|
||||
def grade(self) -> float:
|
||||
"""Получить среднюю оценку студента по текущему контролю."""
|
||||
return 2
|
||||
self._update_fingerprint()
|
||||
|
||||
@property
|
||||
def score(self):
|
||||
"""Количество баллов у студента."""
|
||||
return sum([a.total for a in self.assignments])
|
||||
|
||||
def _update_fingerprint(self) -> None:
|
||||
fingerprint = uuid.uuid3(uuid.NAMESPACE_DNS, "%s" % self.assignments)
|
||||
self.fingerprint = fingerprint
|
||||
self["fingerprint"] = fingerprint
|
||||
|
||||
@ -11,6 +11,7 @@ from telegram import (
|
||||
Update,
|
||||
ParseMode,
|
||||
)
|
||||
from telegram import message
|
||||
from telegram.ext import (
|
||||
CallbackContext,
|
||||
CommandHandler,
|
||||
@ -25,10 +26,12 @@ from telegram.ext.callbackqueryhandler import CallbackQueryHandler
|
||||
from utils.inline_keyboard import *
|
||||
from messages import Messages
|
||||
|
||||
TOKEN = Path(".secrets/bot_token.txt").read_text()
|
||||
MESSAGES = Messages()
|
||||
ADMIN = "admin"
|
||||
HOST = "http://carolyn-spreadsheets:5000"
|
||||
SUB_TIME_DELTA = datetime.timedelta(minutes=15)
|
||||
MESSAGES = Messages()
|
||||
SUB_TIME_DELTA = datetime.timedelta(minutes=30)
|
||||
TOKEN = Path(".secrets/bot_token.txt").read_text()
|
||||
DEV_CHAT = -507530583 # ID канала где собираются логи
|
||||
|
||||
BROADCAST_MESSAGE, BROADCAST_PUBLISH_DONE = range(2)
|
||||
GRADES_ASSNT, GRADES_VIEW = range(2)
|
||||
@ -88,6 +91,7 @@ def grades_start(update: Update, context: CallbackContext):
|
||||
"""
|
||||
user_id = update.message.from_user.id
|
||||
start = time.monotonic()
|
||||
|
||||
# Длинная операция, сообщим о запущенном процессе.
|
||||
look_msg = update.message.reply_html(MESSAGES.Assignments.START)
|
||||
|
||||
@ -98,6 +102,12 @@ def grades_start(update: Update, context: CallbackContext):
|
||||
update.message.reply_sticker(MESSAGES.Stickers.DEAD)
|
||||
return ConversationHandler.END
|
||||
|
||||
# При инициализации бота словарь отсутствует
|
||||
if "fingerprints" not in context.bot_data:
|
||||
context.bot_data["fingerprints"] = defaultdict(str)
|
||||
|
||||
context.bot_data["fingerprints"][user_id] = data["fingerprint"]
|
||||
|
||||
context.user_data["subjects"] = data["subjects"]
|
||||
context.user_data["assignments"] = data["assignments"]
|
||||
diff = time.monotonic() - start
|
||||
@ -228,17 +238,51 @@ def publish_message(update: Update, context: CallbackContext):
|
||||
|
||||
def publish_done(update: Update, context: CallbackContext):
|
||||
"""Рассылка выполняется здесь."""
|
||||
message = f"📣 {update.message.text}\n<i>— {update.message.from_user.name}</i>"
|
||||
|
||||
def send_message(chat_id: int, message: telegram.Message):
|
||||
txt = message.text if message.text else message.caption
|
||||
txt = "" if not txt else txt
|
||||
txt = f"📣 {txt}\n<i>— {message.from_user.name}</i>"
|
||||
|
||||
if message.document:
|
||||
context.bot.send_document(
|
||||
chat_id=chat_id,
|
||||
caption=txt,
|
||||
parse_mode="HTML",
|
||||
document=message.document,
|
||||
)
|
||||
elif message.photo:
|
||||
file = message.photo[-1]
|
||||
context.bot.send_photo(
|
||||
chat_id=chat_id, caption=txt, parse_mode="HTML", photo=file
|
||||
)
|
||||
elif message.animation:
|
||||
context.bot.send_animation(
|
||||
chat_id=chat_id,
|
||||
caption=txt,
|
||||
parse_mode="HTML",
|
||||
animation=message.animation,
|
||||
)
|
||||
else:
|
||||
context.bot.send_message(chat_id=chat_id, text=txt, parse_mode="HTML")
|
||||
|
||||
group = context.user_data["broadcast_to"]
|
||||
send_status = update.message.reply_text("0%")
|
||||
try:
|
||||
students = context.bot_data["groups"][group]["students"]
|
||||
except:
|
||||
LOG.exception(
|
||||
"Не получилось взять студента с группой, %s, группы %s",
|
||||
group,
|
||||
context.bot_data["groups"],
|
||||
)
|
||||
for i, student in enumerate(students):
|
||||
progress = i / len(students) * 100
|
||||
if student["tg_id"]:
|
||||
context.bot.send_message(
|
||||
chat_id=student["tg_id"], text=message, parse_mode="HTML"
|
||||
)
|
||||
send_message(student["tg_id"], update.message)
|
||||
send_status.edit_text(f"{progress:.1f}% 🏃♂️ Рассылка")
|
||||
|
||||
send_message(DEV_CHAT, update.message)
|
||||
send_status.edit_text("100% 👍 Все сообщения разосланы")
|
||||
return ConversationHandler.END
|
||||
|
||||
@ -288,8 +332,6 @@ def unsub(update: Update, context: CallbackContext):
|
||||
# Длинная операция, сообщим о запущенном процессе.
|
||||
msg = update.message.reply_text(text=MESSAGES.Unsub.START)
|
||||
|
||||
# LOG.info(f"Unsubcripting {user_id}")
|
||||
|
||||
try:
|
||||
req = requests.get(
|
||||
f"{HOST}/unsub",
|
||||
@ -310,6 +352,7 @@ def unsub(update: Update, context: CallbackContext):
|
||||
|
||||
msg.edit_text(text=MESSAGES.Unsub.UNSUBBED)
|
||||
|
||||
|
||||
def variant(update: Update, context: CallbackContext):
|
||||
"""Проверка своего варианта внутри группы"""
|
||||
|
||||
@ -339,6 +382,14 @@ def variant(update: Update, context: CallbackContext):
|
||||
|
||||
msg.edit_text(text=MESSAGES.Variant.get(data["student"]["number"]))
|
||||
|
||||
|
||||
def error(update: Update, context: CallbackContext):
|
||||
LOG.error(msg="Exception while handling an update:", exc_info=context.error)
|
||||
update.message.reply_text("Ой, кажется у меня %s" % repr(context.error))
|
||||
update.message.reply_sticker(MESSAGES.Stickers.DEAD)
|
||||
return ConversationHandler.END
|
||||
|
||||
|
||||
# ======================================================================= SCHED
|
||||
|
||||
|
||||
@ -410,7 +461,9 @@ def main() -> None:
|
||||
entry_points=[CommandHandler("broadcast", broadcast)],
|
||||
states={
|
||||
BROADCAST_MESSAGE: [MessageHandler(Filters.text, publish_message)],
|
||||
BROADCAST_PUBLISH_DONE: [MessageHandler(Filters.text, publish_done)],
|
||||
BROADCAST_PUBLISH_DONE: [
|
||||
MessageHandler(Filters.all & ~Filters.command, publish_done)
|
||||
],
|
||||
},
|
||||
fallbacks=[CommandHandler("cancel", cancel)],
|
||||
)
|
||||
@ -425,6 +478,7 @@ def main() -> None:
|
||||
dispatcher.add_handler(sub_handler)
|
||||
dispatcher.add_handler(unsub_handler)
|
||||
dispatcher.add_handler(variant_handler)
|
||||
dispatcher.add_error_handler(error)
|
||||
|
||||
# Начало работы бота
|
||||
updater.start_polling()
|
||||
|
||||
@ -21,7 +21,7 @@ class TgLogger(logging.Logger):
|
||||
```
|
||||
"""
|
||||
|
||||
CHAT_LOG_ID = -507530583 # ID канала где собираются логи
|
||||
DEV_CHAT = -507530583 # ID канала где собираются логи
|
||||
|
||||
def __init__(self, name, level=logging.NOTSET):
|
||||
self._bot = None
|
||||
@ -43,7 +43,7 @@ class TgLogger(logging.Logger):
|
||||
):
|
||||
if self._bot is not None:
|
||||
txt = f"{emoji} {msg};"
|
||||
self._bot.send_message(chat_id=self.CHAT_LOG_ID, text=txt)
|
||||
self._bot.send_message(chat_id=self.DEV_CHAT, text=txt)
|
||||
|
||||
def warning(self, msg: Any, *args: Any, **kwargs: Any) -> None:
|
||||
self._log_to_telegram(msg=msg, emoji="⚠", *args)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user