Обновления сентября 2025
This commit is contained in:
+4
-3
@@ -1,3 +1,4 @@
|
|||||||
__pycache__
|
__pycache__
|
||||||
lecture.json
|
lecture.json
|
||||||
result.txt
|
result.txt
|
||||||
|
custom.txt
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
День недели
|
||||||
|
Номер пары
|
||||||
|
Название занятия
|
||||||
|
Преподаватель
|
||||||
|
|
||||||
|
ВТ
|
||||||
|
6
|
||||||
|
Обеспечение антитеррористической защищенности объектов здравоохранения Российской Федерации
|
||||||
|
Окутина Н Н
|
||||||
-24
@@ -1,24 +0,0 @@
|
|||||||
ВТ
|
|
||||||
2
|
|
||||||
Методика сохранения и укрепления традиционных российских духовно-нравственных ценностей и профилактики деструктивной идеологии
|
|
||||||
Сергеева Юлия Валерьевна
|
|
||||||
|
|
||||||
ВТ
|
|
||||||
3
|
|
||||||
Методика сохранения и укрепления традиционных российских духовно-нравственных ценностей и профилактики деструктивной идеологии
|
|
||||||
Сергеева Юлия Валерьевна
|
|
||||||
|
|
||||||
ПТ
|
|
||||||
2
|
|
||||||
Методика сохранения и укрепления традиционных российских духовно-нравственных ценностей и профилактики деструктивной идеологии
|
|
||||||
Сергеева Юлия Валерьевна
|
|
||||||
|
|
||||||
ПТ
|
|
||||||
3
|
|
||||||
Методика сохранения и укрепления традиционных российских духовно-нравственных ценностей и профилактики деструктивной идеологии
|
|
||||||
Сергеева Юлия Валерьевна
|
|
||||||
|
|
||||||
ПТ
|
|
||||||
4
|
|
||||||
Методика сохранения и укрепления традиционных российских духовно-нравственных ценностей и профилактики деструктивной идеологии
|
|
||||||
Сергеева Юлия Валерьевна
|
|
||||||
@@ -1,75 +1,99 @@
|
|||||||
import json
|
import json
|
||||||
import requests
|
import requests
|
||||||
import time
|
import time
|
||||||
import tqdm
|
import tqdm
|
||||||
|
|
||||||
class Non200StatusCodeError(Exception):
|
class Non200StatusCodeError(Exception):
|
||||||
def __init__(self, status_code, message="Сервер вернул не 200 код"):
|
def __init__(self, status_code, message="Сервер вернул не 200 код"):
|
||||||
self.status_code = status_code
|
self.status_code = status_code
|
||||||
self.message = message
|
self.message = message
|
||||||
super().__init__(self.message)
|
super().__init__(self.message)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.message}. Код статуса: {self.status_code}"
|
return f"{self.message}. Код статуса: {self.status_code}"
|
||||||
|
|
||||||
def do_request(name, start, end):
|
class ResultFileWriter:
|
||||||
response = requests.post(
|
def __init__(self, filename):
|
||||||
"https://ulsu.ru/oto/api/production/external/createKtalkMeeting.php",
|
self.filename = filename
|
||||||
data={
|
self.file = None
|
||||||
"name": name,
|
self.last_auditorium = ""
|
||||||
"desc": "Встреча создана средствами API (Copyright © 2023 Kravenrus), обработка расписания © Kozhilya",
|
self.last_day_of_week = ""
|
||||||
"startAt": start,
|
|
||||||
"endAt": end,
|
def __enter__(self):
|
||||||
"stateAnonymous": "true",
|
self.file = open(self.filename, "w", encoding="utf-8")
|
||||||
"stateSessionHalls": "false",
|
return self
|
||||||
"stateLobby": "false",
|
|
||||||
"stateSip": "false",
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
"stateAutoRecording": "true",
|
if self.file:
|
||||||
"stateAudioPolicy": "false",
|
self.file.close()
|
||||||
"stateVideoPolicy": "false",
|
|
||||||
"stateScreenSharePolicy": "false",
|
def write_lecture(self, lecture, url):
|
||||||
"pinCode": "false"
|
"""Записывает информацию о лекции в файл"""
|
||||||
},
|
if lecture['auditorium'] != self.last_auditorium:
|
||||||
headers={ \
|
if self.last_auditorium != "":
|
||||||
"Referer": "https://ulsu.ru/oto/ktalk/"
|
self._write_line("\n========================\n")
|
||||||
}
|
self._write_line(lecture['auditorium'] + "\n")
|
||||||
)
|
self.last_auditorium = lecture['auditorium']
|
||||||
|
self.last_day_of_week = ""
|
||||||
if response.status_code == 200:
|
|
||||||
time.sleep(5)
|
if lecture['day_of_week'] != self.last_day_of_week:
|
||||||
data = response.json()
|
prefix = f" {lecture['day_of_week']} "
|
||||||
return f"https://{data['meetingData']['global']['space']}.ktalk.ru/{data['createdMeeting']['roomName']}"
|
self.last_day_of_week = lecture['day_of_week']
|
||||||
|
else:
|
||||||
else:
|
prefix = " " * 6
|
||||||
raise Non200StatusCodeError(response.status_code, "Ответ сервера: " + response.text)
|
|
||||||
|
self._write_line(f"{prefix} #{lecture['pair_number']} - {lecture['name']}\n")
|
||||||
if __name__ == "__main__":
|
self._write_line(" " * 12 + url + "\n")
|
||||||
lines = []
|
|
||||||
|
def _write_line(self, line):
|
||||||
with open("lecture.json", "r", encoding="utf-8") as f:
|
"""Вспомогательный метод для записи строки в файл"""
|
||||||
lectures = json.load(f)
|
self.file.write(line)
|
||||||
|
self.file.flush() # Принудительно сбрасываем буфер
|
||||||
last_auditorium = ""
|
|
||||||
last_day_of_week = ""
|
def do_request(name, start, end):
|
||||||
|
response = requests.post(
|
||||||
for lecture in tqdm.tqdm(lectures):
|
"https://ulsu.ru/oto/api/production/external/createKtalkMeeting.php",
|
||||||
url = do_request(lecture['name'], lecture['start'], lecture['end'])
|
data={
|
||||||
|
"name": name,
|
||||||
if lecture['auditorium'] != last_auditorium:
|
"desc": "Встреча создана средствами API (Copyright © 2023 Kravenrus), обработка расписания © Kozhilya",
|
||||||
if last_auditorium != "":
|
"startAt": start,
|
||||||
lines.append("\n========================\n")
|
"endAt": end,
|
||||||
lines.append(lecture['auditorium'] + "\n")
|
"stateAnonymous": "true",
|
||||||
last_auditorium = lecture['auditorium']
|
"stateSessionHalls": "false",
|
||||||
last_day_of_week = ""
|
"stateLobby": "false",
|
||||||
|
"stateSip": "false",
|
||||||
if lecture['day_of_week'] != last_day_of_week:
|
"stateAutoRecording": "true",
|
||||||
prefix = f" {lecture['day_of_week']} "
|
"stateAudioPolicy": "false",
|
||||||
last_day_of_week = lecture['day_of_week']
|
"stateVideoPolicy": "false",
|
||||||
else:
|
"stateScreenSharePolicy": "false",
|
||||||
prefix = " " * 6
|
"pinCode": "false"
|
||||||
|
},
|
||||||
lines.append(f"{prefix} #{lecture['pair_number']} - {lecture['name']}\n")
|
headers={
|
||||||
lines.append(" " * 12 + url + "\n")
|
"Referer": "https://ulsu.ru/oto/ktalk/"
|
||||||
|
}
|
||||||
with open("result.txt", "w", encoding="utf-8") as f:
|
)
|
||||||
f.writelines(lines)
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
time.sleep(5)
|
||||||
|
data = response.json()
|
||||||
|
return f"https://{data['meetingData']['global']['space']}.ktalk.ru/{data['createdMeeting']['roomName']}"
|
||||||
|
else:
|
||||||
|
raise Non200StatusCodeError(response.status_code, "Ответ сервера: " + response.text)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
with open("lecture.json", "r", encoding="utf-8") as f:
|
||||||
|
lectures = json.load(f)
|
||||||
|
|
||||||
|
# Используем контекстный менеджер для работы с файлом
|
||||||
|
with ResultFileWriter("result.txt") as writer:
|
||||||
|
for lecture in tqdm.tqdm(lectures):
|
||||||
|
try:
|
||||||
|
url = do_request(lecture['name'], lecture['start'], lecture['end'])
|
||||||
|
writer.write_lecture(lecture, url)
|
||||||
|
except Exception as e:
|
||||||
|
# Записываем информацию об ошибке в файл
|
||||||
|
error_line = f"\nОШИБКА при обработке лекции '{lecture['name']}': {str(e)}\n\n"
|
||||||
|
writer._write_line(error_line)
|
||||||
|
print(f"Произошла ошибка: {e}")
|
||||||
|
# Продолжаем обработку следующих лекций
|
||||||
|
continue
|
||||||
+15
-14
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"first_week": "2025-01-06",
|
"first_week": "2025-09-01",
|
||||||
"week": [
|
"week": [
|
||||||
"ПН",
|
"ПН",
|
||||||
"ВТ",
|
"ВТ",
|
||||||
"СР",
|
"СР",
|
||||||
"ЧТ",
|
"ЧТ",
|
||||||
"ПТ",
|
"ПТ",
|
||||||
"СБ"
|
"СБ"
|
||||||
],
|
],
|
||||||
"aliases": {
|
"aliases": {
|
||||||
"Введение в НОК": "Введение в специальности Н.О.К.",
|
"Введение в НОК": "Введение в специальности Н.О.К.",
|
||||||
"Проф.электив. Введение в специальность стоматология": "Предпрофессиональный электив. Введение в специальность Стоматология"
|
"Проф.электив. Введение в специальность стоматология": "Предпрофессиональный электив. Введение в специальность Стоматология" ,
|
||||||
}
|
"Абделбаги Мохаммат абба нана": "Абделбаги Махаммат абба нана"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+406
-372
@@ -1,373 +1,407 @@
|
|||||||
import gspread
|
import gspread
|
||||||
import requests
|
import requests
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
from functools import lru_cache
|
import os
|
||||||
|
from functools import lru_cache
|
||||||
@lru_cache(None)
|
|
||||||
def partialize(s):
|
@lru_cache(None)
|
||||||
s = s.lower().replace("ё", "е")
|
def partialize(s):
|
||||||
p = re.split("[. ]+", s)
|
s = s.lower().replace("ё", "е")
|
||||||
return list(filter(lambda a: a, p))
|
p = re.split("[. ]+", s)
|
||||||
|
return list(filter(lambda a: a, p))
|
||||||
def test_short(short: str, full: str) -> bool:
|
|
||||||
short_parts = partialize(short)
|
def test_short(short: str, full: str) -> bool:
|
||||||
full_parts = partialize(full)
|
short_parts = partialize(short)
|
||||||
|
full_parts = partialize(full)
|
||||||
for i in range(min(len(short_parts), len(full_parts))):
|
|
||||||
if not full_parts[i].startswith(short_parts[i]):
|
for i in range(min(len(short_parts), len(full_parts))):
|
||||||
return False
|
if not full_parts[i].startswith(short_parts[i]):
|
||||||
|
return False
|
||||||
return True
|
|
||||||
|
return True
|
||||||
def find_in_list(short: str, full_list: list) -> str|list:
|
|
||||||
result = []
|
def find_in_list(short: str, full_list: list) -> str|list:
|
||||||
|
result = []
|
||||||
for item in full_list:
|
|
||||||
if test_short(short, item):
|
for item in full_list:
|
||||||
result.append(item)
|
if test_short(short, item):
|
||||||
|
result.append(item)
|
||||||
if len(result) == 1:
|
|
||||||
return result[0]
|
if len(result) == 1:
|
||||||
elif short in result:
|
return result[0]
|
||||||
return short
|
elif short in result:
|
||||||
else:
|
return short
|
||||||
return result
|
else:
|
||||||
|
return result
|
||||||
def find_teacher_by_date(input_string: str, target_date: str) -> str:
|
|
||||||
"""
|
def find_teacher_by_date(input_string: str, target_date: str) -> str:
|
||||||
Ищет преподавателя по целевой дате в строке с датами и ФИО.
|
"""
|
||||||
|
Ищет преподавателя по целевой дате в строке с датами и ФИО.
|
||||||
:param input_string: Строка с датами и ФИО преподавателей.
|
|
||||||
:param target_date: Целевая дата в формате "%d.%m".
|
:param input_string: Строка с датами и ФИО преподавателей.
|
||||||
:return: ФИО преподавателя, связанного с целевой датой, или None, если дата не найдена.
|
:param target_date: Целевая дата в формате "%d.%m".
|
||||||
"""
|
:return: ФИО преподавателя, связанного с целевой датой, или None, если дата не найдена.
|
||||||
# Преобразуем целевую дату в формат "%d.%m"
|
"""
|
||||||
target_date_str = target_date#.strftime("%d.%m")
|
# Преобразуем целевую дату в формат "%d.%m"
|
||||||
|
target_date_str = target_date#.strftime("%d.%m")
|
||||||
# Регулярное выражение для поиска дат в формате "%d.%m"
|
|
||||||
date_pattern = re.compile(r"\d{2}\.\d{2}")
|
# Регулярное выражение для поиска дат в формате "%d.%m"
|
||||||
|
date_pattern = re.compile(r"\d{2}\.\d{2}")
|
||||||
# Разделяем строку на строки по переносам
|
|
||||||
lines = re.split(" {5,}", input_string)
|
# Разделяем строку на строки по переносам
|
||||||
|
lines = re.split(" {5,}", input_string)
|
||||||
# Проходим по каждой строке
|
|
||||||
for i in range(len(lines)):
|
# Проходим по каждой строке
|
||||||
# Разделяем строку на даты и ФИО
|
for i in range(len(lines)):
|
||||||
parts = lines[i].strip().split()
|
# Разделяем строку на даты и ФИО
|
||||||
|
parts = lines[i].strip().split()
|
||||||
# Извлекаем даты и ФИО
|
|
||||||
dates = []
|
# Извлекаем даты и ФИО
|
||||||
teacher = ""
|
dates = []
|
||||||
for part in parts:
|
teacher = ""
|
||||||
if date_pattern.fullmatch(part.replace(",", "")): # Убираем запятые, если они есть
|
for part in parts:
|
||||||
dates.append(part.replace(",", "")) # Убираем запятые, если они есть
|
if date_pattern.fullmatch(part.replace(",", "")): # Убираем запятые, если они есть
|
||||||
else:
|
dates.append(part.replace(",", "")) # Убираем запятые, если они есть
|
||||||
teacher += part + " " # Собираем ФИО
|
else:
|
||||||
|
teacher += part + " " # Собираем ФИО
|
||||||
# Проверяем, есть ли целевая дата в списке дат
|
|
||||||
if target_date_str in dates:
|
# Проверяем, есть ли целевая дата в списке дат
|
||||||
return teacher.strip(), i # Возвращаем ФИО преподавателя
|
if target_date_str in dates:
|
||||||
|
return teacher.strip(), i # Возвращаем ФИО преподавателя
|
||||||
# Если дата не найдена
|
|
||||||
return input_string, 1
|
# Если дата не найдена
|
||||||
|
return input_string, 1
|
||||||
def shorten_name(name: str) -> str:
|
|
||||||
parts = name.split()
|
def shorten_name(name: str) -> str:
|
||||||
result = ""
|
parts = name.split()
|
||||||
|
result = ""
|
||||||
for i in range(len(parts)):
|
|
||||||
if i == 0:
|
for i in range(len(parts)):
|
||||||
result = parts[0] + " "
|
if i == 0:
|
||||||
else:
|
result = parts[0] + " "
|
||||||
result += parts[i][0] + "."
|
else:
|
||||||
|
result += parts[i][0] + "."
|
||||||
return result
|
|
||||||
|
return result
|
||||||
|
|
||||||
class Singleton(type):
|
|
||||||
_instances = {}
|
class Singleton(type):
|
||||||
def __call__(cls, *args, **kwargs):
|
_instances = {}
|
||||||
if cls not in cls._instances:
|
def __call__(cls, *args, **kwargs):
|
||||||
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
|
if cls not in cls._instances:
|
||||||
return cls._instances[cls]
|
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
|
||||||
|
return cls._instances[cls]
|
||||||
class Lecture:
|
|
||||||
def __init__(self, auditorium: str, day_of_week: str, pair_number: str, lecture: str, teacher: str, regular: bool = True):
|
class Lecture:
|
||||||
self.struct = Structure()
|
def __init__(self, auditorium: str, day_of_week: str, pair_number: str, lecture: str, teacher: str, regular: bool = True):
|
||||||
self.auditorium = auditorium
|
self.struct = Structure()
|
||||||
self.day_of_week = day_of_week
|
self.auditorium = auditorium
|
||||||
self.day_of_week_i = self.struct.settings["week"].index(day_of_week)
|
self.day_of_week = day_of_week
|
||||||
self.pair_number = pair_number
|
self.day_of_week_i = self.struct.settings["week"].index(day_of_week)
|
||||||
self.input_teacher = teacher
|
self.pair_number = pair_number
|
||||||
self.input_lecture = lecture
|
self.input_teacher = teacher
|
||||||
|
self.input_lecture = lecture
|
||||||
print("[Пара] ", self.auditorium, "/", self.day_of_week, "/", self.pair_number)
|
|
||||||
|
print("[Пара] ", self.auditorium, "/", self.day_of_week, "/", self.pair_number)
|
||||||
self.process_time()
|
|
||||||
|
self.process_time()
|
||||||
if regular:
|
|
||||||
self.teacher = self.process_teacher()
|
if regular:
|
||||||
if self.teacher == False:
|
self.teacher = self.process_teacher()
|
||||||
self.complete = False
|
if self.teacher == False:
|
||||||
print()
|
self.complete = False
|
||||||
return
|
print()
|
||||||
|
return
|
||||||
self.lecture = self.process_lecture()
|
|
||||||
if self.lecture == False:
|
self.lecture = self.process_lecture()
|
||||||
self.complete = False
|
if self.lecture == False:
|
||||||
print()
|
self.complete = False
|
||||||
return
|
print()
|
||||||
|
return
|
||||||
print("[Препод.] ", self.input_teacher, "->", self.teacher)
|
|
||||||
print("[Предмет] ", self.input_lecture, "->", self.lecture)
|
print("[Препод.] ", self.input_teacher, "->", self.teacher)
|
||||||
else:
|
print("[Предмет] ", self.input_lecture, "->", self.lecture)
|
||||||
self.teacher = self.input_teacher
|
else:
|
||||||
self.lecture = self.input_lecture
|
self.teacher = self.input_teacher
|
||||||
|
self.lecture = self.input_lecture
|
||||||
self.name = self.format_name()
|
|
||||||
self.complete = True
|
self.name = self.format_name()
|
||||||
|
self.complete = True
|
||||||
print()
|
|
||||||
|
print()
|
||||||
def process_time(self):
|
|
||||||
target_date = self.struct.monday + timedelta(days=self.day_of_week_i)
|
def process_time(self):
|
||||||
self.date = target_date.strftime("%d.%m")
|
target_date = self.struct.monday + timedelta(days=self.day_of_week_i)
|
||||||
|
self.date = target_date.strftime("%d.%m")
|
||||||
for call in self.struct.calls:
|
|
||||||
if call["lesson"] == self.pair_number:
|
for call in self.struct.calls:
|
||||||
start_time = datetime.fromisoformat("2025-01-01T" + call["start_time"])
|
if call["lesson"] == self.pair_number:
|
||||||
end_time = datetime.fromisoformat("2025-01-01T" + call["end_time"])
|
start_time = datetime.fromisoformat("2025-01-01T" + call["start_time"])
|
||||||
|
end_time = datetime.fromisoformat("2025-01-01T" + call["end_time"])
|
||||||
# Объединяем дату и время
|
|
||||||
self.start = target_date.replace(
|
# Объединяем дату и время
|
||||||
hour=start_time.hour,
|
self.start = target_date.replace(
|
||||||
minute=start_time.minute,
|
hour=start_time.hour,
|
||||||
second=start_time.second,
|
minute=start_time.minute,
|
||||||
microsecond=0,
|
second=start_time.second,
|
||||||
tzinfo=start_time.tzinfo
|
microsecond=0,
|
||||||
).isoformat(timespec="milliseconds")
|
tzinfo=start_time.tzinfo
|
||||||
self.end = target_date.replace(
|
).isoformat(timespec="milliseconds")
|
||||||
hour=end_time.hour,
|
self.end = target_date.replace(
|
||||||
minute=end_time.minute,
|
hour=end_time.hour,
|
||||||
second=end_time.second,
|
minute=end_time.minute,
|
||||||
microsecond=0,
|
second=end_time.second,
|
||||||
tzinfo=end_time.tzinfo
|
microsecond=0,
|
||||||
).isoformat(timespec="milliseconds")
|
tzinfo=end_time.tzinfo
|
||||||
def process_teacher(self) -> str:
|
).isoformat(timespec="milliseconds")
|
||||||
teacher, self.teacher_index = find_teacher_by_date(self.input_teacher, self.date)
|
def process_teacher(self) -> str:
|
||||||
|
teacher, self.teacher_index = find_teacher_by_date(self.input_teacher, self.date)
|
||||||
if teacher in self.struct.settings["aliases"].keys():
|
|
||||||
teacher = self.struct.settings["aliases"][teacher]
|
if teacher in self.struct.settings["aliases"].keys():
|
||||||
|
teacher = self.struct.settings["aliases"][teacher]
|
||||||
variants = find_in_list(teacher, self.struct.teachers)
|
|
||||||
|
variants = find_in_list(teacher, self.struct.teachers)
|
||||||
return self.select_variant(teacher, variants, True)
|
|
||||||
def process_lecture(self) -> str:
|
return self.select_variant(teacher, variants, True)
|
||||||
parts = re.split(" {5,}", self.input_lecture)
|
def process_lecture(self) -> str:
|
||||||
|
# parts = re.split(" {5,}", self.input_lecture)
|
||||||
if self.teacher_index < len(parts):
|
lecture, _ = find_teacher_by_date(self.input_lecture, self.date)
|
||||||
lecture = parts[self.teacher_index]
|
|
||||||
else:
|
# if self.teacher_index < len(parts):
|
||||||
lecture = parts[0]
|
# lecture = parts[self.teacher_index]
|
||||||
|
# else:
|
||||||
if lecture in self.struct.settings["aliases"].keys():
|
# lecture = parts[0]
|
||||||
lecture = self.struct.settings["aliases"][lecture]
|
|
||||||
|
if lecture in self.struct.settings["aliases"].keys():
|
||||||
variants = find_in_list(lecture, self.struct.disciplines)
|
lecture = self.struct.settings["aliases"][lecture]
|
||||||
|
|
||||||
return self.select_variant(lecture, variants, False)
|
variants = find_in_list(lecture, self.struct.disciplines)
|
||||||
|
|
||||||
def format_name(self) -> str:
|
return self.select_variant(lecture, variants, False)
|
||||||
formatted_date = datetime.fromisoformat(self.start).strftime("%H:%M %d.%m.%Y")
|
|
||||||
return f"{self.lecture} [{shorten_name(self.teacher)}] ({formatted_date})"
|
def format_name(self) -> str:
|
||||||
|
formatted_date = datetime.fromisoformat(self.start).strftime("%H:%M %d.%m.%Y")
|
||||||
def select_variant(self, search: str, variants: str|list, is_teacher: bool) -> bool|str:
|
return f"{self.lecture} [{shorten_name(self.teacher)}] ({formatted_date})"
|
||||||
if isinstance(variants, str):
|
|
||||||
return variants
|
def select_variant(self, search: str, variants: str|list, is_teacher: bool) -> bool|str:
|
||||||
|
if isinstance(variants, str):
|
||||||
print("Невозможно выбрать",
|
return variants
|
||||||
"преподавателя" if is_teacher else "лекцию",
|
|
||||||
"для значения",
|
print("Невозможно выбрать",
|
||||||
f'"{search}".',
|
"преподавателя" if is_teacher else "лекцию",
|
||||||
"Выберите вариант:" if len(variants) > 0 else "")
|
"для значения",
|
||||||
|
f'"{search}".',
|
||||||
if len(variants) == 0:
|
"Выберите вариант:" if len(variants) > 0 else "")
|
||||||
return False
|
|
||||||
|
if len(variants) == 0:
|
||||||
for i in range(len(variants)):
|
return False
|
||||||
print(str(i + 1).rjust(3) + ".", variants[i])
|
|
||||||
print(" 0. [Пропустить]")
|
for i in range(len(variants)):
|
||||||
|
print(str(i + 1).rjust(3) + ".", variants[i])
|
||||||
choice = int(input("Введите значение: ")) - 1
|
print(" 0. [Пропустить]")
|
||||||
|
|
||||||
if choice < 0:
|
choice = int(input("Введите значение: ")) - 1
|
||||||
return False
|
|
||||||
|
if choice < 0:
|
||||||
return variants[choice]
|
return False
|
||||||
|
|
||||||
def json(self) -> dict|None:
|
return variants[choice]
|
||||||
return {
|
|
||||||
"auditorium": self.auditorium,
|
def json(self) -> dict|None:
|
||||||
"day_of_week": self.day_of_week,
|
return {
|
||||||
"pair_number": self.pair_number,
|
"auditorium": self.auditorium,
|
||||||
"input_lecture": self.input_lecture,
|
"day_of_week": self.day_of_week,
|
||||||
"input_teacher": self.input_teacher,
|
"pair_number": self.pair_number,
|
||||||
|
"input_lecture": self.input_lecture,
|
||||||
|
"input_teacher": self.input_teacher,
|
||||||
"teacher": self.teacher,
|
|
||||||
"lecture": self.lecture,
|
|
||||||
"start": self.start,
|
"teacher": self.teacher,
|
||||||
"end": self.end,
|
"lecture": self.lecture,
|
||||||
"name": self.name
|
"start": self.start,
|
||||||
}
|
"end": self.end,
|
||||||
|
"name": self.name
|
||||||
class Structure(metaclass=Singleton):
|
}
|
||||||
def __init__(self):
|
|
||||||
print("Подключение Google Sheets...", end=" ")
|
class Structure(metaclass=Singleton):
|
||||||
gc = gspread.oauth()
|
def __init__(self):
|
||||||
self.document = gc.open_by_key("1srtrHkwbTCfvxyeDtb_ca4RMx8W1kxgUQ-MAIumamVA")
|
print("Подключение Google Sheets...", end=" ")
|
||||||
print("Готово.")
|
# gc = self.load_gspread()
|
||||||
|
# self.document = gc.open_by_key("1srtrHkwbTCfvxyeDtb_ca4RMx8W1kxgUQ-MAIumamVA")
|
||||||
print("Загрузка настроек...", end=" ")
|
self.document = self.load_gspread()
|
||||||
with open("settings.json", 'r', encoding='utf-8') as file:
|
print("Готово.")
|
||||||
self.settings = json.load(file)
|
|
||||||
print("Готово.")
|
print("Загрузка настроек...", end=" ")
|
||||||
|
with open("settings.json", 'r', encoding='utf-8') as file:
|
||||||
print("Загрузка списка преподавателей...", end=" ")
|
self.settings = json.load(file)
|
||||||
self.teachers = list(map(lambda a: a["name"], self.get_json("teachers")))
|
print("Готово.")
|
||||||
print("Готово.")
|
|
||||||
|
print("Загрузка списка преподавателей...", end=" ")
|
||||||
print("Загрузка списка дисциплин...", end=" ")
|
self.teachers = list(map(lambda a: a["name"], self.get_json("teachers")))
|
||||||
self.disciplines = list(map(lambda a: a["name"], self.get_json("disciplines")))
|
print("Готово.")
|
||||||
print("Готово.")
|
|
||||||
|
print("Загрузка списка дисциплин...", end=" ")
|
||||||
print("Загрузка расписания...", end=" ")
|
self.disciplines = list(map(lambda a: a["name"], self.get_json("disciplines")))
|
||||||
self.calls = self.get_json("calls", "schedule")
|
print("Готово.")
|
||||||
print("Готово.")
|
|
||||||
|
print("Загрузка расписания...", end=" ")
|
||||||
self.monday = self.get_monday()
|
self.calls = self.get_json("calls", "schedule")
|
||||||
self.week = self.get_week_number()
|
print("Готово.")
|
||||||
|
|
||||||
print("Данные загружены!")
|
self.monday = self.get_monday()
|
||||||
|
self.week = self.get_week_number()
|
||||||
def get_json(self, type, sub_type=None) -> dict:
|
|
||||||
resp = requests.get(url="https://ulsu.ru/oto/api/production/internal/getEnterpriseData.php?type=" + type, params={})
|
print("Данные загружены!")
|
||||||
return resp.json()[type if sub_type is None else sub_type]
|
|
||||||
|
def load_gspread(self) -> gspread.Spreadsheet:
|
||||||
def get_monday(self):
|
try:
|
||||||
today = datetime.today()
|
gc = gspread.oauth()
|
||||||
days_until_next_monday = (7 - today.weekday()) % 7
|
except:
|
||||||
|
self.remove_auth_data()
|
||||||
# Если сегодня понедельник, days_until_next_monday будет 0, поэтому добавляем 7 дней
|
gc = gspread.oauth()
|
||||||
# if days_until_next_monday == 0:
|
|
||||||
# days_until_next_monday = 7
|
try:
|
||||||
|
return gc.open_by_key("1srtrHkwbTCfvxyeDtb_ca4RMx8W1kxgUQ-MAIumamVA")
|
||||||
return today + timedelta(days=days_until_next_monday)
|
except:
|
||||||
def get_week_number(self, target_date:datetime|None=None):
|
self.remove_auth_data()
|
||||||
if target_date is None:
|
return gc.open_by_key("1srtrHkwbTCfvxyeDtb_ca4RMx8W1kxgUQ-MAIumamVA")
|
||||||
target_date = self.monday
|
|
||||||
|
def remove_auth_data(self):
|
||||||
first_week_start_date = datetime.strptime(self.settings["first_week"], "%Y-%m-%d")
|
appdata_path = os.environ.get('APPDATA')
|
||||||
|
file_path = os.path.join(appdata_path, "gspread", "authorized_user.json")
|
||||||
return ((target_date - first_week_start_date).days // 7) % 2 + 1
|
os.remove(file_path)
|
||||||
|
|
||||||
def get_worksheets(self):
|
|
||||||
return self.document.worksheets()
|
def get_json(self, type, sub_type=None) -> dict:
|
||||||
def get_document_data(self, worksheet, range_name=None, max_retries=5, initial_backoff=1, maximum_backoff=32):
|
resp = requests.get(url="https://ulsu.ru/oto/api/production/internal/getEnterpriseData.php?type=" + type, params={})
|
||||||
retries = 0
|
return resp.json()[type if sub_type is None else sub_type]
|
||||||
while retries < max_retries:
|
|
||||||
try:
|
def get_monday(self):
|
||||||
if range_name is None:
|
today = datetime.today()
|
||||||
return worksheet.get_all_values()
|
days_until_next_monday = (7 - today.weekday()) % 7
|
||||||
else:
|
|
||||||
return worksheet.get(range_name)
|
# Если сегодня понедельник, days_until_next_monday будет 0, поэтому добавляем 7 дней
|
||||||
except gspread.exceptions.APIError as e:
|
# if days_until_next_monday == 0:
|
||||||
retries += 1
|
# days_until_next_monday = 7
|
||||||
if retries == max_retries:
|
|
||||||
raise e
|
return today + timedelta(days=days_until_next_monday)
|
||||||
|
def get_week_number(self, target_date:datetime|None=None):
|
||||||
backoff = min(initial_backoff * (2 ** (retries - 1)) + random.random(), maximum_backoff)
|
if target_date is None:
|
||||||
time.sleep(backoff)
|
target_date = self.monday
|
||||||
def find_range(self, worksheet, first_week=True):
|
|
||||||
def check(cell):
|
first_week_start_date = datetime.strptime(self.settings["first_week"], "%Y-%m-%d")
|
||||||
if first_week:
|
|
||||||
return "1 неделя" in cell or "I неделя" in cell
|
return ((target_date - first_week_start_date).days // 7) % 2 + 1
|
||||||
else:
|
|
||||||
return "2 неделя" in cell or "II неделя" in cell
|
def get_worksheets(self):
|
||||||
|
return self.document.worksheets()
|
||||||
all_values = self.get_document_data(worksheet)
|
def get_document_data(self, worksheet, range_name=None, max_retries=5, initial_backoff=1, maximum_backoff=32):
|
||||||
|
retries = 0
|
||||||
week_row = None
|
while retries < max_retries:
|
||||||
for i, row in enumerate(all_values):
|
try:
|
||||||
for j, cell in enumerate(row):
|
if range_name is None:
|
||||||
if check(cell):
|
return worksheet.get_all_values()
|
||||||
week_row = i + 1
|
else:
|
||||||
break
|
return worksheet.get(range_name)
|
||||||
if week_row is not None:
|
except gspread.exceptions.APIError as e:
|
||||||
break
|
retries += 1
|
||||||
|
if retries == max_retries:
|
||||||
for row in range(week_row, worksheet.row_count + 1):
|
raise e
|
||||||
cell_value = all_values[row][0]
|
|
||||||
if cell_value == "1":
|
backoff = min(initial_backoff * (2 ** (retries - 1)) + random.random(), maximum_backoff)
|
||||||
return "C%d:H%d" % (row+1, row+16)
|
time.sleep(backoff)
|
||||||
|
def find_range(self, worksheet, first_week=True):
|
||||||
def get_lectures_from_range(self, worksheet, range_name) -> list:
|
def check(cell):
|
||||||
# Получаем название аудитории из названия листа
|
if first_week:
|
||||||
auditorium = worksheet.title
|
return "1 неделя" in cell or "I неделя" in cell
|
||||||
|
else:
|
||||||
# Получаем данные из указанного диапазона
|
return "2 неделя" in cell or "II неделя" in cell
|
||||||
data = self.get_document_data(worksheet, range_name)
|
|
||||||
|
all_values = self.get_document_data(worksheet)
|
||||||
|
|
||||||
if len(data) < 2:
|
week_row = None
|
||||||
return []
|
for i, row in enumerate(all_values):
|
||||||
|
for j, cell in enumerate(row):
|
||||||
# Дни недели
|
if check(cell):
|
||||||
days_of_week = self.settings["week"]
|
week_row = i + 1
|
||||||
day_order = {day: idx for idx, day in enumerate(days_of_week)} # Словарь для порядка дней
|
break
|
||||||
|
if week_row is not None:
|
||||||
# Список для хранения лекций
|
break
|
||||||
lectures = []
|
|
||||||
|
for row in range(week_row, worksheet.row_count + 1):
|
||||||
# Проходим по данным
|
cell_value = all_values[row][0]
|
||||||
for row_index in range(0, len(data), 2): # Шаг 2, так как каждая пара строк — это одна пара в расписании
|
if cell_value == "1":
|
||||||
lecture_row = data[row_index] # Строка с названием лекции
|
return "C%d:H%d" % (row+1, row+16)
|
||||||
teacher_row = data[row_index + 1] # Строка с преподавателем
|
|
||||||
|
def get_lectures_from_range(self, worksheet, range_name) -> list:
|
||||||
# Номер пары
|
# Получаем название аудитории из названия листа
|
||||||
pair_number = (row_index // 2) + 1
|
auditorium = worksheet.title
|
||||||
|
|
||||||
# Проходим по дням недели (столбцам)
|
# Получаем данные из указанного диапазона
|
||||||
for col_index, (lecture, teacher) in enumerate(zip(lecture_row, teacher_row)):
|
data = self.get_document_data(worksheet, range_name)
|
||||||
if lecture.strip() and teacher.strip(): # Если есть и название лекции, и преподаватель
|
|
||||||
day_of_week = days_of_week[col_index] # День недели
|
|
||||||
lectures.append(Lecture(auditorium, day_of_week, pair_number, lecture, teacher))
|
if len(data) < 2:
|
||||||
|
return []
|
||||||
lectures = sorted(lectures, key=lambda x: (x.day_of_week_i, x.pair_number))
|
|
||||||
|
# Дни недели
|
||||||
return lectures
|
days_of_week = self.settings["week"]
|
||||||
|
day_order = {day: idx for idx, day in enumerate(days_of_week)} # Словарь для порядка дней
|
||||||
|
|
||||||
|
# Список для хранения лекций
|
||||||
if __name__ == "__main__":
|
lectures = []
|
||||||
struct = Structure()
|
|
||||||
|
# Проходим по данным
|
||||||
week = struct.get_week_number()
|
for row_index in range(0, len(data), 2): # Шаг 2, так как каждая пара строк — это одна пара в расписании
|
||||||
|
lecture_row = data[row_index] # Строка с названием лекции
|
||||||
lectures = []
|
|
||||||
|
if row_index + 1 == len(data):
|
||||||
for worksheet in struct.get_worksheets():
|
print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
|
||||||
print(worksheet.title + "...")
|
print(f"Ошибка с данными, row_index = {row_index + 1} не существует!")
|
||||||
range_name = struct.find_range(worksheet, week == 1)
|
print(data)
|
||||||
part = struct.get_lectures_from_range(worksheet, range_name)
|
print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
|
||||||
|
continue
|
||||||
lectures.extend(part)
|
|
||||||
|
teacher_row = data[row_index + 1] # Строка с преподавателем
|
||||||
lectures = list(filter(lambda l: l.complete, lectures))
|
|
||||||
|
# Номер пары
|
||||||
with open("lecture.json", "w", encoding="utf-8") as f:
|
pair_number = (row_index // 2) + 1
|
||||||
|
|
||||||
|
# Проходим по дням недели (столбцам)
|
||||||
|
for col_index, (lecture, teacher) in enumerate(zip(lecture_row, teacher_row)):
|
||||||
|
if lecture.strip() and teacher.strip(): # Если есть и название лекции, и преподаватель
|
||||||
|
day_of_week = days_of_week[col_index] # День недели
|
||||||
|
|
||||||
|
# if col_index > 3:
|
||||||
|
# continue
|
||||||
|
|
||||||
|
lectures.append(Lecture(auditorium, day_of_week, pair_number, lecture, teacher))
|
||||||
|
|
||||||
|
lectures = sorted(lectures, key=lambda x: (x.day_of_week_i, x.pair_number))
|
||||||
|
|
||||||
|
return lectures
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
struct = Structure()
|
||||||
|
|
||||||
|
week = struct.get_week_number()
|
||||||
|
|
||||||
|
lectures = []
|
||||||
|
|
||||||
|
for worksheet in struct.get_worksheets():
|
||||||
|
print(worksheet.title + "...")
|
||||||
|
range_name = struct.find_range(worksheet, week == 1)
|
||||||
|
part = struct.get_lectures_from_range(worksheet, range_name)
|
||||||
|
|
||||||
|
lectures.extend(part)
|
||||||
|
|
||||||
|
lectures = list(filter(lambda l: l.complete, lectures))
|
||||||
|
|
||||||
|
with open("lecture.json", "w", encoding="utf-8") as f:
|
||||||
json.dump(list(map(lambda l: l.json(), lectures)), f, ensure_ascii=False, indent=4)
|
json.dump(list(map(lambda l: l.json(), lectures)), f, ensure_ascii=False, indent=4)
|
||||||
@@ -1,75 +1,75 @@
|
|||||||
import re
|
import re
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
|
|
||||||
@lru_cache(None)
|
@lru_cache(None)
|
||||||
def partialize(s):
|
def partialize(s):
|
||||||
s = s.lower().replace("ё", "е")
|
s = s.lower().replace("ё", "е")
|
||||||
p = re.split("[. ]+", s)
|
p = re.split("[. ]+", s)
|
||||||
return list(filter(lambda a: a, p))
|
return list(filter(lambda a: a, p))
|
||||||
|
|
||||||
def test_short(short: str, full: str) -> bool:
|
def test_short(short: str, full: str) -> bool:
|
||||||
short_parts = partialize(short)
|
short_parts = partialize(short)
|
||||||
full_parts = partialize(full)
|
full_parts = partialize(full)
|
||||||
|
|
||||||
for i in range(min(len(short_parts), len(full_parts))):
|
for i in range(min(len(short_parts), len(full_parts))):
|
||||||
if not full_parts[i].startswith(short_parts[i]):
|
if not full_parts[i].startswith(short_parts[i]):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def find_in_list(short, full_list):
|
def find_in_list(short, full_list):
|
||||||
result = []
|
result = []
|
||||||
|
|
||||||
for item in full_list:
|
for item in full_list:
|
||||||
if test_short(short, item):
|
if test_short(short, item):
|
||||||
result.append(item)
|
result.append(item)
|
||||||
|
|
||||||
if len(result) == 1:
|
if len(result) == 1:
|
||||||
return result[0]
|
return result[0]
|
||||||
elif short in result:
|
elif short in result:
|
||||||
return short
|
return short
|
||||||
else:
|
else:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def find_teacher_by_date(input_string, target_date):
|
def find_teacher_by_date(input_string, target_date):
|
||||||
"""
|
"""
|
||||||
Ищет преподавателя по целевой дате в строке с датами и ФИО.
|
Ищет преподавателя по целевой дате в строке с датами и ФИО.
|
||||||
|
|
||||||
:param input_string: Строка с датами и ФИО преподавателей.
|
:param input_string: Строка с датами и ФИО преподавателей.
|
||||||
:param target_date: Целевая дата в формате "%d.%m".
|
:param target_date: Целевая дата в формате "%d.%m".
|
||||||
:return: ФИО преподавателя, связанного с целевой датой, или None, если дата не найдена.
|
:return: ФИО преподавателя, связанного с целевой датой, или None, если дата не найдена.
|
||||||
"""
|
"""
|
||||||
# Преобразуем целевую дату в формат "%d.%m"
|
# Преобразуем целевую дату в формат "%d.%m"
|
||||||
target_date_str = target_date#.strftime("%d.%m")
|
target_date_str = target_date#.strftime("%d.%m")
|
||||||
|
|
||||||
# Регулярное выражение для поиска дат в формате "%d.%m"
|
# Регулярное выражение для поиска дат в формате "%d.%m"
|
||||||
date_pattern = re.compile(r"\d{2}\.\d{2}")
|
date_pattern = re.compile(r"\d{2}\.\d{2}")
|
||||||
|
|
||||||
# Разделяем строку на строки по переносам
|
# Разделяем строку на строки по переносам
|
||||||
lines = re.split(" {5,}", input_string)
|
lines = re.split(" {5,}", input_string)
|
||||||
|
|
||||||
# Проходим по каждой строке
|
# Проходим по каждой строке
|
||||||
for line in lines:
|
for line in lines:
|
||||||
# Разделяем строку на даты и ФИО
|
# Разделяем строку на даты и ФИО
|
||||||
parts = line.strip().split()
|
parts = line.strip().split()
|
||||||
|
|
||||||
# Извлекаем даты и ФИО
|
# Извлекаем даты и ФИО
|
||||||
dates = []
|
dates = []
|
||||||
teacher = ""
|
teacher = ""
|
||||||
for part in parts:
|
for part in parts:
|
||||||
if date_pattern.fullmatch(part.replace(",", "")): # Убираем запятые, если они есть
|
if date_pattern.fullmatch(part.replace(",", "")): # Убираем запятые, если они есть
|
||||||
dates.append(part.replace(",", "")) # Убираем запятые, если они есть
|
dates.append(part.replace(",", "")) # Убираем запятые, если они есть
|
||||||
else:
|
else:
|
||||||
teacher += part + " " # Собираем ФИО
|
teacher += part + " " # Собираем ФИО
|
||||||
|
|
||||||
# Проверяем, есть ли целевая дата в списке дат
|
# Проверяем, есть ли целевая дата в списке дат
|
||||||
if target_date_str in dates:
|
if target_date_str in dates:
|
||||||
return teacher.strip() # Возвращаем ФИО преподавателя
|
return teacher.strip() # Возвращаем ФИО преподавателя
|
||||||
|
|
||||||
# Если дата не найдена
|
# Если дата не найдена
|
||||||
return input_string
|
return input_string
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# print(test_short("кузнецов в.н.", "Кузнецов Владимир Николаевич"))
|
# print(test_short("кузнецов в.н.", "Кузнецов Владимир Николаевич"))
|
||||||
s = " 10.02, 24.02, 10.03 Абдусалямова Л.И . 27.01, 07.04 Голубцова Т.С. 24.03 Шаршова С.М. "
|
s = " 10.02, 24.02, 10.03 Абдусалямова Л.И . 27.01, 07.04 Голубцова Т.С. 24.03 Шаршова С.М. "
|
||||||
print(find_teacher_by_date(s, "10.03"))
|
print(find_teacher_by_date(s, "10.03"))
|
||||||
Reference in New Issue
Block a user