|
|
import json |
|
|
import os |
|
|
import sys |
|
|
from typing import List, Dict |
|
|
from scraper.html_scraper import HTMLScraper |
|
|
from scraper.pdf_parser import PDFParser |
|
|
from scraper.normalize import DataNormalizer |
|
|
from knowledge_base import KnowledgeBase |
|
|
from retriever import Retriever |
|
|
|
|
|
def update_data_async(): |
|
|
try: |
|
|
print('Начинаем обновление данных с сайтов ITMO...') |
|
|
|
|
|
|
|
|
scraper = HTMLScraper() |
|
|
programs = scraper.scrape_programs() |
|
|
scraper.save_programs(programs) |
|
|
|
|
|
if not programs: |
|
|
print('Не удалось получить данные программ, используем тестовые данные') |
|
|
knowledge_base = KnowledgeBase() |
|
|
retriever = Retriever() |
|
|
retriever.build_or_load_index(knowledge_base.courses) |
|
|
return |
|
|
|
|
|
|
|
|
pdf_parser = PDFParser() |
|
|
all_courses = [] |
|
|
|
|
|
for program_id, program in programs.items(): |
|
|
print(f'\nОбработка программы: {program["title"]}') |
|
|
|
|
|
if not program.get('pdf_links'): |
|
|
print(f'PDF ссылки не найдены для программы {program_id}') |
|
|
continue |
|
|
|
|
|
for pdf_link in program['pdf_links']: |
|
|
try: |
|
|
filename = pdf_link['filename'] |
|
|
url = pdf_link['url'] |
|
|
|
|
|
print(f'Скачивание PDF: {filename}') |
|
|
local_path = pdf_parser.download_pdf(url, filename) |
|
|
|
|
|
if local_path: |
|
|
print(f'Парсинг PDF: {filename}') |
|
|
courses = pdf_parser.parse_pdf(local_path, program_id) |
|
|
all_courses.extend(courses) |
|
|
print(f'Извлечено курсов из {filename}: {len(courses)}') |
|
|
else: |
|
|
print(f'Не удалось скачать PDF: {filename}') |
|
|
|
|
|
except Exception as e: |
|
|
print(f'Ошибка обработки PDF {pdf_link["filename"]}: {e}') |
|
|
|
|
|
|
|
|
if all_courses: |
|
|
print(f'\nНормализация {len(all_courses)} курсов...') |
|
|
normalizer = DataNormalizer() |
|
|
normalized_courses = normalizer.normalize_courses(all_courses) |
|
|
enriched_courses = normalizer.enrich_courses(normalized_courses) |
|
|
|
|
|
|
|
|
save_courses(enriched_courses) |
|
|
|
|
|
|
|
|
print('Создание индекса...') |
|
|
retriever = Retriever() |
|
|
retriever.build_or_load_index(enriched_courses) |
|
|
|
|
|
|
|
|
stats = normalizer.get_statistics(enriched_courses) |
|
|
print(f'Статистика: {stats}') |
|
|
|
|
|
print('Обновление данных завершено успешно!') |
|
|
else: |
|
|
print('Не удалось извлечь курсы из PDF, используем тестовые данные') |
|
|
knowledge_base = KnowledgeBase() |
|
|
retriever = Retriever() |
|
|
retriever.build_or_load_index(knowledge_base.courses) |
|
|
|
|
|
except Exception as e: |
|
|
print(f'Ошибка обновления данных: {e}') |
|
|
print('Используем тестовые данные...') |
|
|
knowledge_base = KnowledgeBase() |
|
|
retriever = Retriever() |
|
|
retriever.build_or_load_index(knowledge_base.courses) |
|
|
|
|
|
def save_courses(courses: List[Dict], output_path: str = 'data/processed/courses.json'): |
|
|
os.makedirs(os.path.dirname(output_path), exist_ok=True) |
|
|
|
|
|
with open(output_path, 'w', encoding='utf-8') as f: |
|
|
json.dump(courses, f, ensure_ascii=False, indent=2) |
|
|
|
|
|
print(f'Курсы сохранены в {output_path}') |
|
|
|
|
|
def check_data_exists() -> bool: |
|
|
programs_path = 'data/processed/programs.json' |
|
|
courses_path = 'data/processed/courses.json' |
|
|
index_path = 'data/index/index.faiss' |
|
|
|
|
|
return all(os.path.exists(path) for path in [programs_path, courses_path, index_path]) |
|
|
|
|
|
def load_existing_data() -> tuple[Dict, List[Dict]]: |
|
|
programs = {} |
|
|
courses = [] |
|
|
|
|
|
try: |
|
|
with open('data/processed/programs.json', 'r', encoding='utf-8') as f: |
|
|
programs = json.load(f) |
|
|
except FileNotFoundError: |
|
|
print('Файл programs.json не найден') |
|
|
|
|
|
try: |
|
|
with open('data/processed/courses.json', 'r', encoding='utf-8') as f: |
|
|
courses = json.load(f) |
|
|
except FileNotFoundError: |
|
|
print('Файл courses.json не найден') |
|
|
|
|
|
return programs, courses |
|
|
|
|
|
def check_for_updates() -> bool: |
|
|
"""Проверяет наличие обновлений на сайтах ITMO""" |
|
|
try: |
|
|
scraper = HTMLScraper() |
|
|
programs, _ = load_existing_data() |
|
|
|
|
|
if not programs: |
|
|
return True |
|
|
|
|
|
updates = scraper.check_updates(programs) |
|
|
return len(updates) > 0 |
|
|
|
|
|
except Exception as e: |
|
|
print(f'Ошибка проверки обновлений: {e}') |
|
|
return False |
|
|
|
|
|
def initialize_data(): |
|
|
if check_data_exists(): |
|
|
print('Данные уже существуют, проверяем обновления...') |
|
|
|
|
|
if check_for_updates(): |
|
|
print('Обнаружены обновления, запускаем обновление данных...') |
|
|
update_data_async() |
|
|
else: |
|
|
print('Обновлений не найдено, загружаем существующие данные...') |
|
|
programs, courses = load_existing_data() |
|
|
|
|
|
if courses: |
|
|
retriever = Retriever() |
|
|
retriever.build_or_load_index(courses) |
|
|
print(f'Загружено {len(courses)} курсов') |
|
|
else: |
|
|
print('Курсы не найдены, запускаем обновление...') |
|
|
update_data_async() |
|
|
else: |
|
|
print('Данные не найдены, запускаем первичное обновление...') |
|
|
update_data_async() |
|
|
|
|
|
def main(): |
|
|
if len(sys.argv) > 1: |
|
|
if sys.argv[1] == '--force': |
|
|
print('Принудительное обновление данных...') |
|
|
update_data_async() |
|
|
elif sys.argv[1] == '--check': |
|
|
print('Проверка обновлений...') |
|
|
if check_for_updates(): |
|
|
print('Обнаружены обновления') |
|
|
else: |
|
|
print('Обновлений не найдено') |
|
|
else: |
|
|
print('Использование: python update_data.py [--force|--check]') |
|
|
else: |
|
|
initialize_data() |
|
|
|
|
|
if __name__ == '__main__': |
|
|
main() |
|
|
|