refactor: big, more patterns\n\nBREAKING CHANGES

This commit is contained in:
2026-03-16 20:53:42 +03:00
parent 2105e9bc36
commit 7e0e4a0b71
5 changed files with 416 additions and 244 deletions

131
main.py
View File

@@ -5,6 +5,7 @@
import json
import os
import random
import time
import traceback
import uuid
@@ -26,50 +27,75 @@ FACULTETS = sorted([
DIRNAME = "excels"
DIFFABLE_DATES = "diffable_dates.txt"
SKIP_DIFFABLE_DATES = True
DEBUG_ONE_FAC = None #'fevt'
result_groups = {}
LOGGING = False
unique_raws = set()
result = {
"version": 1,
"notice": "ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Данные, доступ к API и т.д. предоставляется КАК-ЕСТЬ (AS-IS) без каких либо, явно или не явно подразумеваемых гарантий.\n\nПарсер написал: Миронов Станислав\n\nИсточник данных: https://www.vstu.ru/student/raspisaniya/zanyatiy/index.php",
"actual_at": round(time.time()),
"documentation": "https://fazziclay.com/api/v1/vstu_schedule_parser/scheme.json",
"daypicture": "QwQ",
"daycite": "running on a rope",
"documentation": "https://fazziclay.com/api/v1/vstu_schedule_parser/scheme.json (temporary outdated)",
"daypicture": "0w0",
"daycite": "KIlLSWITCH",
"contact": "https://fazziclay.com/",
"university": "VSTU",
"university_site": "https://www.vstu.ru/",
"source": "https://fazziclay.com/api/v1/vstu_schedule_parser/result.json",
"stat": {
"total_parsing_time": -1,
"excels": {
"fine": 0,
"bad": 0
},
"groups": 0,
"unique_raws": -1
},
"api_notices": {
"updated_at": 1759651871,
"text": "Пожалуйста сохраняйте 'updated_at', это время изменения ЭТОГО текста. Тут возможно будут появлятся важные BREAKING CHANGES и дедлайны к ним.\nПо хорошему если updated_at другой по сравнению с вашем кэшем это сообщение должно отправляться вам в телеграм как уведомление о поедстоящих изменениях\nwarning=True значит 'text' содержит важное а не как щас hint.\n\n ~fazziclay aka Stanislav;\n\n2025-10-05: добавлено data_source_hash в эксель и в группу. Это SHA1 of скачанный эксель файл.",
"warning": False,
"updated_at": 1773523692,
"text_pre1": "Пожалуйста сохраняйте 'updated_at', это время изменения ЭТОГО текста. Тут возможно будут появлятся важные BREAKING CHANGES и дедлайны к ним.\nПо хорошему если updated_at другой по сравнению с вашем кэшем это сообщение должно отправляться вам в телеграм как уведомление о поедстоящих изменениях\nwarning=True значит 'text' содержит важное а не как щас hint.\n\n ~fazziclay aka Stanislav;\n\n2025-10-05: добавлено data_source_hash в эксель и в группу. Это SHA1 of скачанный эксель файл.",
"text": "2026-03-15 BREAKING CHANGES! By Stanislav Mironov.\n\nИзменено многое в угоду унифкации и расширению спаршенных групп. Пока alpha",
"warning": True,
"tut-plavayuschaya-struktura": "required only 'updated_at', 'text' and 'warning'"
},
"doubled_groups": [],
"debug": {
"bleu~~": 2
"bleu~~": 3
},
"excels": [],
"facultets": FACULTETS,
"emptykey1": "",
"emptykey2": "",
"groups": result_groups,
"emptykey3": "",
"emptykey4": "",
"group_names_parsed": [],
"unique_raws": unique_raws,
"see_header_at_top_of_this_file": "SEE TOP OF THIS FILE | ОБРАТИТЕ ВНИМАНИЕ НА ВЕРХ ЭТОГО ФАЙЛА"
}
def process_obj(data):
try:
if isinstance(data, dict):
for key, value in data.items():
if key == "raw":
unique_raws.update(value)
process_obj(value)
# Если это список, проходим по его элементам
elif isinstance(data, list):
for item in data:
process_obj(item)
except Exception as e:
print("Failed process_obj")
print(e)
def process_excel_file(facultet, excel_url, counter, latest_changed):
is_xlsx = excel_url.endswith(".xlsx")
download_place = f"{DIRNAME}/" + f"_[C{counter}]_" + facultet + ".xls" + ("x" if is_xlsx else "")
excel_filename = excel_url.split("/")[-1]
if "ФЭУ" not in excel_filename:
print("SKIPPED")
return
excel_info = {
"filename": excel_filename,
@@ -80,9 +106,9 @@ def process_excel_file(facultet, excel_url, counter, latest_changed):
"group_names_parsed": [],
"facultet": facultet,
"counter": counter,
"week_keys_metadata": {}
"sheets": []
}
parser.LOGGING = False
parser.LOGGING = LOGGING
try:
aigenerated.download_file_from_url(excel_url, download_place)
@@ -94,44 +120,45 @@ def process_excel_file(facultet, excel_url, counter, latest_changed):
while True:
print(f"Parsing sheet №{reader.get_sheet_index()+1} (from 1)")
sheet_dict = {
"index": reader.get_sheet_index(),
"name": reader.get_sheet_name(),
"reader_info": reader.info(),
"group_names_parsed": [],
"groups": {}
}
excel_info['sheets'].append(sheet_dict)
prs = parser.Parser(reader)
print("Parser created; parser.parse();")
prs.parse()
print("parsed done!")
if len(prs.raw_no_schedule) > 0:
sheet_dict["raw_no_schedule"] = prs.raw_no_schedule
if len(prs.features) > 0:
sheet_dict["features"] = sorted(prs.features)
if prs.parser_error is not None:
excel_info["parser_error_cycle_" + str(reader.get_sheet_index()+1)] = prs.parser_error
sheet_dict["parser_error"] = prs.parser_error
if prs.parser_warnings is not None and len(prs.parser_warnings) > 0:
excel_info["parser_warnings_cycle_" + str(reader.get_sheet_index()+1)] = prs.parser_warnings
sheet_dict["parser_warnings"] = prs.parser_warnings
for group_name in prs.groups.keys():
if group_name in result_groups.keys():
print(f" -- WTF -- Doubled groups -- name: {group_name}")
if 'warning_doubled_groups_skip' not in excel_info.keys():
excel_info['warning_doubled_groups_skip'] = []
excel_info['warning_doubled_groups_skip'].append(group_name)
result['doubled_groups'].append(group_name)
continue
gr = result_groups[group_name] = prs.groups[group_name]
gr['facultet'] = facultet
gr['data_source'] = excel_filename # same as 'filename' in excel_info's
gr['data_source_hash'] = sha1hash
gr['debug'] = {
"excel_url": excel_url,
"reader_info": reader.info(),
"reader_sheet_index": reader.get_sheet_index(),
"download_place": download_place
}
gr = prs.groups[group_name]
gr["excel_url"] = excel_url
sheet_dict["group_names_parsed"].append(group_name)
excel_info["group_names_parsed"].append(group_name)
excel_info['week_keys_metadata'] = prs.week_keys_metadata
result["group_names_parsed"].append(group_name)
result['stat']['groups'] += 1
sheet_dict['week_keys_metadata'] = prs.week_keys_metadata
sheet_dict['groups'][group_name] = gr
process_obj(gr['slots'])
print(f"Populates {len(prs.groups)} groups to result: " + " ".join(prs.groups.keys()))
print(f"Populates {len(prs.groups)} groups: " + " ".join(prs.groups.keys()))
if not reader.has_next_sheet():
print("File ended")
@@ -159,10 +186,12 @@ def process_excel_file(facultet, excel_url, counter, latest_changed):
})
result['excels'].append(excel_info)
k = "fine" if len(excel_info['group_names_parsed']) > 0 else "bad"
result['stat']['excels'][k] += 1
faileds = []
def main():
global result_groups, result
global result
t = utils.StepTimeCounter()
try:
try:
@@ -189,7 +218,9 @@ def main():
if now_diffable_dates == prev_diffable_dates:
print("No date changes in vstu.ru website. Stopping")
return
if not SKIP_DIFFABLE_DATES:
return
print("SKIP_DIFFABLE_DATES is True, force resuming")
counter = 10000
for excel_link in EXCEL_LINKS:
@@ -200,14 +231,8 @@ def main():
process_excel_file(facultet, excel_url, counter, latest_changed)
print("Saving result.json")
group_names_alphabeticaly = sorted(result_groups.keys())
sorted_groups = {}
for group_name in group_names_alphabeticaly:
sorted_groups[group_name] = result_groups[group_name]
result['groups'] = sorted_groups
result['stat']['total_parsing_time'] = t.step()
result['unique_raws'] = sorted(unique_raws)
json.dump(result, open('result.json', 'w'), indent=2, ensure_ascii=False)
print("Saved to result.json indent=2")