Files
ktalk-scheduler/structure.py
T
2025-03-07 14:52:59 +04:00

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} не найдена.")