File size: 7,293 Bytes
5071500
 
 
 
53fe915
 
 
5071500
 
 
 
 
53fe915
5071500
53fe915
 
 
 
5071500
53fe915
 
 
 
 
 
5071500
53fe915
 
 
5071500
53fe915
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5071500
53fe915
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5071500
 
 
53fe915
 
 
 
5071500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53fe915
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5071500
 
53fe915
5071500
53fe915
 
5071500
53fe915
 
 
 
 
 
 
 
 
 
 
5071500
 
 
 
 
53fe915
 
 
 
 
 
 
 
 
 
 
 
5071500
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
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...')
        
        # 1. Скрапинг страниц программ
        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
        
        # 2. Скачивание и парсинг PDF
        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}')
        
        # 3. Нормализация данных
        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)
            
            # 4. Создание индекса
            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()