201 lines
8.5 KiB
Python
201 lines
8.5 KiB
Python
import gspread
|
|
import requests
|
|
from datetime import datetime, timedelta
|
|
import json
|
|
import random
|
|
import time
|
|
from util import find_in_list
|
|
|
|
class Structure:
|
|
def __init__(self):
|
|
print("Подключение Google Sheets...", end=" ")
|
|
gc = gspread.oauth()
|
|
self.document = gc.open_by_key("1srtrHkwbTCfvxyeDtb_ca4RMx8W1kxgUQ-MAIumamVA")
|
|
print("Готово.")
|
|
|
|
print("Загрузка настроек...", end=" ")
|
|
with open("settings.json", 'r', encoding='utf-8') as file:
|
|
self.settings = json.load(file)
|
|
print("Готово.")
|
|
|
|
print("Загрузка списка преподавателей...", end=" ")
|
|
self.teachers = list(map(lambda a: a["name"], self.get_json("teachers")))
|
|
print("Готово.")
|
|
|
|
print("Загрузка списка дисциплин...", end=" ")
|
|
self.disciplines = list(map(lambda a: a["name"], self.get_json("disciplines")))
|
|
print("Готово.")
|
|
|
|
print("Загрузка расписания...", end=" ")
|
|
self.calls = self.get_json("calls", "schedule")
|
|
print("Готово.")
|
|
|
|
print("Данные загружены!")
|
|
|
|
|
|
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={})
|
|
return resp.json()[type if sub_type is None else sub_type]
|
|
|
|
def get_week_number(self, target_date=None):
|
|
if target_date is None:
|
|
target_date = self.get_monday()
|
|
|
|
target_date = datetime.strptime(target_date, "%Y-%m-%d")
|
|
first_week_start_date = datetime.strptime(self.settings["first_week"], "%Y-%m-%d")
|
|
|
|
return ((target_date - first_week_start_date).days // 7) % 2 + 1
|
|
|
|
def get_monday(self):
|
|
today = datetime.today()
|
|
days_until_next_monday = (7 - today.weekday()) % 7
|
|
|
|
# Если сегодня понедельник, days_until_next_monday будет 0, поэтому добавляем 7 дней
|
|
# if days_until_next_monday == 0:
|
|
# days_until_next_monday = 7
|
|
|
|
# Вычисляем дату следующего понедельника
|
|
next_monday = today + timedelta(days=days_until_next_monday)
|
|
|
|
# Возвращаем дату в формате "%Y-%m-%d"
|
|
return next_monday.strftime("%Y-%m-%d")
|
|
|
|
|
|
def get_worksheets(self):
|
|
return self.document.worksheets()
|
|
|
|
def get_document_data(self, worksheet, range_name=None, max_retries=5, initial_backoff=1, maximum_backoff=32):
|
|
retries = 0
|
|
while retries < max_retries:
|
|
try:
|
|
if range_name is None:
|
|
return worksheet.get_all_values()
|
|
else:
|
|
return worksheet.get(range_name)
|
|
except gspread.exceptions.APIError as e:
|
|
retries += 1
|
|
if retries == max_retries:
|
|
raise e
|
|
|
|
backoff = min(initial_backoff * (2 ** (retries - 1)) + random.random(), maximum_backoff)
|
|
time.sleep(backoff)
|
|
|
|
|
|
def find_range(self, worksheet, first_week=True):
|
|
def check(cell):
|
|
if first_week:
|
|
return "1 неделя" in cell or "I неделя" in cell
|
|
else:
|
|
return "2 неделя" in cell or "II неделя" in cell
|
|
|
|
all_values = self.get_document_data(worksheet)
|
|
|
|
week_row = None
|
|
for i, row in enumerate(all_values):
|
|
for j, cell in enumerate(row):
|
|
if check(cell):
|
|
week_row = i + 1
|
|
break
|
|
if week_row is not None:
|
|
break
|
|
|
|
for row in range(week_row, worksheet.row_count + 1):
|
|
cell_value = all_values[row][0]
|
|
if cell_value == "1":
|
|
return "C%d:H%d" % (row+1, row+16)
|
|
|
|
def get_lectures_from_range(self, worksheet, range_name):
|
|
# Получаем название аудитории из названия листа
|
|
auditorium = worksheet.title
|
|
|
|
# Получаем данные из указанного диапазона
|
|
data = self.get_document_data(worksheet, range_name)
|
|
|
|
|
|
if len(data) < 2:
|
|
return []
|
|
|
|
# Дни недели
|
|
days_of_week = self.settings["week"]
|
|
day_order = {day: idx for idx, day in enumerate(days_of_week)} # Словарь для порядка дней
|
|
|
|
# Список для хранения лекций
|
|
lectures = []
|
|
|
|
# Проходим по данным
|
|
for row_index in range(0, len(data), 2): # Шаг 2, так как каждая пара строк — это одна пара в расписании
|
|
lecture_row = data[row_index] # Строка с названием лекции
|
|
teacher_row = data[row_index + 1] # Строка с преподавателем
|
|
|
|
# Номер пары
|
|
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] # День недели
|
|
lectures.append({
|
|
"auditorium": auditorium,
|
|
"day_of_week": day_of_week,
|
|
"pair_number": pair_number,
|
|
"lecture": lecture,
|
|
"teacher": teacher
|
|
})
|
|
|
|
lectures = sorted(lectures, key=lambda x: (day_order[x["day_of_week"]], x["pair_number"]))
|
|
|
|
return lectures
|
|
|
|
|
|
|
|
def find_teachers(self, short_name):
|
|
return find_in_list(short_name, self.teachers)
|
|
|
|
def find_disciplines(self, short_name):
|
|
return find_in_list(short_name, self.disciplines)
|
|
|
|
|
|
def get_lecture_time(self, day_of_week, pair_number, monday=None):
|
|
"""
|
|
Возвращает время начала и окончания лекции для указанного дня недели и номера пары.
|
|
|
|
:param day_of_week: День недели (0 - понедельник, 1 - вторник, ..., 6 - воскресенье).
|
|
:param pair_number: Номер пары (начиная с 1).
|
|
:return: Кортеж (start_time, end_time) в формате "2025-03-09T08:00:00.000+04:00".
|
|
"""
|
|
if monday is None:
|
|
monday = datetime.strptime(self.get_monday(), "%Y-%m-%d")
|
|
|
|
# Определяем дату для указанного дня недели
|
|
target_date = monday + timedelta(days=self.settings["week"].index(day_of_week))
|
|
|
|
# Находим время для указанной пары
|
|
for call in self.calls:
|
|
if call["lesson"] == pair_number:
|
|
start_time = datetime.fromisoformat("2025-01-01T" + call["start_time"])
|
|
end_time = datetime.fromisoformat("2025-01-01T" + call["end_time"])
|
|
|
|
# Объединяем дату и время
|
|
start_datetime = target_date.replace(
|
|
hour=start_time.hour,
|
|
minute=start_time.minute,
|
|
second=start_time.second,
|
|
microsecond=0,
|
|
tzinfo=start_time.tzinfo
|
|
)
|
|
end_datetime = target_date.replace(
|
|
hour=end_time.hour,
|
|
minute=end_time.minute,
|
|
second=end_time.second,
|
|
microsecond=0,
|
|
tzinfo=end_time.tzinfo
|
|
)
|
|
|
|
# Форматируем результат
|
|
return (
|
|
start_datetime.isoformat(timespec="milliseconds"),
|
|
end_datetime.isoformat(timespec="milliseconds")
|
|
)
|
|
|
|
# Если пара не найдена
|
|
raise ValueError(f"Пара с номером {pair_number} не найдена.") |