diff --git a/SpreadsheetsService/app.py b/SpreadsheetsService/app.py
index 31dfd56..d6b5d0e 100644
--- a/SpreadsheetsService/app.py
+++ b/SpreadsheetsService/app.py
@@ -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:
diff --git a/SpreadsheetsService/manager/manager.py b/SpreadsheetsService/manager/manager.py
index 2410f41..a81dc79 100644
--- a/SpreadsheetsService/manager/manager.py
+++ b/SpreadsheetsService/manager/manager.py
@@ -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],
diff --git a/SpreadsheetsService/models/student.py b/SpreadsheetsService/models/student.py
index bd982db..04443b5 100644
--- a/SpreadsheetsService/models/student.py
+++ b/SpreadsheetsService/models/student.py
@@ -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
diff --git a/TelegramBot/bot.py b/TelegramBot/bot.py
index caee0ec..8758a53 100644
--- a/TelegramBot/bot.py
+++ b/TelegramBot/bot.py
@@ -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— {update.message.from_user.name}"
+
+ 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— {message.from_user.name}"
+
+ 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%")
- students = context.bot_data["groups"][group]["students"]
+ 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()
diff --git a/TelegramBot/utils/tg_logger.py b/TelegramBot/utils/tg_logger.py
index 662faa7..2bede3e 100644
--- a/TelegramBot/utils/tg_logger.py
+++ b/TelegramBot/utils/tg_logger.py
@@ -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)