Files
VSTU_Schedule_Parser/utils.py
2026-03-18 22:15:49 +03:00

202 lines
7.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Copyright Stanislav Mironov
import time
import xlrd
from coord import Coord
from translations import ExcelSheetReader
import hashlib
import requests
from urllib.parse import urlsplit, urlunsplit, quote
def download_file_from_url(url, output_filename):
"""
Скачивает файл по URL со спецсимволами и пробелами, сохраняя его под указанным именем.
Args:
url (str): Исходный URL, который может содержать пробелы и кириллицу.
output_filename (str): Имя файла для сохранения (например, 'calc.xls').
"""
try:
# --- Шаг 1: Правильное кодирование URL ---
# Разбираем URL на части: ('https', 'www.vstu.ru', '/path/to file.xls', '', '')
parts = urlsplit(url)
# Кодируем только путь, оставляя слэши '/' безопасными
# Это превратит ' ' в '%20', 'В' в '%D0%92' и т.д.
encoded_path = quote(parts.path, safe='/-_')
# Собираем URL обратно из частей с уже закодированным путем
encoded_url = urlunsplit((parts.scheme, parts.netloc, encoded_path, parts.query, parts.fragment))
# --- Шаг 2: Скачивание файла ---
response = requests.get(encoded_url, stream=True)
# Проверяем, успешен ли запрос (код 200 OK)
# Если сервер вернет ошибку (404, 500 и т.д.), здесь возникнет исключение
response.raise_for_status()
# --- Шаг 3: Сохранение файла ---
# Открываем файл для записи в бинарном режиме ('wb')
# Использование 'with' гарантирует, что файл будет закрыт автоматически
with open(output_filename, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
print(f"✅ Файл успешно скачан и сохранен как '{output_filename}'")
except requests.exceptions.RequestException as e:
print(f"❌ Ошибка скачивания: {e}")
except Exception as e:
print(f"❌ Произошла непредвиденная ошибка: {e}")
def calculate_sha1(filepath):
"""
Calculates the SHA1 hash of a given file.
Args:
filepath (str): The path to the file.
Returns:
str: The hexadecimal representation of the SHA1 hash, or None if the file is not found.
"""
sha1_hash = hashlib.sha1()
try:
with open(filepath, "rb") as f:
# Read the file in chunks to handle large files efficiently
for chunk in iter(lambda: f.read(4096), b""):
sha1_hash.update(chunk)
return sha1_hash.hexdigest()
except FileNotFoundError:
print(f"Error: File not found at {filepath}")
return None
except Exception as e:
print(f"An error occurred: {e}")
return None
class StepTimeCounter:
def __init__(self):
self.time: float = -1.0
self.createtime = time.time()
self.setnow()
def setnow(self):
self.time = time.time()
def step(self, no_set_now=False):
left = time.time() - self.time
if not no_set_now:
self.setnow()
return left
def from_create(self):
left = time.time() - self.createtime
return left
EMPTY_CTYPES = [xlrd.XL_CELL_EMPTY, xlrd.XL_CELL_BLANK]
def discards_list(trg, nones=True, emptystrings=True):
if nones: remove_from_list(trg, [None])
if emptystrings: remove_from_list(trg, [""])
def has_no_bottom_border(reader: "ExcelSheetReader", coord):
return reader.get_border_style(coord, 'bottom') == 0 and reader.get_border_style(coord.shift(down=1), 'top') == 0
def find_element_index(my_list, element):
if element in my_list:
return my_list.index(element)
else:
return -1
def next_element(arr, el):
index = find_element_index(arr, el)
return arr[index + 1]
def remove_from_list(l: list, todel: list):
for x in todel:
if x in l:
l.remove(x)
return l
def parse_all_dirt(reader: "ExcelSheetReader", min_pos: Coord, right, down, with_cells=False):
RET = set()
row = min_pos.row
while row < min_pos.row + down:
col = min_pos.col
while col < min_pos.col + right:
#print(excel_coordinate(row, col))
cv = reader.cell(row, col)
if cv is not None and not cv.is_empty():
RET.add(cv if with_cells else str(cv.value))
col += 1
row += 1
return RET
def excel_coordinate(row, col):
"""
Преобразует координаты строки и столбца (начиная с 0) в эквивалент Excel (например, A7, CB34).
Args:
row: Индекс строки (начиная с 0).
col: Индекс столбца (начиная с 0).
Returns:
Строка, представляющая координату ячейки в стиле Excel.
~ Google Gemini, tested
"""
col_str = ''
while col >= 0:
col_str = chr(ord('A') + col % 26) + col_str # Преобразуем в буквы, начиная с A
col = col // 26 - 1 # Уменьшаем номер столбца и учитываем переход к следующему разряду (как в 26-ричной системе)
return col_str + str(row + 1) # Добавляем номер строки (Excel начинается с 1)
def merged_humanize(crange):
"""Получить из 4 цифр границ AA:BB координаты как в Excel"""
row_low, col_low, row_high, col_high = crange # see order!
return excel_coordinate(row_low, col_low) + ":" + excel_coordinate(row_high, col_high)
def unspace(s: str):
"""Убрать пробелы из текста"""
if s is None:
return "!!!Python None!!!"
return s.strip().replace(" ", "").replace("\t", "")
def find(sh, query = None):
for rx in range(sh.nrows):
i = 0
for x in sh.row(rx):
if x.value == query:
return rx, i
i += 1
return None
def weekday_to_num(st: str):
if st.upper().strip().startswith("ПОНЕД"):
return 1
if st.upper().strip() == "ВТОРНИК":
return 2
if st.upper().strip() == "СРЕДА":
return 3
if st.upper().strip() == "ЧЕТВЕРГ":
return 4
if st.upper().strip() == "ПЯТНИЦА":
return 5
if st.upper().strip() == "СУББОТА":
return 6
if st.upper().strip().startswith("ВОСКР"):
return 7
print(f"Unknown weekday num for str: {st}; returnted -1")
return -1